import {
  BookingApiClient,
  cancelBookingNowViaApi,
  createBookingViaApi,
  resolveBookingApiClient,
} from '@/manager/modules/booking/booking-api';
import { BookingCreateData } from '@/manager/modules/booking/booking-types';
import { createCustomerViaApi, deleteCustomerViaApi } from '@/manager/modules/customer/customer-api';
import { CustomerCreateData } from '@/manager/modules/customer/customer-types';
import { createUnitViaApi, deleteUnitViaApi } from '@/manager/modules/unit/unit-api';
import { UnitCreateData } from '@/manager/modules/unit/unit-types';
import { UnitCreateDialog } from '@/manager/modules/unit/views/UnitCreateDialog';
import { UnitDetailsPage } from '@/manager/modules/unit/views/UnitDetailsPage';
import { UnitListPage } from '@/manager/modules/unit/views/UnitListPage';
import { createUnitTypeViaApi, deleteUnitTypeViaApi } from '@/manager/modules/unit-type/unit-type-api';
import { UnitTypeCreateData } from '@/manager/modules/unit-type/unit-type-types';
import { test as apiTest } from '@/manager/shared/api-fixtures';
import { Booking } from '@/shared/types/booking-types';
import { Customer } from '@/shared/types/customer-types';
import { Unit } from '@/shared/types/unit-types';
import { UnitType } from '@/shared/types/unit-type-types';

interface UnitFixtures {
  unitListPage: UnitListPage;
  unitDetailsPage: UnitDetailsPage;
  unitCreateDialog: UnitCreateDialog;
  createUnit: (data: UnitCreateData) => Promise<Unit>;
  createUnitType: (data: UnitTypeCreateData) => Promise<UnitType>;
  createCustomer: (data: CustomerCreateData) => Promise<Customer>;
  createBooking: (data: BookingCreateData) => Promise<Booking>;
  trackUnitForTeardown: (id: number) => void;
}

interface UnitInternalFixtures {
  bookingApiClient: BookingApiClient;
  unitTeardown: { unitIds: number[]; unitTypeIds: number[]; customerIds: number[]; bookingIds: number[] };
}

export const test = apiTest.extend<UnitFixtures & UnitInternalFixtures>({
  unitListPage: async ({ page }, use) => {
    await use(new UnitListPage(page));
  },

  unitDetailsPage: async ({ page }, use) => {
    await use(new UnitDetailsPage(page));
  },

  unitCreateDialog: async ({ page }, use) => {
    await use(new UnitCreateDialog(page));
  },

  // Routes booking create/cancel to the manager or billing API depending on the run's billing mode; every
  // other entity below always uses the manager API.
  bookingApiClient: async ({ managerApiRequest, billingApiRequest }, use) => {
    await use(resolveBookingApiClient(managerApiRequest, billingApiRequest));
  },

  createUnit: async ({ managerApiRequest, unitTeardown }, use) => {
    await use(async (data) => {
      const unit = await apiTest.step('setup: create unit via API', () => createUnitViaApi(managerApiRequest, data));
      unitTeardown.unitIds.push(unit.id);
      return unit;
    });
  },

  createUnitType: async ({ managerApiRequest, unitTeardown }, use) => {
    await use(async (data) => {
      const unitType = await apiTest.step('setup: create unit type via API', () =>
        createUnitTypeViaApi(managerApiRequest, data)
      );
      unitTeardown.unitTypeIds.push(unitType.id);
      return unitType;
    });
  },

  createCustomer: async ({ managerApiRequest, unitTeardown }, use) => {
    await use(async (data) => {
      const customer = await apiTest.step('setup: create customer via API', () =>
        createCustomerViaApi(managerApiRequest, data)
      );
      unitTeardown.customerIds.push(customer.id);
      return customer;
    });
  },

  createBooking: async ({ bookingApiClient, unitTeardown }, use) => {
    await use(async (data) => {
      const booking = await apiTest.step('setup: create booking via API', () =>
        createBookingViaApi(bookingApiClient, data)
      );
      unitTeardown.bookingIds.push(booking.id);
      return booking;
    });
  },

  trackUnitForTeardown: async ({ unitTeardown }, use) => {
    await use((id) => unitTeardown.unitIds.push(id));
  },

  unitTeardown: async ({ managerApiRequest, bookingApiClient }, use) => {
    const unitIds: number[] = [];
    const unitTypeIds: number[] = [];
    const customerIds: number[] = [];
    const bookingIds: number[] = [];

    await use({ unitIds, unitTypeIds, customerIds, bookingIds });

    // TODO(KIN-4692): Teardown is disabled for now. Deleting these entities kicks off a flood of async
    // backend jobs, and when the job queue backs up, the jobs other e2e tests rely on (e.g. invoice
    // generation) don't run in time and those tests fail. Until the queue pressure is sorted out, we
    // leave all created entities for the env reseed to clean up rather than tearing down here.
    return;

    await apiTest.step('teardown: cancel created bookings via API', () =>
      Promise.all(bookingIds.map((id) => cancelBookingNowViaApi(bookingApiClient, id)))
    );
    await apiTest.step('teardown: delete created units via API', () =>
      Promise.all(unitIds.map((id) => deleteUnitViaApi(managerApiRequest, id)))
    );
    await apiTest.step('teardown: delete created unit types via API', () =>
      Promise.all(unitTypeIds.map((id) => deleteUnitTypeViaApi(managerApiRequest, id)))
    );
    await apiTest.step('teardown: delete created customers via API', () =>
      Promise.all(customerIds.map((id) => deleteCustomerViaApi(managerApiRequest, id)))
    );
  },
});

export { expect } from '@playwright/test';
