/**
 * Tử Vi Số · Engine wrapper for iztro
 *
 * Wraps iztro's `astro.bySolar` / `astro.byLunar` into a normalized,
 * UI-friendly shape. iztro returns a Functional* class instance with i18n
 * already applied, but the shape is wide and lacks ergonomic helpers for
 * our specific layout (4×4 grid with center info).
 *
 * Why a wrapper:
 *   - Stable contract for components even if iztro evolves
 *   - One place to apply Vietnamese-specific formatting
 *   - Easier to mock in tests
 */
import { astro } from "iztro";

export type Gender = "Nam" | "Nữ";

export type CalendarType = "solar" | "lunar";

export type ChartInput = {
  /** YYYY-M-D (no zero-pad needed; iztro accepts both) */
  date: string;
  /** 0..12 — hour-pillar index (0 = early Tý 23:00–01:00, 12 = late Tý) */
  timeIndex: number;
  gender: Gender;
  calendar: CalendarType;
  /** for lunar input only */
  isLeapMonth?: boolean;
  fullName?: string;
};

export type Star = {
  name: string;
  /** miếu / vượng / đắc / lợi / bình / bất / hãm / "" */
  brightness?: string;
  /** Lộc / Quyền / Khoa / Kỵ / "" */
  mutagen?: string;
  /** major (chính tinh), minor (phụ tinh chính), adj (phụ tinh bàng) */
  kind: "major" | "minor" | "adj";
};

export type Palace = {
  index: number;
  /** Mệnh, Phụ Mẫu, ... */
  name: string;
  /** Heavenly stem of the palace cell */
  heavenlyStem: string;
  /** Earthly branch (Tý, Sửu, ...) */
  earthlyBranch: string;
  isBodyPalace: boolean;
  isOriginalPalace: boolean;
  majorStars: Star[];
  minorStars: Star[];
  /** Bàng tinh, sát tinh phụ — lower visual priority */
  adjStars: Star[];
};

export type Chart = {
  fullName: string;
  gender: Gender;
  /** "1990-3-15" */
  solarDate: string;
  /** "一九九〇年二月十九" — i18n by iztro, kept as-is for display */
  lunarDate: string;
  /** "Canh Ngọ - Kỷ Mão - Kỷ Mão - Giáp Tý" */
  chineseDate: string;
  /** Hour pillar label, e.g. "Giờ tý sớm 00:00~01:00" */
  time: string;
  /** Display range, e.g. "00:00~01:00" */
  timeRange: string;
  /** "Ngựa", "Mão", … */
  zodiac: string;
  /** Western: "Cung Song Ngư" */
  sign: string;
  /** Mệnh chủ (soul ruler star) */
  soulStar: string;
  /** Thân chủ (body ruler star) */
  bodyStar: string;
  /** Cục mệnh ("Thổ Ngũ Cục", …) */
  fiveElements: string;
  /** EarthlyBranchName of the body palace cell */
  bodyBranch: string;
  /** EarthlyBranchName of the soul palace cell */
  soulBranch: string;
  palaces: Palace[];
};

const BRANCH_ORDER = [
  "Dần",
  "Mão",
  "Thìn",
  "Tỵ",
  "Ngọ",
  "Mùi",
  "Thân",
  "Dậu",
  "Tuất",
  "Hợi",
  "Tý",
  "Sửu",
] as const;

/**
 * iztro returns palaces starting at Dần (index 0) by default. We expose them
 * as-is (index 0 → Dần) because the chart grid layout is driven by branch,
 * not by palace order.
 */
function normalizePalace(p: unknown, index: number): Palace {
  // iztro types are wide; treat as record and validate at runtime.
  const raw = p as Record<string, unknown>;
  const majors = (raw.majorStars ?? []) as Array<Record<string, unknown>>;
  const minors = (raw.minorStars ?? []) as Array<Record<string, unknown>>;
  const adjs = (raw.adjectiveStars ?? []) as Array<Record<string, unknown>>;
  return {
    index,
    name: String(raw.name ?? ""),
    heavenlyStem: String(raw.heavenlyStem ?? ""),
    earthlyBranch: String(raw.earthlyBranch ?? ""),
    isBodyPalace: Boolean(raw.isBodyPalace),
    isOriginalPalace: Boolean(raw.isOriginalPalace),
    majorStars: majors.map((s) => ({
      name: String(s.name ?? ""),
      brightness: s.brightness ? String(s.brightness) : "",
      mutagen: s.mutagen ? String(s.mutagen) : "",
      kind: "major",
    })),
    minorStars: minors.map((s) => ({
      name: String(s.name ?? ""),
      brightness: s.brightness ? String(s.brightness) : "",
      mutagen: s.mutagen ? String(s.mutagen) : "",
      kind: "minor",
    })),
    adjStars: adjs.map((s) => ({
      name: String(s.name ?? ""),
      brightness: s.brightness ? String(s.brightness) : "",
      mutagen: s.mutagen ? String(s.mutagen) : "",
      kind: "adj",
    })),
  };
}

/**
 * Build the 12-palace astrolabe (lá số) for the given input.
 *
 * Throws if inputs are invalid (date format, time index out of range, etc.)
 */
export function buildChart(input: ChartInput): Chart {
  if (!/^\d{4}-\d{1,2}-\d{1,2}$/.test(input.date)) {
    throw new Error(`Date phải đúng định dạng YYYY-M-D (nhận: "${input.date}")`);
  }
  if (input.timeIndex < 0 || input.timeIndex > 12) {
    throw new Error(`timeIndex phải trong 0..12 (nhận: ${input.timeIndex})`);
  }
  const genderZh = input.gender === "Nam" ? "男" : "女";

  // iztro typings expect localized literal unions; cast through string.
  const result =
    input.calendar === "solar"
      ? // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (astro as any).bySolar(input.date, input.timeIndex, genderZh, true, "vi-VN")
      : // eslint-disable-next-line @typescript-eslint/no-explicit-any
        (astro as any).byLunar(
          input.date,
          input.timeIndex,
          genderZh,
          input.isLeapMonth ?? false,
          true,
          "vi-VN"
        );

  const palacesRaw = (result.palaces ?? []) as unknown[];
  const palaces = palacesRaw.map((p, i) => normalizePalace(p, i));

  return {
    fullName: input.fullName?.trim() || "",
    gender: input.gender,
    solarDate: String(result.solarDate ?? ""),
    lunarDate: String(result.lunarDate ?? ""),
    chineseDate: String(result.chineseDate ?? ""),
    time: String(result.time ?? ""),
    timeRange: String(result.timeRange ?? ""),
    zodiac: String(result.zodiac ?? ""),
    sign: String(result.sign ?? ""),
    soulStar: String(result.soul ?? ""),
    bodyStar: String(result.body ?? ""),
    fiveElements: String(result.fiveElementsClass ?? ""),
    bodyBranch: String(result.earthlyBranchOfBodyPalace ?? ""),
    soulBranch: String(result.earthlyBranchOfSoulPalace ?? ""),
    palaces,
  };
}

/**
 * Find a palace by its earthly branch name. Used by ChartGrid to place
 * palace cells at fixed grid positions (4×4 layout).
 */
export function findPalaceByBranch(
  chart: Chart,
  branch: (typeof BRANCH_ORDER)[number]
): Palace | undefined {
  return chart.palaces.find((p) => p.earthlyBranch === branch);
}

export const PALACE_BRANCHES = BRANCH_ORDER;
