import { getUnitTypeBookingPlan } from '@/manager/modules/booking/booking-factories';
import { test } from '@/manager/modules/booking/booking-fixtures';
import { toBookingItemsDetails } from '@/manager/modules/booking/booking-mappers';
import { BookingAdjustmentData, BookingCreateData } from '@/manager/modules/booking/booking-types';
import { expectSingleLocatorToHaveText } from '@/manager/shared/utils/expect-utils';
import { getFutureDate } from '@/shared/utils/date-utils';
import { formatBillingPeriod, formatCurrency, formatDate } from '@/shared/utils/formatters';
import { expect } from '@/shared/utils/matchers';

function getPlanSwitchAdjustment(data: BookingCreateData, planName: string): BookingAdjustmentData {
  return { plan: getUnitTypeBookingPlan(data.unitType.unitType, planName), applyType: 'end_current_period' };
}

test('edits the adjustment from the notification banner', async ({
  setupBookingCreateData,
  createBooking,
  createBookingAdjustment,
  bookingDetailsPage,
}) => {
  const data = await setupBookingCreateData();
  const booking = await createBooking(data);
  const adjustment = getPlanSwitchAdjustment(data, 'Advanced');
  const editedAdjustment = getPlanSwitchAdjustment(data, 'Premium');
  const { timezone } = data.location;
  const periodEnd = getFutureDate(
    data.unitType.plan.amount,
    data.unitType.plan.period === 'monthly' ? 'month' : 'week'
  );

  await createBookingAdjustment(booking, adjustment);

  await bookingDetailsPage.goto(booking.id);

  const dialog = await test.step('open the adjustment dialog', () =>
    bookingDetailsPage.openBookingAdjustmentDialogFromNotification());

  await test.step('verify the adjustment in the dialog', async () => {
    await expect(dialog.itemsFieldSet.bookingPlanHost).toContainText(adjustment.plan!.name!);
  });

  await test.step('adjust the booking plan', () => dialog.adjust(editedAdjustment));

  await test.step('verify the adjustment notification', async () => {
    await expect(bookingDetailsPage.adjustmentNotificationCard.main).toBeVisible();
    await expect(bookingDetailsPage.adjustmentNotificationCard.main).toContainText(
      `The changes will be applied on ${formatDate(periodEnd, timezone)}`
    );
  });

  await test.step('verify the adjustment in the dialog', async () => {
    const reopenedDialog = await bookingDetailsPage.openBookingAdjustmentDialogFromNotification();
    await expect(reopenedDialog.itemsFieldSet.bookingPlanHost).toContainText(editedAdjustment.plan!.name!);
  });
});

test('edits the adjustment from the actions menu', async ({
  setupBookingCreateData,
  createBooking,
  createBookingAdjustment,
  bookingDetailsPage,
}) => {
  const data = await setupBookingCreateData();
  const booking = await createBooking(data);
  const adjustment = getPlanSwitchAdjustment(data, 'Advanced');
  const editedAdjustment = getPlanSwitchAdjustment(data, 'Premium');

  await createBookingAdjustment(booking, adjustment);

  await bookingDetailsPage.goto(booking.id);

  const dialog = await test.step('open the adjustment dialog', () => bookingDetailsPage.openBookingAdjustmentDialog());

  await test.step('verify the adjustment in the dialog', async () => {
    await expect(dialog.itemsFieldSet.bookingPlanHost).toContainText(adjustment.plan!.name!);
  });

  await test.step('adjust the booking plan', () => dialog.adjust(editedAdjustment));

  await test.step('verify the adjustment in the dialog', async () => {
    const reopenedDialog = await bookingDetailsPage.openBookingAdjustmentDialog();
    await expect(reopenedDialog.itemsFieldSet.bookingPlanHost).toContainText(editedAdjustment.plan!.name!);
  });
});

test('removes the adjustment when the changes are reverted', async ({
  setupBookingCreateData,
  createBooking,
  createBookingAdjustment,
  bookingDetailsPage,
}) => {
  const data = await setupBookingCreateData();
  const booking = await createBooking(data);
  const originalPlan = data.unitType.plan;
  const { currency } = data.location;
  const originalPlanLabel = formatBillingPeriod(originalPlan.period, originalPlan.amount, originalPlan.name);
  const amountExclVat = formatCurrency(toBookingItemsDetails(data).totals.exclVat, currency);

  await createBookingAdjustment(booking, getPlanSwitchAdjustment(data, 'Advanced'));

  await bookingDetailsPage.goto(booking.id);

  const dialog = await test.step('open the adjustment dialog', () =>
    bookingDetailsPage.openBookingAdjustmentDialogFromNotification());

  await test.step('revert the changes', () => dialog.revert({ plan: originalPlan, applyType: 'end_current_period' }));

  await test.step('verify the adjustment is removed', async () => {
    await expect(bookingDetailsPage.adjustmentNotificationCard.main).toBeHidden();
    await expectSingleLocatorToHaveText(bookingDetailsPage.billingCard.bookingPlan, originalPlanLabel);
    await expectSingleLocatorToHaveText(bookingDetailsPage.billingCard.amountExclVat, amountExclVat);
  });
});
