import { BookingCreateDataOptions, getUnitTypeBookingPlan } from '@/manager/modules/booking/booking-factories';
import { BookingCreateDataSetup } from '@/manager/modules/booking/booking-fixtures';
import {
  BookingAdjustmentData,
  BookingAdjustmentEntityFactory,
  BookingCreateData,
} from '@/manager/modules/booking/booking-types';
import { ProductOneTime, ProductRecurring } from '@/shared/types/product-types';
import { getFutureDate } from '@/shared/utils/date-utils';

export interface BookingAdjustmentTestCase {
  description: string;
  setup?: BookingCreateDataSetup;
  options?: BookingCreateDataOptions;
  getAdjustment: (
    data: BookingCreateData,
    create: BookingAdjustmentEntityFactory
  ) => BookingAdjustmentData | Promise<BookingAdjustmentData>;
  expectProrationInvoice?: boolean;
}

function seededRecurringProduct(data: BookingCreateData, index: number): ProductRecurring {
  return data.recurringProducts![index].product;
}

function seededOneTimeProduct(data: BookingCreateData, index: number): ProductOneTime {
  return data.oneTimeProducts![index].product;
}

export const bookingAdjustmentHappyTestCases: BookingAdjustmentTestCase[] = [
  {
    description: 'switches the unit immediately',
    getAdjustment: async (_, create) => ({ unit: await create.unit(), applyType: 'immediately' }),
  },
  {
    description: 'switches the booking plan immediately',
    expectProrationInvoice: true,
    getAdjustment: (data) => ({
      plan: getUnitTypeBookingPlan(data.unitType.unitType, data.unitType.plan.period, 'Advanced'),
      applyType: 'immediately',
      prorationBehavior: 'immediately',
    }),
  },
  {
    description: 'adds a protection plan immediately',
    expectProrationInvoice: true,
    getAdjustment: async (_, create) => ({ insurance: await create.insurance(), applyType: 'immediately' }),
  },
  {
    description: 'adds a deposit immediately',
    getAdjustment: async (_, create) => ({ deposit: await create.deposit(), applyType: 'immediately' }),
  },
  {
    description: 'applies a discount immediately',
    getAdjustment: async (_, create) => ({ discount: await create.discount(), applyType: 'immediately' }),
  },
  {
    description: 'changes the recurring products immediately',
    setup: { recurringProducts: [{ quantity: 1 }, { quantity: 1 }] },
    expectProrationInvoice: true,
    getAdjustment: async (data, create) => ({
      recurringProducts: {
        changeQuantity: [{ name: seededRecurringProduct(data, 0).name, quantity: 3 }],
        add: [{ product: await create.recurringProduct(), quantity: 2 }],
        remove: [seededRecurringProduct(data, 1).name],
      },
      applyType: 'immediately',
    }),
  },
  {
    description: 'combines multiple changes immediately',
    setup: { insurance: true, recurringProducts: [{ quantity: 1 }, { quantity: 1 }] },
    getAdjustment: async (data, create) => ({
      insurance: await create.insurance(),
      discount: await create.discount(),
      recurringProducts: {
        changeQuantity: [{ name: seededRecurringProduct(data, 0).name, quantity: 2 }],
      },
      applyType: 'immediately',
    }),
  },
  {
    description: 'combines changes on a monthly booking',
    options: { billingType: 'monthly' },
    setup: { recurringProducts: [{ quantity: 1 }] },
    getAdjustment: async (data, create) => ({
      insurance: await create.insurance(),
      recurringProducts: {
        changeQuantity: [{ name: seededRecurringProduct(data, 0).name, quantity: 2 }],
        add: [{ product: await create.recurringProduct(), quantity: 1 }],
      },
      applyType: 'immediately',
    }),
  },
  {
    description: 'switches the unit type immediately',
    getAdjustment: async (data, create) => {
      const { unitType, unit } = await create.unitTypeWithUnit();
      return {
        unitType,
        unit,
        plan: getUnitTypeBookingPlan(unitType, data.unitType.plan.period, 'Basic'),
        insurance: await create.insurance(),
        applyType: 'immediately',
      };
    },
  },
  {
    description: 'schedules a booking plan switch for the period end',
    getAdjustment: (data) => ({
      plan: getUnitTypeBookingPlan(data.unitType.unitType, data.unitType.plan.period, 'Advanced'),
      applyType: 'end_current_period',
    }),
  },
  {
    description: 'schedules combined changes for a custom date',
    setup: { insurance: true, recurringProducts: [{ quantity: 1 }] },
    getAdjustment: async (data, create) => ({
      plan: getUnitTypeBookingPlan(data.unitType.unitType, data.unitType.plan.period, 'Advanced'),
      insurance: await create.insurance(),
      recurringProducts: {
        add: [{ product: seededRecurringProduct(data, 0), quantity: 2 }],
      },
      applyType: 'custom',
      applyDate: getFutureDate(10, 'day'),
    }),
  },
  {
    description: 'switches the unit on a backdated booking',
    options: { moveInDaysInTheFuture: -3 },
    getAdjustment: async (_, create) => ({ unit: await create.unit(), applyType: 'immediately' }),
  },
  {
    description: 'switches the booking plan on a backdated booking',
    options: { moveInDaysInTheFuture: -3 },
    expectProrationInvoice: true,
    getAdjustment: (data) => ({
      plan: getUnitTypeBookingPlan(data.unitType.unitType, data.unitType.plan.period, 'Advanced'),
      applyType: 'immediately',
      prorationBehavior: 'immediately',
    }),
  },
  {
    description: 'schedules a booking plan switch on a backdated booking',
    options: { moveInDaysInTheFuture: -3 },
    getAdjustment: (data) => ({
      plan: getUnitTypeBookingPlan(data.unitType.unitType, data.unitType.plan.period, 'Advanced'),
      applyType: 'end_current_period',
    }),
  },
  {
    description: 'switches the unit on a scheduled booking',
    options: { moveInDaysInTheFuture: 1 },
    getAdjustment: async (_, create) => ({ unit: await create.unit(), applyType: 'immediately' }),
  },
  {
    description: 'switches the booking plan on a scheduled booking',
    options: { moveInDaysInTheFuture: 1 },
    getAdjustment: (data) => ({
      plan: getUnitTypeBookingPlan(data.unitType.unitType, data.unitType.plan.period, 'Advanced'),
      applyType: 'immediately',
    }),
  },
  {
    description: 'changes the one-time products on a scheduled booking',
    options: { moveInDaysInTheFuture: 1 },
    setup: { oneTimeProducts: [{ quantity: 1 }, { quantity: 1 }] },
    getAdjustment: async (data, create) => ({
      oneTimeProducts: {
        changeQuantity: [{ name: seededOneTimeProduct(data, 0).name, quantity: 3 }],
        add: [{ product: await create.oneTimeProduct(), quantity: 1 }],
        remove: [seededOneTimeProduct(data, 1).name],
      },
      applyType: 'immediately',
    }),
  },
  {
    description: 'combines changes on a scheduled booking',
    options: { moveInDaysInTheFuture: 1 },
    setup: { recurringProducts: [{ quantity: 1 }], oneTimeProducts: [{ quantity: 1 }, { quantity: 1 }] },
    getAdjustment: (data) => ({
      plan: getUnitTypeBookingPlan(data.unitType.unitType, data.unitType.plan.period, 'Advanced'),
      recurringProducts: {
        add: [{ product: seededRecurringProduct(data, 0), quantity: 2 }],
      },
      oneTimeProducts: {
        changeQuantity: [{ name: seededOneTimeProduct(data, 0).name, quantity: 2 }],
      },
      applyType: 'immediately',
    }),
  },
];
