export type ZodiacSign =
  | "capricorn"
  | "aquarius"
  | "pisces"
  | "aries"
  | "taurus"
  | "gemini"
  | "cancer"
  | "leo"
  | "virgo"
  | "libra"
  | "scorpio"
  | "sagittarius";

interface ZodiacBoundary {
  sign: ZodiacSign;
  month: number;
  day: number;
}

const DAYS_BEFORE_MONTH = [
  0, // January
  31, // February
  59, // March
  90, // April
  120, // May
  151, // June
  181, // July
  212, // August
  243, // September
  273, // October
  304, // November
  334, // December
];

const ZODIAC_BOUNDARIES: ZodiacBoundary[] = [
  { sign: "capricorn", month: 1, day: 1 },
  { sign: "aquarius", month: 1, day: 20 },
  { sign: "pisces", month: 2, day: 19 },
  { sign: "aries", month: 3, day: 21 },
  { sign: "taurus", month: 4, day: 20 },
  { sign: "gemini", month: 5, day: 21 },
  { sign: "cancer", month: 6, day: 21 },
  { sign: "leo", month: 7, day: 23 },
  { sign: "virgo", month: 8, day: 23 },
  { sign: "libra", month: 9, day: 23 },
  { sign: "scorpio", month: 10, day: 23 },
  { sign: "sagittarius", month: 11, day: 22 },
  { sign: "capricorn", month: 12, day: 22 },
];

function isValidMonth(month: number): boolean {
  return Number.isInteger(month) && month >= 1 && month <= 12;
}

function getDaysInMonth(month: number): number {
  return new Date(2024, month, 0).getDate();
}

function toDayOfYear(month: number, day: number): number | null {
  if (!isValidMonth(month)) {
    return null;
  }

  const maxDay = getDaysInMonth(month);
  if (!Number.isInteger(day) || day < 1 || day > maxDay) {
    return null;
  }

  return DAYS_BEFORE_MONTH[month - 1] + day;
}

function boundaryToDayOfYear(boundary: ZodiacBoundary): number {
  const dayOfYear = toDayOfYear(boundary.month, boundary.day);
  if (dayOfYear === null) {
    throw new Error(`Invalid zodiac boundary: ${boundary.sign}`);
  }
  return dayOfYear;
}

const ZODIAC_BOUNDARIES_WITH_DAY = ZODIAC_BOUNDARIES.map((boundary) => ({
  ...boundary,
  dayOfYear: boundaryToDayOfYear(boundary),
}));

export function getZodiacSign(month: number, day: number): ZodiacSign | null {
  const dayOfYear = toDayOfYear(month, day);
  if (dayOfYear === null) {
    return null;
  }

  let currentSign: ZodiacSign = "capricorn";

  for (const boundary of ZODIAC_BOUNDARIES_WITH_DAY) {
    if (dayOfYear >= boundary.dayOfYear) {
      currentSign = boundary.sign;
    } else {
      break;
    }
  }

  return currentSign;
}
