"""
Tests for the Surya Siddhanta calculation engine.

Verifies:
  1. Ahargana computation
  2. Mean and true longitudes
  3. Manda samskara (equation of center) bounds
  4. Panchanga elements vs Drik (within expected tolerances)
  5. SS sunrise computation
  6. Full SS panchanga output (feature parity with Drik)
  7. Side-by-side comparison via method="both"
"""

from datetime import date
from kaalavidya import Panchanga
from kaalavidya.surya_siddhanta.ganita import (
    compute_ahargana,
    compute_sun_true,
    compute_moon_true,
    mean_sun,
    mean_moon,
    sun_apogee,
    moon_apogee,
    moon_node,
    compute_all_true,
    compute_tithi_ss,
    compute_nakshatra_ss,
    compute_yoga_ss,
    compute_karana_ss,
)
from kaalavidya.surya_siddhanta.constants import (
    KALI_EPOCH_JD,
    DAILY_MOTION_SUN,
    DAILY_MOTION_MOON,
)
from kaalavidya.surya_siddhanta.sunrise import compute_ss_sunrise_sunset
from kaalavidya.surya_siddhanta.panchanga_ss import compute_full_ss_panchanga


def test_ahargana():
    """Verify Ahargana for known dates."""
    print("=== Ahargana Tests ===")

    # Dec 3, 1998 at noon UT → JD = 2451150.5 (approx)
    jd_dec3_1998 = 2451150.5  # midnight UT
    ahargana = compute_ahargana(jd_dec3_1998)
    print(f"  Dec 3, 1998 midnight UT: Ahargana = {ahargana:.1f} days")
    assert abs(ahargana - 1862685.0) < 1.0, f"Expected ~1862685, got {ahargana}"

    # Kali epoch itself → Ahargana = 0
    ahargana_epoch = compute_ahargana(KALI_EPOCH_JD)
    print(f"  Kali epoch: Ahargana = {ahargana_epoch:.1f} days")
    assert ahargana_epoch == 0.0, "Ahargana at epoch must be 0"

    # Check that Ahargana corresponds to ~5100 years
    years = ahargana / 365.25
    print(f"  Elapsed: ~{years:.0f} years from Kali epoch")
    assert 5090 < years < 5110, f"Expected ~5100 years, got {years:.0f}"

    print("  ✓ Ahargana tests passed\n")


def test_daily_motions():
    """Verify daily motions are close to known values."""
    print("=== Daily Motion Tests ===")

    print(f"  Sun  daily motion: {DAILY_MOTION_SUN:.6f}° (modern: ~0.985609°)")
    assert abs(DAILY_MOTION_SUN - 0.985609) < 0.001, "Sun daily motion out of range"

    print(f"  Moon daily motion: {DAILY_MOTION_MOON:.6f}° (modern: ~13.17636°)")
    assert abs(DAILY_MOTION_MOON - 13.17636) < 0.01, "Moon daily motion out of range"

    print("  ✓ Daily motion tests passed\n")


def test_mean_longitudes():
    """Verify mean longitudes at epoch are 0°."""
    print("=== Mean Longitude Tests ===")

    ahargana_zero = 0.0
    ms = mean_sun(ahargana_zero)
    mm = mean_moon(ahargana_zero)
    print(f"  At Kali epoch: mean Sun = {ms:.4f}°, mean Moon = {mm:.4f}°")
    assert ms == 0.0, "Mean Sun at epoch must be 0°"
    assert mm == 0.0, "Mean Moon at epoch must be 0°"

    ahargana_1yr = 365.25
    ms_1yr = mean_sun(ahargana_1yr)
    print(f"  After 1 year: mean Sun = {ms_1yr:.4f}° (expected ~360°)")
    assert abs(ms_1yr) < 1.0 or abs(ms_1yr - 360.0) < 1.0

    print("  ✓ Mean longitude tests passed\n")


def test_manda_samskara_bounds():
    """Verify equation of center is within expected bounds."""
    print("=== Manda Samskara Tests ===")

    ahargana = 1862685.0  # Dec 3, 1998
    data = compute_all_true(ahargana)

    sun_eq = abs(data["sun_equation"])
    moon_eq = abs(data["moon_equation"])

    print(f"  Sun equation : {data['sun_equation']:+.4f}° (max expected ~2.2°)")
    print(f"  Moon equation: {data['moon_equation']:+.4f}° (max expected ~5.1°)")

    assert sun_eq < 2.5, f"Sun equation {sun_eq}° exceeds 2.5° max"
    assert moon_eq < 5.5, f"Moon equation {moon_eq}° exceeds 5.5° max"

    print("  ✓ Manda samskara bounds passed\n")


def test_true_longitudes_range():
    """True longitudes must be in [0, 360)."""
    print("=== True Longitude Range Tests ===")

    test_aharganas = [0.0, 100000.0, 500000.0, 1000000.0, 1862685.0]

    for ag in test_aharganas:
        ts = compute_sun_true(ag)
        tm = compute_moon_true(ag)
        assert 0 <= ts < 360, f"Sun {ts}° out of range at ahargana={ag}"
        assert 0 <= tm < 360, f"Moon {tm}° out of range at ahargana={ag}"

    print("  ✓ All longitudes in [0°, 360°)\n")


def test_ss_sunrise():
    """Test SS sunrise computation."""
    print("=== SS Sunrise Tests ===")

    result = compute_ss_sunrise_sunset(
        date(1998, 12, 3), latitude=16.5062, longitude=80.648,
        timezone="Asia/Kolkata"
    )

    sr = result["sunrise"]
    ss = result["sunset"]
    print(f"  Dec 3, 1998 Vijayawada:")
    print(f"    SS Sunrise: {sr.strftime('%I:%M:%S %p')}")
    print(f"    SS Sunset : {ss.strftime('%I:%M:%S %p')}")
    print(f"    Day length: {result['day_duration_hours']:.2f} hours")
    print(f"    Declination: {result['declination']:.2f}°")
    print(f"    Chara: {result['chara_deg']:.2f}°")
    print(f"    EoT: {result['equation_of_time_min']:.2f} min")

    assert 5 <= sr.hour <= 7, f"Sunrise hour {sr.hour} out of range"
    assert result["day_duration_hours"] < 12.0, "Day length should be < 12h in December"
    assert result["declination"] < 0, "December declination should be negative"

    print("  ✓ SS sunrise test passed\n")


def test_ss_sunrise_vs_drik():
    """Compare SS sunrise with Drik sunrise across dates."""
    print("=== SS Sunrise vs Drik ===")
    print(f"  {'Date':<16s} {'SS Sunrise':>12s} {'Drik':>10s} {'Δ(min)':>8s}")
    print("  " + "-" * 50)

    test_dates = [
        (1998, 12, 3),
        (2025, 2, 18),
        (2024, 6, 21),
        (2024, 12, 21),
    ]

    for y, m, d in test_dates:
        ss = compute_ss_sunrise_sunset(date(y, m, d), 16.5062, 80.648, "Asia/Kolkata")
        p = Panchanga(year=y, month=m, day=d, latitude=16.5062, longitude=80.648,
                      timezone="Asia/Kolkata", city="Vijayawada")
        dk = p.compute()

        diff_min = (ss["sunrise"] - dk.sun.sunrise).total_seconds() / 60
        print(f"  {y}-{m:02d}-{d:02d}       {ss['sunrise'].strftime('%I:%M %p'):>12s} {dk.sun.sunrise.strftime('%I:%M %p'):>10s} {diff_min:>+7.1f}m")

        assert abs(diff_min) < 30, f"SS sunrise differs by {diff_min:.1f} min (too much)"

    print("  ✓ SS sunrise comparison passed\n")


def test_full_ss_panchanga_features():
    """
    Verify that the SS panchanga has all the features of Drik panchanga.

    The SS DailyPanchanga should contain:
      ✓ Sun times (sunrise, sunset)
      ✓ Vara + lord
      ✓ Tithi with transition times, devata, group
      ✓ Nakshatra with transition times, pada, devata, graha lord
      ✓ Yoga with transition times
      ✓ Karana with transition times
      ✓ Moon rashi, Sun rashi
      ✓ Masa (Amanta + Purnimanta) with Adhika/Kshaya
      ✓ Paksha, Samvatsara
      ✓ Ritu (Solar + Lunar), Ayana
      ✓ Era years
      ✓ Hora table
      ✓ Rahu Kala, Yamagandam, Gulika Kala
      ✓ Durmuhurta, Varjyam
      ✓ Brahma Muhurta, Abhijit Muhurta
      ✓ Amrit Kalam
      ✗ Lagna table (not available in SS mode)
      ✗ Maudhya (not available in SS mode)
      ✗ Eclipses (not available in SS mode)
    """
    print("=== Full SS Panchanga Feature Check ===")

    ss = compute_full_ss_panchanga(
        date(1998, 12, 3), latitude=16.5062, longitude=80.648,
        timezone="Asia/Kolkata", city="Vijayawada",
    )

    # Computation method
    assert ss.computation_method == "Surya Siddhanta", f"Got: {ss.computation_method}"
    print("  ✓ computation_method = 'Surya Siddhanta'")

    # Sun times
    assert ss.sun is not None, "Sun times missing"
    assert ss.sun.sunrise is not None, "Sunrise missing"
    assert ss.sun.sunset is not None, "Sunset missing"
    print(f"  ✓ Sun times: {ss.sun.sunrise.strftime('%I:%M %p')} – {ss.sun.sunset.strftime('%I:%M %p')}")

    # Vara
    assert ss.vara, "Vara missing"
    assert ss.vara_lord, "Vara lord missing"
    print(f"  ✓ Vara: {ss.vara} (Lord: {ss.vara_lord})")

    # Tithi
    assert len(ss.tithi) > 0, "Tithi empty"
    t = ss.tithi[0]
    assert t.name, "Tithi name missing"
    assert t.devata, "Tithi devata missing"
    assert t.group, "Tithi group missing"
    print(f"  ✓ Tithi: {t.name} [Devata: {t.devata}, Group: {t.group}]")

    # Nakshatra
    assert len(ss.nakshatra) > 0, "Nakshatra empty"
    n = ss.nakshatra[0]
    assert n.name, "Nakshatra name missing"
    assert n.pada >= 1, "Nakshatra pada missing"
    assert n.devata, "Nakshatra devata missing"
    assert n.graha_lord, "Nakshatra graha lord missing"
    print(f"  ✓ Nakshatra: {n.name} (Pada {n.pada}) [Devata: {n.devata}, Lord: {n.graha_lord}]")

    # Yoga
    assert len(ss.yoga) > 0, "Yoga empty"
    print(f"  ✓ Yoga: {ss.yoga[0].name}")

    # Karana
    assert len(ss.karana) > 0, "Karana empty"
    print(f"  ✓ Karana: {ss.karana[0].name}")

    # Rashi
    assert ss.moon_rashi is not None, "Moon rashi missing"
    assert ss.sun_rashi is not None, "Sun rashi missing"
    print(f"  ✓ Moon Rashi: {ss.moon_rashi.name} ({ss.moon_rashi.degree:.1f}°)")
    print(f"  ✓ Sun Rashi: {ss.sun_rashi.name} ({ss.sun_rashi.degree:.1f}°)")

    # Calendar
    assert ss.masa is not None, "Amanta masa missing"
    assert ss.masa_purnimanta is not None, "Purnimanta masa missing"
    assert ss.paksha, "Paksha missing"
    assert ss.samvatsara, "Samvatsara missing"
    print(f"  ✓ Masa: {ss.masa.display_name()} (A) / {ss.masa_purnimanta.display_name()} (P)")
    print(f"  ✓ Paksha: {ss.paksha}")
    print(f"  ✓ Samvatsara: {ss.samvatsara}")

    # Ritu & Ayana
    assert ss.ritu_solar, "Solar ritu missing"
    assert ss.ritu_lunar, "Lunar ritu missing"
    assert ss.ayana, "Ayana missing"
    print(f"  ✓ Ritu: {ss.ritu_solar} (Solar) / {ss.ritu_lunar} (Lunar)")
    print(f"  ✓ Ayana: {ss.ayana}")

    # Era years
    assert len(ss.era_years) > 0, "Era years empty"
    print(f"  ✓ Era years: {len(ss.era_years)} entries")

    # Hora table
    assert len(ss.hora_table) == 24, f"Hora table should have 24 entries, got {len(ss.hora_table)}"
    print(f"  ✓ Hora table: {len(ss.hora_table)} entries")

    # Inauspicious periods
    assert ss.rahu_kala is not None, "Rahu Kala missing"
    assert ss.yamagandam is not None, "Yamagandam missing"
    assert ss.gulika_kala is not None, "Gulika Kala missing"
    assert len(ss.durmuhurta) > 0, "Durmuhurta empty"
    print(f"  ✓ Rahu Kala: {ss.rahu_kala.starts_at.strftime('%I:%M %p')} – {ss.rahu_kala.ends_at.strftime('%I:%M %p')}")
    print(f"  ✓ Durmuhurta: {len(ss.durmuhurta)} period(s)")
    print(f"  ✓ Varjyam: {len(ss.varjyam)} period(s)")

    # Auspicious periods
    assert ss.brahma_muhurta is not None, "Brahma Muhurta missing"
    assert ss.abhijit_muhurta is not None, "Abhijit Muhurta missing"
    print(f"  ✓ Brahma Muhurta: {ss.brahma_muhurta.starts_at.strftime('%I:%M %p')} – {ss.brahma_muhurta.ends_at.strftime('%I:%M %p')}")
    print(f"  ✓ Abhijit Muhurta: {ss.abhijit_muhurta.starts_at.strftime('%I:%M %p')} – {ss.abhijit_muhurta.ends_at.strftime('%I:%M %p')}")
    print(f"  ✓ Amrit Kalam: {len(ss.amrit_kalam)} period(s)")

    # Features NOT available in SS mode (should be empty, not None)
    assert ss.lagna_table == [], "Lagna table should be empty in SS mode"
    assert ss.maudhya == [], "Maudhya should be empty in SS mode"
    assert ss.eclipses == [], "Eclipses should be empty in SS mode"
    print("  ✓ Lagna/Maudhya/Eclipses correctly empty in SS mode")

    print("  ✓ All SS features verified!\n")


def test_ss_standalone_output():
    """Show the full SS panchanga output standalone (method='siddhantic')."""
    print("=== SS Standalone Output (method='siddhantic') ===\n")

    p = Panchanga(
        year=1998, month=12, day=3,
        latitude=16.5062, longitude=80.648,
        timezone="Asia/Kolkata", city="Vijayawada",
        method="siddhantic",
    )
    result = p.compute()

    assert result.computation_method == "Surya Siddhanta"
    print(result.summary())
    print()


def test_both_mode_output():
    """Show the full panchanga output with both Drik and SS."""
    print("=== Full Output (method='both') ===\n")

    p = Panchanga(
        year=1998, month=12, day=3,
        latitude=16.5062, longitude=80.648,
        timezone="Asia/Kolkata", city="Vijayawada",
        method="both",
    )
    result = p.compute()

    # Primary should be Drik
    assert result.computation_method == "Drik Ganita"
    # SS comparison should be attached
    assert result.ss_panchanga is not None
    assert result.ss_panchanga.computation_method == "Surya Siddhanta"

    # Both should have full features
    assert len(result.tithi) > 0
    assert len(result.ss_panchanga.tithi) > 0
    assert len(result.hora_table) == 24
    assert len(result.ss_panchanga.hora_table) == 24

    # Drik should have lagna, SS should not
    assert len(result.lagna_table) > 0, "Drik should have lagna table"
    assert result.ss_panchanga.lagna_table == [], "SS should NOT have lagna table"

    print(result.summary())
    print()


def test_benchmark_dec3_1998():
    """Compare SS panchanga with Drik for Dec 3, 1998, Vijayawada."""
    print("=== Benchmark: Dec 3, 1998 (Vijayawada) ===")

    p = Panchanga(
        year=1998, month=12, day=3,
        latitude=16.5062, longitude=80.648,
        timezone="Asia/Kolkata", city="Vijayawada",
        method="both",
    )
    result = p.compute()
    ss = result.ss_panchanga

    drik_tithi = result.tithi[0].name
    ss_tithi = ss.tithi[0].name
    drik_nak = result.nakshatra[0].name
    ss_nak = ss.nakshatra[0].name

    print(f"  Drik Tithi    : {drik_tithi}")
    print(f"  SS   Tithi    : {ss_tithi}")
    print(f"  Drik Nakshatra: {drik_nak}")
    print(f"  SS   Nakshatra: {ss_nak}")
    print(f"  Drik Sunrise  : {result.sun.sunrise.strftime('%I:%M:%S %p')}")
    print(f"  SS   Sunrise  : {ss.sun.sunrise.strftime('%I:%M:%S %p')}")

    # Sun/Moon longitude comparison
    drik_sun = result.sun_rashi.index * 30 + result.sun_rashi.degree
    ss_sun = ss.sun_rashi.index * 30 + ss.sun_rashi.degree
    drik_moon = result.moon_rashi.index * 30 + result.moon_rashi.degree
    ss_moon = ss.moon_rashi.index * 30 + ss.moon_rashi.degree
    print(f"  Sun  Δ : {(ss_sun - drik_sun):+.3f}°")
    print(f"  Moon Δ : {(ss_moon - drik_moon):+.3f}°")

    # Sun difference should be small (< 3°)
    assert abs(ss_sun - drik_sun) < 5.0, f"Sun Δ too large"
    # Moon difference can be larger but bounded
    assert abs(ss_moon - drik_moon) < 10.0, f"Moon Δ too large"

    print("  ✓ Benchmark test passed\n")


def test_multiple_dates_comparison():
    """Test SS vs Drik across multiple dates and check tolerance."""
    print("=== Multi-Date Comparison ===")
    print(f"  {'Date':<16s} {'Drik Tithi':<24s} {'SS Tithi':<24s} {'Sun Δ':>8s} {'Moon Δ':>8s} {'Match'}")
    print("  " + "-" * 90)

    test_dates = [
        (1998, 12, 3),
        (2025, 2, 18),
        (2000, 1, 14),
        (1947, 8, 15),
        (1985, 6, 21),
        (2024, 4, 9),
        (1975, 11, 1),
        (2010, 3, 30),
    ]

    tithi_matches = 0
    for y, m, d in test_dates:
        p = Panchanga(
            year=y, month=m, day=d,
            latitude=16.5062, longitude=80.648,
            timezone="Asia/Kolkata", city="Vijayawada",
            method="both",
        )
        result = p.compute()
        ss = result.ss_panchanga

        drik_tithi = result.tithi[0].name
        ss_tithi = ss.tithi[0].name

        drik_sun = result.sun_rashi.index * 30 + result.sun_rashi.degree
        ss_sun = ss.sun_rashi.index * 30 + ss.sun_rashi.degree
        drik_moon = result.moon_rashi.index * 30 + result.moon_rashi.degree
        ss_moon = ss.moon_rashi.index * 30 + ss.moon_rashi.degree

        match = "✓" if result.tithi[0].index == ss.tithi[0].index else "~"
        if match == "✓":
            tithi_matches += 1

        print(f"  {y}-{m:02d}-{d:02d}       {drik_tithi:<24s} {ss_tithi:<24s} {(ss_sun - drik_sun):>+7.3f}° {(ss_moon - drik_moon):>+7.3f}° {match}")

    match_pct = tithi_matches / len(test_dates) * 100
    print(f"\n  Tithi match rate: {tithi_matches}/{len(test_dates)} ({match_pct:.0f}%)")
    assert tithi_matches >= len(test_dates) // 2, "Tithi match rate too low"
    print("  ✓ Multi-date comparison passed\n")


if __name__ == "__main__":
    test_ahargana()
    test_daily_motions()
    test_mean_longitudes()
    test_manda_samskara_bounds()
    test_true_longitudes_range()
    test_ss_sunrise()
    test_ss_sunrise_vs_drik()
    test_full_ss_panchanga_features()
    test_ss_standalone_output()
    test_both_mode_output()
    test_benchmark_dec3_1998()
    test_multiple_dates_comparison()

    print("═══ All Surya Siddhanta tests passed ═══")
