import { SavedKundali } from "@/app/lib/kundali";

import { AnyObject, ChartStyle, PlanetRow, YoginiRow } from "./kundliTypes";

/*
|--------------------------------------------------------------------------
| Shared Kundli Parsing / Formatting Helpers
|--------------------------------------------------------------------------
|
| Extracted from app/free-kundli/[id]/page.tsx (pure relocation, no
| behavior change) so the exact same defensive parsing logic can be
| reused by any other caller with its own raw kundli API response —
| e.g. app/components/consultation/KundliModal.tsx.
*/

/*
|--------------------------------------------------------------------------
| API Constants
|--------------------------------------------------------------------------
*/

export const API_BASE =
  process.env.NEXT_PUBLIC_API_BASE_URL || "https://api.astrology.togwe.com/api";

/*
|--------------------------------------------------------------------------
| Planet Order
|--------------------------------------------------------------------------
*/

export const PLANET_ORDER = [
  "Ascendant",
  "Sun",
  "Moon",
  "Mars",
  "Mercury",
  "Jupiter",
  "Venus",
  "Saturn",
  "Rahu",
  "Ketu",
];

/*
|--------------------------------------------------------------------------
| Zodiac Order
|--------------------------------------------------------------------------
*/

export const ZODIACS = [
  "Aries",
  "Taurus",
  "Gemini",
  "Cancer",
  "Leo",
  "Virgo",
  "Libra",
  "Scorpio",
  "Sagittarius",
  "Capricorn",
  "Aquarius",
  "Pisces",
];

/*
|--------------------------------------------------------------------------
| Divisional Charts
|--------------------------------------------------------------------------
*/

export const DIVISIONAL_CHARTS = [
  {
    id: "D1",
    title: "Rashi Chart",
  },
  {
    id: "D2",
    title: "Hora Chart",
  },
  {
    id: "D3",
    title: "Drekkana Chart",
  },
  {
    id: "D4",
    title: "Chaturthamsha Chart",
  },
  {
    id: "D7",
    title: "Saptamsha Chart",
  },
  {
    id: "D9",
    title: "Navamsa Chart",
  },
  {
    id: "D10",
    title: "Dashamsha Chart",
  },
  {
    id: "D12",
    title: "Dwadashamsha Chart",
  },
  {
    id: "D16",
    title: "Shodasamsha Chart",
  },
  {
    id: "D20",
    title: "Vimshamsha Chart",
  },
  {
    id: "D24",
    title: "Chaturvimshamsha Chart",
  },
  {
    id: "D27",
    title: "Saptavimshamsha Chart",
  },
  {
    id: "D30",
    title: "Trimshamsha Chart",
  },
  {
    id: "D40",
    title: "Khavedamsha Chart",
  },
  {
    id: "D45",
    title: "Akshavedamsha Chart",
  },
  {
    id: "D60",
    title: "Shashtiamsha Chart",
  },
];

/*
|--------------------------------------------------------------------------
| Get API Response
|--------------------------------------------------------------------------
*/

export function getApiResponse(kundali: SavedKundali): AnyObject {
  const raw = (kundali as any)?.response;

  if (raw && typeof raw === "object") {
    return raw;
  }

  /*
   * Some older saved records may have
   * response directly on object.
   */

  return kundali as any;
}

/*
|--------------------------------------------------------------------------
| Get Complete Details
|--------------------------------------------------------------------------
*/

export function getCompleteDetails(response: AnyObject): AnyObject {
  if (Array.isArray(response?.complete_details)) {
    return response.complete_details[0] || {};
  }

  /*
   * If response itself is already
   * complete_details[0].
   */

  if (response?.birth_details || response?.details || response?.pdf_url) {
    return response;
  }

  /*
   * Saved API data may be nested.
   */

  if (response?.data) {
    return getCompleteDetails(response.data);
  }

  return {};
}

/*
|--------------------------------------------------------------------------
| Record
|--------------------------------------------------------------------------
*/

export function getRecord(response: AnyObject): AnyObject {
  if (Array.isArray(response?.recordList)) {
    return response.recordList[0] || {};
  }

  return {};
}

/*
|--------------------------------------------------------------------------
| Generate Chart URL
|--------------------------------------------------------------------------
|
| Standalone version of the page's original getChartImage(div) closure.
| It used to capture chart/birth-detail/chartStyle from page state —
| now takes them as explicit arguments so a different caller (e.g. a
| modal reading its own local variables) can build the same URLs.
*/

export function getKundliChartImageUrl(
  div: string,
  chart: AnyObject,
  birth: AnyObject,
  chartStyle: ChartStyle,
): string {
  /*
   * First use exact URL returned
   * by API.
   */

  if (chart?.chart_images?.[div]) {
    return chart.chart_images[div];
  }

  /*
   * D1 fallback.
   */

  if (div === "D1" && chart?.chart_image) {
    return chart.chart_image;
  }

  /*
   * Generate same API chart-image
   * URL for a divisional chart that
   * is not included in chart_images.
   */

  const dob = birth?.birthDate;

  const tob = birth?.birthTime;

  const lat = birth?.latitude;

  const lon = birth?.longitude;

  const tz = birth?.timezone;

  if (!dob || !tob || lat === undefined || lon === undefined) {
    return "";
  }

  const query = new URLSearchParams({
    div,
    dob: String(dob).substring(0, 10),
    lang: birth?.lang || "en",
    lat: Number(lat).toFixed(6),
    lon: Number(lon).toFixed(6),
    style: chartStyle,
    tob: String(tob),
    tz: String(tz ?? 5.5),
  });

  return `${API_BASE}/user/kundali/chart-image?${query.toString()}`;
}

/*
|--------------------------------------------------------------------------
| Build Planet Rows
|--------------------------------------------------------------------------
*/

export function buildPlanetRows(planetDetails: AnyObject): PlanetRow[] {
  const rows: PlanetRow[] = [];

  if (!planetDetails || typeof planetDetails !== "object") {
    return rows;
  }

  Object.entries(planetDetails).forEach(([key, value]) => {
    if (!value || typeof value !== "object") {
      return;
    }

    rows.push({
      key,
      full_name: value?.full_name || value?.name || key,
      name: value?.name,
      rasi_no: value?.rasi_no,
      zodiac: value?.zodiac,
      retro: Boolean(value?.retro),
      house: value?.house,
      global_degree: value?.global_degree,
      local_degree: value?.local_degree,
      pseudo_rasi: value?.pseudo_rasi,
      pseudo_rasi_lord: value?.pseudo_rasi_lord,
      pseudo_nakshatra: value?.pseudo_nakshatra,
      pseudo_nakshatra_no: value?.pseudo_nakshatra_no,
      pseudo_nakshatra_pada: value?.pseudo_nakshatra_pada,
      pseudo_nakshatra_lord: value?.pseudo_nakshatra_lord,
      sub_lord: value?.sub_lord,
      sub_sub_lord: value?.sub_sub_lord,
    });
  });

  /*
   * API normally returns correct order.
   * This only sorts known planets.
   */

  return rows.sort((a, b) => {
    const ai = PLANET_ORDER.findIndex(
      (name) => name.toLowerCase() === a.full_name.toLowerCase(),
    );

    const bi = PLANET_ORDER.findIndex(
      (name) => name.toLowerCase() === b.full_name.toLowerCase(),
    );

    return (ai === -1 ? 999 : ai) - (bi === -1 ? 999 : bi);
  });
}

/*
|--------------------------------------------------------------------------
| Build KP Planet Rows
|--------------------------------------------------------------------------
*/

export function buildKPPlanetRows(kpPlanets: AnyObject): PlanetRow[] {
  if (!kpPlanets || typeof kpPlanets !== "object") {
    return [];
  }

  return Object.entries(kpPlanets)
    .filter(
      ([key, value]) =>
        value &&
        typeof value === "object" &&
        key !== "ascendant" &&
        key !== "midheaven",
    )
    .map(([key, value]) => ({
      key,
      full_name: value?.full_name || value?.name || key,
      name: value?.name,
      rasi_no: value?.rasi_no,
      zodiac: value?.zodiac,
      retro: Boolean(value?.retro),
      house: value?.house,
      global_degree: value?.global_degree,
      local_degree: value?.local_degree,
      pseudo_rasi: value?.pseudo_rasi,
      pseudo_rasi_lord: value?.pseudo_rasi_lord,
      pseudo_nakshatra: value?.pseudo_nakshatra,
      pseudo_nakshatra_no: value?.pseudo_nakshatra_no,
      pseudo_nakshatra_pada: value?.pseudo_nakshatra_pada,
      pseudo_nakshatra_lord: value?.pseudo_nakshatra_lord,
      sub_lord: value?.sub_lord,
      sub_sub_lord: value?.sub_sub_lord,
    }));
}

/*
|--------------------------------------------------------------------------
| Ruling Planets (Asc/Moon sign+star+sub lord, Day Lord)
|--------------------------------------------------------------------------
|
| Derived entirely from the same confirmed real chart.kp.kp_planets
| data already used by the KP Planet Details table — no separate API
| field for "ruling planets" is known to exist.
*/

const WEEKDAY_LORDS: Record<string, string> = {
  sunday: "Sun",
  monday: "Moon",
  tuesday: "Mars",
  wednesday: "Mercury",
  thursday: "Jupiter",
  friday: "Venus",
  saturday: "Saturn",
};

const WEEKDAY_ORDER = [
  "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday",
];

export function getDayLord(panchang: AnyObject, birthDate: string): string {
  // Prefer the backend's own already-computed weekday (sunrise-based
  // Vedic day, not just the calendar date) over a hand-rolled guess.
  const weekdayName = panchang?.day || panchang?.weekday || panchang?.vaar;

  if (typeof weekdayName === "string" && WEEKDAY_LORDS[weekdayName.trim().toLowerCase()]) {
    return WEEKDAY_LORDS[weekdayName.trim().toLowerCase()];
  }

  if (!birthDate || typeof birthDate !== "string") {
    return "";
  }

  const parts = birthDate.split("-").map(Number);

  if (parts.length !== 3 || parts.some((part) => Number.isNaN(part))) {
    return "";
  }

  const [year, month, day] = parts;

  const parsedDate = new Date(year, month - 1, day);

  return WEEKDAY_LORDS[WEEKDAY_ORDER[parsedDate.getDay()]] || "";
}

export function getRulingPlanets(
  chart: AnyObject,
  panchang: AnyObject,
  birthDate: string,
): { label: string; value: string }[] {
  const kpPlanets = chart?.kp?.kp_planets ?? chart?.kp_planets;

  const rows: { label: string; value: string }[] = [];

  if (kpPlanets && typeof kpPlanets === "object") {
    const entries = Object.values(kpPlanets).filter(
      (value): value is AnyObject => value !== null && typeof value === "object",
    );

    const ascendant = entries.find((entry) => entry?.full_name === "Ascendant");

    const moon = entries.find((entry) => entry?.full_name === "Moon");

    if (ascendant?.pseudo_rasi_lord) rows.push({ label: "Asc Sign Lord", value: ascendant.pseudo_rasi_lord });

    if (ascendant?.pseudo_nakshatra_lord) rows.push({ label: "Asc Star Lord", value: ascendant.pseudo_nakshatra_lord });

    if (ascendant?.sub_lord) rows.push({ label: "Asc Sub Lord", value: ascendant.sub_lord });

    if (moon?.pseudo_rasi_lord) rows.push({ label: "Moon Sign Lord", value: moon.pseudo_rasi_lord });

    if (moon?.pseudo_nakshatra_lord) rows.push({ label: "Moon Star Lord", value: moon.pseudo_nakshatra_lord });

    if (moon?.sub_lord) rows.push({ label: "Moon Sub Lord", value: moon.sub_lord });
  }

  const dayLord = getDayLord(panchang, birthDate);

  if (dayLord) {
    rows.push({ label: "Day Lord", value: dayLord });
  }

  return rows;
}

/*
|--------------------------------------------------------------------------
| Build KP Cusps
|--------------------------------------------------------------------------
*/

export function buildKPColumns(chart: AnyObject): AnyObject[] {
  /*
   * Confirmed real shape: the KP cusp array is chart.kp.kp_houses
   * (each item: house, start_rasi/end_rasi, local_start_degree/
   * local_end_degree, bhavmadhya, start_nakshatra/end_nakshatra,
   * cusp_sub_lord/cusp_sub_sub_lord) — nested under a "kp" wrapper
   * alongside kp_planets, not flat on chart.
   */

  if (Array.isArray(chart?.kp?.kp_houses)) {
    return chart.kp.kp_houses;
  }

  if (Array.isArray(chart?.kp_houses)) {
    return chart.kp_houses;
  }

  if (Array.isArray(chart?.cusps)) {
    return chart.cusps;
  }

  if (Array.isArray(chart?.kp_cusps)) {
    return chart.kp_cusps;
  }

  if (Array.isArray(chart?.cusp_details)) {
    return chart.cusp_details;
  }

  return [];
}

/*
|--------------------------------------------------------------------------
| Planet Predictions
|--------------------------------------------------------------------------
*/

export function getPlanetPredictions(details: AnyObject): AnyObject[] {
  const source =
    details?.prediction?.planetary ||
    details?.planetary_predictions ||
    details?.predictions?.planetary ||
    {};

  const rows: AnyObject[] = [];

  if (source && typeof source === "object") {
    Object.entries(source).forEach(([planet, value]) => {
      if (Array.isArray(value)) {
        value.forEach((item) => {
          rows.push({
            planet_considered: item?.planet_considered || planet,
            ...item,
          });
        });
      } else if (value && typeof value === "object") {
        const entry = value as AnyObject;

        rows.push({
          planet_considered: entry?.planet_considered || planet,
          ...entry,
        });
      }
    });
  }

  /*
   * Actual API response also contains
   * prediction arrays by planet in the
   * prediction section.
   */

  const possiblePlanetKeys = [
    "Sun",
    "Moon",
    "Mars",
    "Mercury",
    "Jupiter",
    "Venus",
    "Saturn",
    "Rahu",
    "Ketu",
  ];

  possiblePlanetKeys.forEach((planet) => {
    const value = details?.prediction?.[planet];

    if (Array.isArray(value)) {
      value.forEach((item) => {
        const exists = rows.some(
          (row) =>
            row?.planet_considered === planet &&
            row?.personalised_prediction === item?.personalised_prediction,
        );

        if (!exists) {
          rows.push({
            planet_considered: planet,
            ...item,
          });
        }
      });
    }
  });

  return rows;
}

/*
|--------------------------------------------------------------------------
| Find First Value
|--------------------------------------------------------------------------
*/

export function findFirstValue(object: AnyObject, keys: string[]): any {
  if (!object) {
    return "—";
  }

  for (const key of keys) {
    if (
      object?.[key] !== undefined &&
      object?.[key] !== null &&
      object?.[key] !== ""
    ) {
      return object[key];
    }
  }

  return "—";
}

/*
|--------------------------------------------------------------------------
| Number
|--------------------------------------------------------------------------
*/

export function formatNumber(value: any, digits = 2): string {
  if (value === null || value === undefined || value === "") {
    return "—";
  }

  const number = Number(value);

  if (Number.isNaN(number)) {
    return String(value);
  }

  return number.toFixed(digits);
}

/*
|--------------------------------------------------------------------------
| Display Value
|--------------------------------------------------------------------------
*/

export function formatDisplayValue(value: any): string {
  if (value === null || value === undefined || value === "") {
    return "—";
  }

  if (Array.isArray(value)) {
    return value.join(", ");
  }

  if (typeof value === "object") {
    try {
      return JSON.stringify(value);
    } catch {
      return "—";
    }
  }

  return String(value);
}

/*
|--------------------------------------------------------------------------
| Month
|--------------------------------------------------------------------------
*/

export function getMonthName(month: number): string {
  const months = [
    "Jan",
    "Feb",
    "Mar",
    "Apr",
    "May",
    "Jun",
    "Jul",
    "Aug",
    "Sep",
    "Oct",
    "Nov",
    "Dec",
  ];

  return months[month - 1] || "";
}

/*
|--------------------------------------------------------------------------
| Birth Date
|--------------------------------------------------------------------------
*/

export function formatBirthDate(value: any): string {
  if (!value) {
    return "—";
  }

  const date = String(value).substring(0, 10);

  const parts = date.split("-");

  if (parts.length !== 3) {
    return String(value);
  }

  return `${parts[2]} ${getMonthName(Number(parts[1]))} ${parts[0]}`;
}

/*
|--------------------------------------------------------------------------
| Birth Time
|--------------------------------------------------------------------------
*/

export function formatBirthTime(value: any): string {
  if (!value) {
    return "—";
  }

  const parts = String(value).split(":");

  if (parts.length < 2) {
    return String(value);
  }

  let hour = Number(parts[0]);

  const minute = parts[1];

  if (Number.isNaN(hour)) {
    return String(value);
  }

  const suffix = hour >= 12 ? "PM" : "AM";

  hour = hour % 12 || 12;

  return `${String(hour).padStart(2, "0")}:${minute} ${suffix}`;
}

/*
|--------------------------------------------------------------------------
| Dasha Active-Period Detection
|--------------------------------------------------------------------------
|
| Best-effort: the confirmed real "mahadasha_order" values are already
| pre-formatted display strings like "31-07-2010 - 31-07-2026" (or
| "Birth - 31-07-2010" for the first period) — parses that same shape
| to flag whichever period contains today's date.
*/

const MONTH_ABBR: Record<string, number> = {
  jan: 0, feb: 1, mar: 2, apr: 3, may: 4, jun: 5,
  jul: 6, aug: 7, sep: 8, oct: 9, nov: 10, dec: 11,
};

export function parseDDMMYYYY(text: string): Date | null {
  const trimmed = text.trim();

  if (!trimmed || trimmed.toLowerCase() === "birth") {
    return null;
  }

  // Numeric "DD-MM-YYYY" (Vimshottari's mahadasha_order format).
  const numeric = trimmed.match(/^(\d{1,2})-(\d{1,2})-(\d{4})$/);

  if (numeric) {
    const [, day, month, year] = numeric;

    return new Date(Number(year), Number(month) - 1, Number(day));
  }

  // "DD-Mon-YYYY" (e.g. "15-Jan-1999") — seen in the Yogini reference.
  const withMonthName = trimmed.match(/^(\d{1,2})-([A-Za-z]{3,})-(\d{4})$/);

  if (withMonthName) {
    const [, day, monthName, year] = withMonthName;

    const month = MONTH_ABBR[monthName.slice(0, 3).toLowerCase()];

    if (month !== undefined) {
      return new Date(Number(year), month, Number(day));
    }
  }

  return null;
}

export function isDateRangeTextActive(rangeText: string): boolean {
  if (!rangeText || typeof rangeText !== "string") {
    return false;
  }

  const parts = rangeText.split(" - ").map((part) => part.trim());

  if (parts.length !== 2) {
    return false;
  }

  const start = parseDDMMYYYY(parts[0]);

  const end = parseDDMMYYYY(parts[1]);

  if (!end) {
    return false;
  }

  const now = new Date();

  const afterStart = start ? now >= start : true;

  const beforeEnd = now < end;

  return afterStart && beforeEnd;
}

/*
|--------------------------------------------------------------------------
| Yogini Dasha Rows
|--------------------------------------------------------------------------
|
| Not confirmed against a real response yet — no Postman entry and no
| pasted sample. Scans several plausible shapes (mirroring the
| confirmed Vimshottari structure's sibling naming) and renders an
| empty state rather than fabricated rows if none match.
*/

export function getYoginiRows(dasha: AnyObject): YoginiRow[] {
  const yogini = dasha?.yogini || {};

  const list =
    (Array.isArray(yogini?.mahadasha) && yogini.mahadasha) ||
    (Array.isArray(yogini?.periods) && yogini.periods) ||
    (Array.isArray(yogini?.dashas) && yogini.dashas) ||
    (Array.isArray(yogini) && yogini) ||
    null;

  if (!list) {
    return [];
  }

  return list
    .map((item: AnyObject) => {
      const code = item?.code || item?.short_name || item?.abbreviation || item?.planet || item?.lord || item?.name;

      if (!code) {
        return null;
      }

      const name = item?.name || item?.dasha || item?.full_name || "";

      const startDate = item?.start_date || item?.start || item?.from || "";

      const endDate = item?.end_date || item?.end || item?.to || "";

      return {
        code: String(code),
        name: String(name) === String(code) ? "" : String(name),
        startDate: String(startDate),
        endDate: String(endDate),
      };
    })
    .filter(Boolean) as YoginiRow[];
}
