import { bookingTableColumnTestIds } from '@/manager/modules/ui/data-table/data/data-table-column-test-ids';
import {
  expectDataTableChipsColumnToHaveText,
  expectDataTableTextColumnToHaveText,
} from '@/manager/modules//ui/data-table/utils/data-table-expect-utils';
import { expect } from '@playwright/test';
import {
  IBookingCancelData,
  IBookingCancelRescheduleData,
  IBookingCreateData,
  IBookingEditNotesData,
} from '@/manager/modules/booking/types/booking-types';
import { expectSingleLocatorToHaveText } from '@/manager/shared/utils/expect-utils';
import { BookingListPage } from '@/manager/modules/booking/views/BookingListPage';
import { toBookingCancellationDetails } from '@/manager/modules/booking/utils/booking-edit-transform-utils';
import { BookingDetailsPage } from '@/manager/modules/booking/views/BookingDetailsPage';

export async function expectBookingNotesToBeEdited(
  startPage: BookingListPage,
  oldBooking: IBookingCreateData,
  changes: IBookingEditNotesData
): Promise<void> {
  const createDialog = await startPage.openBookingCreateDialog();
  const oldDetailsPage = await createDialog.create(oldBooking);

  await expectSingleLocatorToHaveText(oldDetailsPage.notesCard.note, oldBooking.note);

  const editNotesDialog = await oldDetailsPage.notesCard.openBookingEditNotesDialog();
  const newDetailsPage = await editNotesDialog.edit(changes);

  const newBooking: IBookingCreateData = {
    ...oldBooking,
    note: changes.note === null ? undefined : (changes.note ?? oldBooking.note),
  };

  await expectSingleLocatorToHaveText(newDetailsPage.notesCard.note, newBooking.note);
}

export async function expectBookingToBeCanceled(
  startPage: BookingListPage,
  booking: IBookingCreateData,
  cancellation: IBookingCancelData
): Promise<void> {
  const createDialog = await startPage.openBookingCreateDialog();
  const oldDetailsPage = await createDialog.create(booking);

  await expect(oldDetailsPage.title).not.toBeEmpty();

  const bookingId = (await oldDetailsPage.title.innerText()).split(' ')[1];
  const { oldStatus } = toBookingCancellationDetails(booking, cancellation);

  await expectSingleLocatorToHaveText(oldDetailsPage.baseStatus, oldStatus);
  await expectSingleLocatorToHaveText(oldDetailsPage.generalCard.moveOut, undefined);

  const oldListPage = await oldDetailsPage.returnToBookingListPage();
  await oldListPage.filter({
    search: bookingId,
  });

  await expect(oldListPage.dataTable.getRows()).toHaveCount(1);
  await expectDataTableChipsColumnToHaveText(oldListPage.dataTable, bookingTableColumnTestIds.status, oldStatus);
  await expectDataTableTextColumnToHaveText(oldListPage.dataTable, bookingTableColumnTestIds.moveOut, undefined);

  const newDetailsPage = await oldListPage.goToBookingDetailsPage(0);

  const cancelDialog = await newDetailsPage.openBookingCancelDialog();
  await cancelDialog.cancel(cancellation);

  await expectBookingDetailsPageAndThenListPageToDisplayCorrectCancellationData(newDetailsPage, booking, cancellation);
}

export async function expectBookingCancellationToBeRescheduled(
  startPage: BookingListPage,
  booking: IBookingCreateData,
  cancellation: IBookingCancelRescheduleData
): Promise<void> {
  const createDialog = await startPage.openBookingCreateDialog();
  const oldDetailsPage = await createDialog.create(booking);

  const cancelView = await oldDetailsPage.openBookingCancelDialog();
  await cancelView.cancel(cancellation.from);

  const listPage = await expectBookingDetailsPageAndThenListPageToDisplayCorrectCancellationData(
    oldDetailsPage,
    booking,
    cancellation.from
  );
  const newDetailsPage = await listPage.goToBookingDetailsPage(0);

  const cancelRescheduleDialog = await oldDetailsPage.openBookingCancelRescheduleDialog();
  await cancelRescheduleDialog.cancel(cancellation.to);

  await expectBookingDetailsPageAndThenListPageToDisplayCorrectCancellationData(
    newDetailsPage,
    booking,
    cancellation.to
  );
}

export async function expectBookingCancellationToBeRevoked(
  startPage: BookingListPage,
  booking: IBookingCreateData,
  cancellation: IBookingCancelData
): Promise<void> {
  const createDialog = await startPage.openBookingCreateDialog();
  const oldDetailsPage = await createDialog.create(booking);

  const bookingId = (await oldDetailsPage.title.innerText()).split(' ')[1];
  const { oldStatus } = toBookingCancellationDetails(booking, cancellation);

  const cancelView = await oldDetailsPage.openBookingCancelDialog();
  await cancelView.cancel(cancellation);

  const oldListPage = await expectBookingDetailsPageAndThenListPageToDisplayCorrectCancellationData(
    oldDetailsPage,
    booking,
    cancellation
  );
  const newDetailsPage = await oldListPage.goToBookingDetailsPage(0);

  const cancelRevokeDialog = await oldDetailsPage.openBookingCancelRevokeDialog();
  await cancelRevokeDialog.revoke();

  await expectSingleLocatorToHaveText(newDetailsPage.baseStatus, oldStatus);
  await expectSingleLocatorToHaveText(newDetailsPage.generalCard.moveOut, undefined);

  const newListPage = await newDetailsPage.returnToBookingListPage();
  await newListPage.filter({
    search: bookingId,
  });

  await expect(newListPage.dataTable.getRows()).toHaveCount(1);
  await expectDataTableChipsColumnToHaveText(newListPage.dataTable, bookingTableColumnTestIds.status, oldStatus);
  await expectDataTableTextColumnToHaveText(newListPage.dataTable, bookingTableColumnTestIds.moveOut, undefined);
}

export async function expectBookingDetailsPageAndThenListPageToDisplayCorrectCancellationData(
  startPage: BookingDetailsPage,
  booking: IBookingCreateData,
  cancellation: IBookingCancelData
): Promise<BookingListPage> {
  await expectBookingDetailsPageToDisplayCorrectCancellationData(startPage, booking, cancellation);
  const bookingId = (await startPage.title.innerText()).trim().split(' ')[1];
  const listPage = await startPage.returnToBookingListPage();
  await expectBookingListPageToDisplayCorrectCancellationData(listPage, booking, bookingId, cancellation);
  return listPage;
}

export async function expectBookingDetailsPageToDisplayCorrectCancellationData(
  page: BookingDetailsPage,
  booking: IBookingCreateData,
  cancellation: IBookingCancelData
) {
  // define helpers
  const details = toBookingCancellationDetails(booking, cancellation);

  // check details
  await expectSingleLocatorToHaveText(page.baseStatus, details.newStatus);

  if (details.oldStatus === 'scheduled') {
    await expect(page.canceledBeforeStartStatus).toBeVisible();
  } else if (details.oldStatus === 'active') {
    await expectSingleLocatorToHaveText(page.cancelsOnStatus, `ends on ${details.moveOutDate}`);
  }

  await expectSingleLocatorToHaveText(page.generalCard.moveOut, details.moveOutDate);
}

export async function expectBookingListPageToDisplayCorrectCancellationData(
  page: BookingListPage,
  booking: IBookingCreateData,
  bookingId: string,
  cancellation: IBookingCancelData
) {
  // define helpers
  const { newStatus, moveOutDate } = toBookingCancellationDetails(booking, cancellation);

  // filter list
  await page.filter({
    search: bookingId,
  });

  // check list
  await expect(page.dataTable.getRows()).toHaveCount(1);
  await expectDataTableChipsColumnToHaveText(
    page.dataTable,
    bookingTableColumnTestIds.status,
    newStatus === 'cancelled' ? 'cancelled' : [newStatus, '']
  );
  await expectDataTableTextColumnToHaveText(page.dataTable, bookingTableColumnTestIds.moveOut, moveOutDate);
}
