export type MonthPeriod = {
  type: "month";
  startYear: number;
  startMonth: number;
  count: number;
};

export type QuarterPeriod = {
  type: "quarter";
  startYear: number;
  startQuarter: number;
  count: number;
};

export type YearPeriod = {
  type: "year";
  startYear: number;
  count: number;
};

export type CustomPeriod = MonthPeriod | QuarterPeriod | YearPeriod;

export type NaturalLanguagePeriodResult = CustomPeriod | "last30days" | null;

const MONTH_NAMES = [
  "January",
  "February",
  "March",
  "April",
  "May",
  "June",
  "July",
  "August",
  "September",
  "October",
  "November",
  "December",
] as const;

const QUARTER_NAMES = ["Q1", "Q2", "Q3", "Q4"] as const;

const MONTH_MAP: Record<string, number> = {
  jan: 0,
  january: 0,
  feb: 1,
  february: 1,
  mar: 2,
  march: 2,
  apr: 3,
  april: 3,
  may: 4,
  jun: 5,
  june: 5,
  jul: 6,
  july: 6,
  aug: 7,
  august: 7,
  sep: 8,
  sept: 8,
  september: 8,
  oct: 9,
  october: 9,
  nov: 10,
  november: 10,
  dec: 11,
  december: 11,
};

const stripDelimiters = (value: string) => value.toLowerCase().trim().replace(/[,]/g, "");

const parseYear = (yearStr: string): number => {
  const year = parseInt(yearStr, 10);
  if (yearStr.length === 2) {
    if (year <= 30) {
      return 2000 + year;
    }
    return 1900 + year;
  }
  return year;
};

const findMonth = (monthStr: string): number | null => {
  const month = MONTH_MAP[monthStr.toLowerCase()];
  return month ?? null;
};

export const parseNaturalLanguagePeriod = (
  input: string,
  referenceDate: Date = new Date()
): NaturalLanguagePeriodResult => {
  const lower = stripDelimiters(input);
  const now = referenceDate;
  const currentYear = now.getFullYear();
  const currentMonth = now.getMonth();
  const currentQuarter = Math.floor(currentMonth / 3);

  if (/^(last\s*)?30\s*days?$/i.test(lower)) {
    return "last30days";
  }

  if (/^this\s+month$/i.test(lower)) {
    return {
      type: "month",
      startYear: currentYear,
      startMonth: currentMonth,
      count: 1,
    };
  }

  if (/^last\s+month$/i.test(lower)) {
    const lastMonth = currentMonth === 0 ? 11 : currentMonth - 1;
    const lastMonthYear = currentMonth === 0 ? currentYear - 1 : currentYear;
    return {
      type: "month",
      startYear: lastMonthYear,
      startMonth: lastMonth,
      count: 1,
    };
  }

  if (/^this\s+quarter$/i.test(lower)) {
    return {
      type: "quarter",
      startYear: currentYear,
      startQuarter: currentQuarter,
      count: 1,
    };
  }

  if (/^last\s+quarter$/i.test(lower)) {
    const lastQuarter = currentQuarter === 0 ? 3 : currentQuarter - 1;
    const lastQuarterYear = currentQuarter === 0 ? currentYear - 1 : currentYear;
    return {
      type: "quarter",
      startYear: lastQuarterYear,
      startQuarter: lastQuarter,
      count: 1,
    };
  }

  if (/^this\s+year$/i.test(lower)) {
    return {
      type: "year",
      startYear: currentYear,
      count: 1,
    };
  }

  if (/^last\s+year$/i.test(lower)) {
    return {
      type: "year",
      startYear: currentYear - 1,
      count: 1,
    };
  }

  const lastMatch = lower.match(/^last\s+(\d+)\s*(months?|quarters?|years?|mos?|qtrs?|yrs?)$/i);
  if (lastMatch) {
    const count = parseInt(lastMatch[1], 10);
    const unit = lastMatch[2].toLowerCase();

    let type: "month" | "quarter" | "year";
    if (/^(months?|mos?)$/i.test(unit)) {
      type = "month";
    } else if (/^(quarters?|qtrs?)$/i.test(unit)) {
      type = "quarter";
    } else {
      type = "year";
    }

    if (type === "month") {
      const startMonth = currentMonth - count + 1;
      const startYear = currentYear + Math.floor(startMonth / 12);
      return {
        type: "month",
        startYear,
        startMonth: ((startMonth % 12) + 12) % 12,
        count,
      };
    }

    if (type === "quarter") {
      const startQuarter = currentQuarter - count + 1;
      const startYear = currentYear + Math.floor(startQuarter / 4);
      return {
        type: "quarter",
        startYear,
        startQuarter: ((startQuarter % 4) + 4) % 4,
        count,
      };
    }

    return {
      type: "year",
      startYear: currentYear - count + 1,
      count,
    };
  }

  const quarterMatch = lower.match(/^q([1-4])(?:\s+|-)(\d{2,4})$/);
  if (quarterMatch) {
    return {
      type: "quarter",
      startYear: parseYear(quarterMatch[2]),
      startQuarter: parseInt(quarterMatch[1], 10) - 1,
      count: 1,
    };
  }

  const quarterRangeMatch = lower.match(/^q([1-4])\s*(?:-|–|—|to|through|until|thru)\s*q([1-4])(?:\s+|-)(\d{2,4})$/);
  if (quarterRangeMatch) {
    const startQ = parseInt(quarterRangeMatch[1], 10) - 1;
    const endQ = parseInt(quarterRangeMatch[2], 10) - 1;
    const year = parseYear(quarterRangeMatch[3]);

    let count = endQ - startQ + 1;
    if (count <= 0) {
      count += 4;
    }

    return {
      type: "quarter",
      startYear: year,
      startQuarter: startQ,
      count,
    };
  }

  const quarterRangeAcrossYearsMatch = lower.match(
    /^q([1-4])(?:\s+|-)(\d{2,4})\s*(?:-|–|—|to|through|until|thru)\s*q([1-4])(?:\s+|-)(\d{2,4})$/
  );
  if (quarterRangeAcrossYearsMatch) {
    const startQ = parseInt(quarterRangeAcrossYearsMatch[1], 10) - 1;
    const startYear = parseYear(quarterRangeAcrossYearsMatch[2]);
    const endQ = parseInt(quarterRangeAcrossYearsMatch[3], 10) - 1;
    const endYear = parseYear(quarterRangeAcrossYearsMatch[4]);

    const totalQuarters = (endYear - startYear) * 4 + (endQ - startQ) + 1;

    if (totalQuarters > 0 && totalQuarters <= 20) {
      return {
        type: "quarter",
        startYear,
        startQuarter: startQ,
        count: totalQuarters,
      };
    }
  }

  const yearMatch = lower.match(/^(\d{2,4})$/);
  if (yearMatch) {
    return {
      type: "year",
      startYear: parseYear(yearMatch[1]),
      count: 1,
    };
  }

  const yearRangeMatch = lower.match(/^(\d{2,4})\s*(?:-|–|—|to|through|until|thru)\s*(\d{2,4})$/);
  if (yearRangeMatch) {
    const startYear = parseYear(yearRangeMatch[1]);
    const endYear = parseYear(yearRangeMatch[2]);
    const count = endYear - startYear + 1;

    if (count > 0 && count <= 10) {
      return {
        type: "year",
        startYear,
        count,
      };
    }
  }

  const singleMonthMatch = lower.match(/^([a-z]+)(?:\s+|-)(\d{2,4})$/);
  if (singleMonthMatch) {
    const monthIndex = findMonth(singleMonthMatch[1]);
    if (monthIndex !== null) {
      return {
        type: "month",
        startYear: parseYear(singleMonthMatch[2]),
        startMonth: monthIndex,
        count: 1,
      };
    }
  }

  const monthRangeWithYearMatch = lower.match(
    /^([a-z]+)\s*(?:-|–|—|to|through|until|thru)\s*([a-z]+)(?:\s+|-)(\d{2,4})$/
  );
  if (monthRangeWithYearMatch) {
    const startMonthIndex = findMonth(monthRangeWithYearMatch[1]);
    const endMonthIndex = findMonth(monthRangeWithYearMatch[2]);
    const year = parseYear(monthRangeWithYearMatch[3]);

    if (startMonthIndex !== null && endMonthIndex !== null) {
      let count = endMonthIndex - startMonthIndex + 1;
      if (count <= 0) {
        count += 12;
      }

      return {
        type: "month",
        startYear: year,
        startMonth: startMonthIndex,
        count,
      };
    }
  }

  const monthRangeAcrossYearsMatch = lower.match(
    /^([a-z]+)(?:\s+|-)(\d{2,4})\s*(?:-|–|—|to|through|until|thru)\s*([a-z]+)(?:\s+|-)(\d{2,4})$/
  );
  if (monthRangeAcrossYearsMatch) {
    const startMonthIndex = findMonth(monthRangeAcrossYearsMatch[1]);
    const startYear = parseYear(monthRangeAcrossYearsMatch[2]);
    const endMonthIndex = findMonth(monthRangeAcrossYearsMatch[3]);
    const endYear = parseYear(monthRangeAcrossYearsMatch[4]);

    if (startMonthIndex !== null && endMonthIndex !== null) {
      const totalMonths = (endYear - startYear) * 12 + (endMonthIndex - startMonthIndex) + 1;

      if (totalMonths > 0 && totalMonths <= 60) {
        return {
          type: "month",
          startYear,
          startMonth: startMonthIndex,
          count: totalMonths,
        };
      }
    }
  }

  return null;
};

export const generateNaturalLanguagePreview = (
  parsed: NaturalLanguagePeriodResult
): string | null => {
  if (!parsed) {
    return null;
  }

  if (parsed === "last30days") {
    return "Last 30 days";
  }

  if (parsed.type === "year") {
    if (parsed.count === 1) {
      return `${parsed.startYear}`;
    }
    const endYear = parsed.startYear + parsed.count - 1;
    return `${parsed.startYear} to ${endYear}`;
  }

  if (parsed.type === "quarter") {
    if (parsed.count === 1) {
      return `${QUARTER_NAMES[parsed.startQuarter]} ${parsed.startYear}`;
    }
    const endQuarter = (parsed.startQuarter + parsed.count - 1) % 4;
    const endYear = parsed.startYear + Math.floor((parsed.startQuarter + parsed.count - 1) / 4);

    if (parsed.startYear === endYear) {
      return `${QUARTER_NAMES[parsed.startQuarter]} to ${QUARTER_NAMES[endQuarter]} ${parsed.startYear}`;
    }

    return `${QUARTER_NAMES[parsed.startQuarter]} ${parsed.startYear} to ${QUARTER_NAMES[endQuarter]} ${endYear}`;
  }

  if (parsed.type === "month") {
    if (parsed.count === 1) {
      return `${MONTH_NAMES[parsed.startMonth]} ${parsed.startYear}`;
    }
    const endMonth = (parsed.startMonth + parsed.count - 1) % 12;
    const endYear = parsed.startYear + Math.floor((parsed.startMonth + parsed.count - 1) / 12);

    if (parsed.startYear === endYear) {
      return `${MONTH_NAMES[parsed.startMonth]} to ${MONTH_NAMES[endMonth]} ${parsed.startYear}`;
    }

    return `${MONTH_NAMES[parsed.startMonth]} ${parsed.startYear} to ${MONTH_NAMES[endMonth]} ${endYear}`;
  }

  return null;
};
