// Type definitions
export interface Price {
  monthly?: number;
  upfront?: number;
}

export interface Discount {
  monthly?: number;
  upfront?: number;
  monthsCount?: number;
}

export interface Item {
  type?: string;
  name?: string;
  icon?: string;
  image?: string;
  price?: Price;
  isSubitem?: boolean;
  isOptional?: boolean;
  isMandatory?: boolean;
  discount?: Discount;
  monthsFree?: number;
  tooltip?: string;
  badge?: string;
}

export interface DiscountPeriod {
  start: number;
  end: number;
  price: Price;
  discount: Discount;
}

export interface FormattedPeriod {
  name: string;
  price: Price;
  discount: Discount;
}

/**
 *
 * Calculate total payments for each month
 * upfront price is shown just in the first row if its set
 * monthly prices are shown with striked out discounts above the price
 *
 * @param items - cart items raw data
 * @param commitment - commitment in months
 *
 */
export default function generateTotal(
  items: Item[],
  hideFirst?: boolean,
  commitment: number = 24,
): FormattedPeriod[] {
  let discountPeriods: DiscountPeriod[] = [];

  // calculate full price without discounts
  const monthlyPrice = calculateMonthlyPrice(items);
  const upfrontPrice = calculateUpfrontPrice(items);

  for (let month = 1; month < commitment; month++) {
    const price: Price = {
      // show upfront price only in first month
      upfront: upfrontPrice > 0 && month === 1 ? upfrontPrice : undefined,
      monthly: monthlyPrice,
    };

    // calculate discount for current month
    const discount: Discount = {
      monthly: calculateMonthlyDiscount(items, month, commitment),
    };

    // if discount is not the same as last item add new one
    const lastIndex = discountPeriods.length - 1;
    if (
      discountPeriods.length === 0 ||
      discountPeriods[lastIndex].price?.upfront !== price?.upfront ||
      discountPeriods[lastIndex].discount?.monthly !== discount?.monthly
    ) {
      // set correct ending of last period
      if (discountPeriods[lastIndex]) {
        discountPeriods[lastIndex].end = month - 1;
      }

      discountPeriods = [
        ...discountPeriods,
        {
          start: month,
          end: commitment,
          price,
          discount,
        },
      ];
    }
  }
  if (hideFirst) {
    discountPeriods.shift();
    if (discountPeriods[0]) discountPeriods[0].start = 1;
  }
  // return correct format for table
  return format(discountPeriods);
}

function calculateMonthlyPrice(items: Item[]): number {
  return items.reduce((price, item) => price + (item?.price?.monthly ?? 0), 0);
}

function calculateUpfrontPrice(items: Item[]): number {
  return items.reduce((price, item) => {
    price += item?.price?.upfront ?? 0;
    price -= item?.discount?.upfront ?? 0;
    return price;
  }, 0);
}

function calculateMonthlyDiscount(
  items: Item[],
  currentMonth: number,
  commitment: number,
): number {
  return items.reduce((discount, item) => {
    const monthsFree = item?.monthsFree ?? 0;
    const discountMonthsCount = item?.discount?.monthsCount ?? 0;
    // special discounts starts after free months
    const allDiscountsEnd = discountMonthsCount + monthsFree || commitment;

    // if its free this month, add full price to discount
    if (monthsFree && currentMonth <= monthsFree) {
      discount += item?.price?.monthly ?? 0;
    }
    // add temporary or default monthly discount if aplicable
    else if (allDiscountsEnd > 0 && currentMonth <= allDiscountsEnd) {
      discount += item?.discount?.monthly ?? 0;
    }
    return discount;
  }, 0);
}

function format(items: DiscountPeriod[]): FormattedPeriod[] {
  return items.map(({ start, end, price, discount }) => {
    let name = "";
    if (items.length > 1) {
      if (start !== end) {
        name = `${start}. – ${end}. mesiac`;
      } else {
        name = `${start}. mesiac`;
      }
    }

    return {
      name,
      price,
      discount,
    };
  });
}
