/**
 * Dosha Analysis Module
 *
 * Detects and analyzes major Vedic astrology doshas:
 * - Manglik (Kuja) Dosha
 * - Kaal Sarp Dosha
 * - Ganda Moola Dosha
 * - Gandanta Analysis
 */

import type { GrahaPosition, HouseInfo, LagnaInfo } from '../types';

// ── Manglik Dosha ──────────────────────────────────────────────────────────────

export interface ManglikResult {
  isManglik: boolean;
  severity: 'none' | 'mild' | 'full';
  marsHouse: number;
  details: string;
  cancellations: string[];
}

const MANGLIK_HOUSES = [1, 2, 4, 7, 8, 12];

const MARS_OWN_SIGNS = ['Aries', 'Scorpio'];
const MARS_EXALTATION_SIGN = 'Capricorn';
const FIRE_SIGNS = ['Aries', 'Leo', 'Sagittarius'];

/**
 * Detect Manglik (Kuja) Dosha from a birth chart.
 */
export function analyzeManglik(
  planets: GrahaPosition[],
  houses: HouseInfo[],
): ManglikResult {
  const mars = planets.find((p) => p.name === 'Mars');
  const jupiter = planets.find((p) => p.name === 'Jupiter');

  if (!mars) {
    return {
      isManglik: false,
      severity: 'none',
      marsHouse: 0,
      details: 'Mars position not available.',
      cancellations: [],
    };
  }

  // Find Mars house
  const marsHouse = findPlanetHouse(mars.name, houses);

  if (!MANGLIK_HOUSES.includes(marsHouse)) {
    return {
      isManglik: false,
      severity: 'none',
      marsHouse,
      details: `Mars is in house ${marsHouse}, which does not cause Manglik Dosha.`,
      cancellations: [],
    };
  }

  // Mars is in a Manglik house — check cancellations
  const cancellations: string[] = [];

  // 1. Mars in own sign (Aries, Scorpio)
  if (MARS_OWN_SIGNS.includes(mars.signName)) {
    cancellations.push(`Mars is in own sign (${mars.signName}) — Manglik cancelled.`);
  }

  // 2. Mars in exaltation (Capricorn)
  if (mars.signName === MARS_EXALTATION_SIGN) {
    cancellations.push('Mars is exalted in Capricorn — Manglik cancelled.');
  }

  // 3. Mars conjunct Jupiter in same house
  const jupiterHouse = jupiter ? findPlanetHouse(jupiter.name, houses) : 0;
  if (jupiter && jupiterHouse === marsHouse) {
    cancellations.push('Mars is conjunct Jupiter in the same house — Manglik cancelled.');
  }

  // 4. Mars aspected by Jupiter (Jupiter aspects houses 5, 7, 9 from itself)
  if (jupiter && !cancellations.some((c) => c.includes('conjunct Jupiter'))) {
    const jupiterAspects = getJupiterAspectedHouses(jupiterHouse);
    if (jupiterAspects.includes(marsHouse)) {
      cancellations.push('Mars is aspected by Jupiter — Manglik mitigated.');
    }
  }

  // 5. Mars in fire sign in 1st house
  if (marsHouse === 1 && FIRE_SIGNS.includes(mars.signName)) {
    cancellations.push(
      `Mars is in a fire sign (${mars.signName}) in the 1st house — Manglik cancelled.`,
    );
  }

  // 6. Both-partner note
  cancellations.push(
    'Note: If both partners are Manglik, the dosha is considered cancelled (cannot be verified from a single chart).',
  );

  // Determine severity
  const hasFullCancellation = cancellations.some(
    (c) =>
      c.includes('cancelled') && !c.includes('both partners') && !c.includes('mitigated'),
  );

  let severity: ManglikResult['severity'];
  if (hasFullCancellation) {
    severity = 'none';
  } else if ([7, 8].includes(marsHouse)) {
    severity = 'full';
  } else {
    // Houses 1, 2, 4, 12 — check if Jupiter aspect mitigates
    const jupiterMitigates = cancellations.some((c) => c.includes('mitigated'));
    severity = jupiterMitigates ? 'mild' : 'full';
  }

  // If severity ended up 'none' due to cancellation, not Manglik
  const isManglik = severity !== 'none';

  const details = isManglik
    ? `Mars in house ${marsHouse} causes ${severity} Manglik Dosha.`
    : `Mars in house ${marsHouse} would cause Manglik Dosha, but it is cancelled.`;

  return {
    isManglik,
    severity,
    marsHouse,
    details,
    cancellations,
  };
}

// ── Kaal Sarp Dosha ────────────────────────────────────────────────────────────

export interface KaalSarpResult {
  hasDosha: boolean;
  type: string | null;
  rahuHouse: number;
  ketuHouse: number;
  allPlanetsOnOneSide: boolean;
  details: string;
  affectedHouses: number[];
}

const KAAL_SARP_TYPES: Record<number, string> = {
  1: 'Anant',
  2: 'Kulik',
  3: 'Vasuki',
  4: 'Shankhpal',
  5: 'Padma',
  6: 'Mahapadma',
  7: 'Takshak',
  8: 'Karkotak',
  9: 'Shankhchur',
  10: 'Ghatak',
  11: 'Vishdhar',
  12: 'Sheshnag',
};

/**
 * Detect Kaal Sarp Dosha.
 *
 * All 7 planets (Sun through Saturn) must be on one side of the Rahu-Ketu axis.
 */
export function analyzeKaalSarp(
  planets: GrahaPosition[],
  houses: HouseInfo[],
): KaalSarpResult {
  const rahu = planets.find((p) => p.name === 'Rahu');
  const ketu = planets.find((p) => p.name === 'Ketu');

  if (!rahu || !ketu) {
    return {
      hasDosha: false,
      type: null,
      rahuHouse: 0,
      ketuHouse: 0,
      allPlanetsOnOneSide: false,
      details: 'Rahu/Ketu positions not available.',
      affectedHouses: [],
    };
  }

  const rahuHouse = findPlanetHouse('Rahu', houses);
  const ketuHouse = findPlanetHouse('Ketu', houses);

  // Get the 7 main planets (exclude Rahu/Ketu)
  const mainPlanets = planets.filter(
    (p) => p.name !== 'Rahu' && p.name !== 'Ketu',
  );
  const mainPlanetHouses = mainPlanets.map((p) => findPlanetHouse(p.name, houses));

  // Houses from Rahu to Ketu (clockwise, i.e. ascending house numbers)
  const rahuToKetu = getHousesBetween(rahuHouse, ketuHouse);
  const ketuToRahu = getHousesBetween(ketuHouse, rahuHouse);

  const allInRahuToKetu = mainPlanetHouses.every((h) => rahuToKetu.includes(h));
  const allInKetuToRahu = mainPlanetHouses.every((h) => ketuToRahu.includes(h));

  const allOnOneSide = allInRahuToKetu || allInKetuToRahu;

  if (!allOnOneSide) {
    return {
      hasDosha: false,
      type: null,
      rahuHouse,
      ketuHouse,
      allPlanetsOnOneSide: false,
      details:
        'Planets are on both sides of the Rahu-Ketu axis. No Kaal Sarp Dosha.',
      affectedHouses: [],
    };
  }

  const isKaalAmrit = allInKetuToRahu;
  const type = KAAL_SARP_TYPES[rahuHouse] || null;
  const affectedHouses = allInRahuToKetu ? rahuToKetu : ketuToRahu;

  const details = isKaalAmrit
    ? `Kaal Amrit Yoga (${type} type) — all planets between Ketu (house ${ketuHouse}) and Rahu (house ${rahuHouse}). This is considered auspicious.`
    : `Kaal Sarp Dosha (${type} type) — all planets between Rahu (house ${rahuHouse}) and Ketu (house ${ketuHouse}).`;

  return {
    hasDosha: !isKaalAmrit,
    type,
    rahuHouse,
    ketuHouse,
    allPlanetsOnOneSide: true,
    details,
    affectedHouses,
  };
}

// ── Ganda Moola Dosha ──────────────────────────────────────────────────────────

export interface GandaMoolaResult {
  hasDosha: boolean;
  moonNakshatra: string;
  moonPada: number;
  affectedNakshatras: string[];
  details: string;
  severity: 'none' | 'mild' | 'severe';
}

const GANDA_MOOLA_NAKSHATRAS = [
  'Ashwini',    // 1
  'Ashlesha',   // 9
  'Magha',      // 10
  'Jyeshtha',   // 18
  'Moola',      // 19
  'Revati',     // 27
];

// Junction nakshatras where Pada 1 = severe
const SEVERE_PADA_1 = ['Ashwini', 'Magha', 'Moola'];
// Junction nakshatras where Pada 4 = severe
const SEVERE_PADA_4 = ['Ashlesha', 'Jyeshtha', 'Revati'];

/**
 * Detect Ganda Moola Dosha based on Moon's nakshatra.
 */
export function analyzeGandaMoola(
  planets: GrahaPosition[],
): GandaMoolaResult {
  const moon = planets.find((p) => p.name === 'Moon');

  if (!moon) {
    return {
      hasDosha: false,
      moonNakshatra: '',
      moonPada: 0,
      affectedNakshatras: [],
      details: 'Moon position not available.',
      severity: 'none',
    };
  }

  const { nakshatra, nakshatraPada } = moon;

  if (!GANDA_MOOLA_NAKSHATRAS.includes(nakshatra)) {
    return {
      hasDosha: false,
      moonNakshatra: nakshatra,
      moonPada: nakshatraPada,
      affectedNakshatras: [],
      details: `Moon is in ${nakshatra}, which is not a Ganda Moola nakshatra.`,
      severity: 'none',
    };
  }

  // Determine severity
  let severity: GandaMoolaResult['severity'] = 'mild';

  if (SEVERE_PADA_1.includes(nakshatra) && nakshatraPada === 1) {
    severity = 'severe';
  } else if (SEVERE_PADA_4.includes(nakshatra) && nakshatraPada === 4) {
    severity = 'severe';
  }

  const details =
    severity === 'severe'
      ? `Moon in ${nakshatra} Pada ${nakshatraPada} causes severe Ganda Moola Dosha (junction point of nakshatra).`
      : `Moon in ${nakshatra} Pada ${nakshatraPada} causes mild Ganda Moola Dosha.`;

  return {
    hasDosha: true,
    moonNakshatra: nakshatra,
    moonPada: nakshatraPada,
    affectedNakshatras: [nakshatra],
    details,
    severity,
  };
}

// ── Gandanta Analysis ──────────────────────────────────────────────────────────

export interface GandantaPlanet {
  name: string;
  signName: string;
  degree: number;
  type: 'nakshatra_gandanta' | 'rashi_gandanta' | 'tithi_gandanta';
  details: string;
}

export interface GandantaResult {
  hasGandanta: boolean;
  planets: GandantaPlanet[];
}

/**
 * Water-Fire sign junctions (sign numbers).
 * Water sign last 3°20' or Fire sign first 3°20'.
 */
const GANDANTA_JUNCTIONS: { water: number; fire: number }[] = [
  { water: 4, fire: 5 },   // Cancer → Leo
  { water: 8, fire: 9 },   // Scorpio → Sagittarius
  { water: 12, fire: 1 },  // Pisces → Aries
];

const GANDANTA_ORB = 3 + 20 / 60; // 3°20' = one nakshatra pada

const SIGN_NAMES: Record<number, string> = {
  1: 'Aries', 2: 'Taurus', 3: 'Gemini', 4: 'Cancer',
  5: 'Leo', 6: 'Virgo', 7: 'Libra', 8: 'Scorpio',
  9: 'Sagittarius', 10: 'Capricorn', 11: 'Aquarius', 12: 'Pisces',
};

/**
 * Analyze Gandanta positions for all planets and Lagna.
 */
export function analyzeGandanta(
  planets: GrahaPosition[],
  lagna: LagnaInfo,
): GandantaResult {
  const affected: GandantaPlanet[] = [];

  // Check all planets
  for (const planet of planets) {
    const result = checkGandanta(planet.name, planet.signNumber, planet.degreeInSign);
    if (result) {
      affected.push(result);
    }
  }

  // Check Lagna
  const lagnaResult = checkGandanta('Lagna', lagna.signNumber, lagna.degreeInSign);
  if (lagnaResult) {
    affected.push(lagnaResult);
  }

  return {
    hasGandanta: affected.length > 0,
    planets: affected,
  };
}

function checkGandanta(
  name: string,
  signNumber: number,
  degreeInSign: number,
): GandantaPlanet | null {
  for (const junction of GANDANTA_JUNCTIONS) {
    // Water sign: last 3°20' (i.e., degree >= 30 - 3.333...)
    if (signNumber === junction.water && degreeInSign >= 30 - GANDANTA_ORB) {
      return {
        name,
        signName: SIGN_NAMES[signNumber],
        degree: degreeInSign,
        type: 'rashi_gandanta',
        details: `${name} at ${degreeInSign.toFixed(2)}° in ${SIGN_NAMES[signNumber]} is in Gandanta zone (last ${GANDANTA_ORB.toFixed(2)}° of water sign, junction with ${SIGN_NAMES[junction.fire]}).`,
      };
    }

    // Fire sign: first 3°20'
    if (signNumber === junction.fire && degreeInSign <= GANDANTA_ORB) {
      return {
        name,
        signName: SIGN_NAMES[signNumber],
        degree: degreeInSign,
        type: 'rashi_gandanta',
        details: `${name} at ${degreeInSign.toFixed(2)}° in ${SIGN_NAMES[signNumber]} is in Gandanta zone (first ${GANDANTA_ORB.toFixed(2)}° of fire sign, junction with ${SIGN_NAMES[junction.water]}).`,
      };
    }
  }

  return null;
}

// ── Helpers ────────────────────────────────────────────────────────────────────

/**
 * Find which house a planet occupies from the houses array.
 */
function findPlanetHouse(planetName: string, houses: HouseInfo[]): number {
  for (const house of houses) {
    if (house.planets.includes(planetName as any)) {
      return house.number;
    }
  }
  return 0;
}

/**
 * Get houses between two houses (exclusive of both endpoints),
 * moving clockwise (ascending house numbers wrapping at 12).
 */
function getHousesBetween(fromHouse: number, toHouse: number): number[] {
  const result: number[] = [];
  let current = fromHouse;
  while (true) {
    current = current === 12 ? 1 : current + 1;
    if (current === toHouse) break;
    result.push(current);
  }
  return result;
}

/**
 * Get houses that Jupiter aspects from a given house.
 * Jupiter has special aspects on 5th, 7th, and 9th houses from itself.
 */
function getJupiterAspectedHouses(jupiterHouse: number): number[] {
  return [5, 7, 9].map((offset) => ((jupiterHouse - 1 + offset) % 12) + 1);
}
