import os
import glob
import uuid
import importlib.util
from typing import Any, Dict
from dotenv import load_dotenv

from fastapi import APIRouter, FastAPI, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.staticfiles import StaticFiles
from pydantic import BaseModel



# from prokerala_router import router as prokerala_router
prokerala_router = None
from saturn_return_chart import router as saturn_return_router
from composite_chart_router import router as composite_chart_router

from chart_patterns import detect_chart_patterns
from astrology import calculate_planets
from birthday_chart import generate_birthday_chart
from Natal_Planet_Aspects import calculate_aspects
from upcoming_aspects import calculate_upcoming_aspects

from lunar_return_calculation import calculate_lunar_return
from solar_return_calculation import calculate_solar_return

from progressed_chart import router as progressed_chart_router


from transit_chart_generate import generate_transit_chart_svg

from auth_routes import router as auth_router
from user_personas import router as user_personas_router
from user_settings_routes import router as user_settings_router
from user_horoscope_router import router as user_horoscope_router
from planetary_events_router import router as planetary_events_router





# from chart_storage import router as chart_storage_router


# =====================================================
# FastAPI App
# =====================================================

app = FastAPI(title="Astrology API")
router = APIRouter()
app.include_router(auth_router)
app.include_router(user_personas_router)
app.include_router(user_settings_router)
app.include_router(user_horoscope_router)
app.include_router(planetary_events_router) 








# app.include_router(chart_storage_router)

# =====================================================
# Paths
# =====================================================

BASE_DIR = os.path.dirname(os.path.abspath(__file__))

CHARTS_DIR = os.path.join(BASE_DIR, "charts")
BIRTHCHART_DIR = os.path.join(CHARTS_DIR, "birthchart")
SYNASTRY_CHART_DIR = os.path.join(CHARTS_DIR, "synastry-chart")
COMPOSITE_CHART_DIR = os.path.join(CHARTS_DIR, "composite-chart")
SOLAR_RETURN_DIR = os.path.join(CHARTS_DIR, "solar-return")
LUNAR_RETURN_DIR = os.path.join(CHARTS_DIR, "lunar-return")

os.makedirs(CHARTS_DIR, exist_ok=True)
os.makedirs(BIRTHCHART_DIR, exist_ok=True)
os.makedirs(SYNASTRY_CHART_DIR, exist_ok=True)
os.makedirs(COMPOSITE_CHART_DIR, exist_ok=True)
os.makedirs(SOLAR_RETURN_DIR, exist_ok=True)
os.makedirs(LUNAR_RETURN_DIR, exist_ok=True)


# =====================================================
# Static Files
# =====================================================

app.mount("/charts", StaticFiles(directory=CHARTS_DIR), name="charts")

app.include_router(progressed_chart_router)
# =====================================================
# CORS
# =====================================================
load_dotenv()

FRONTEND_URL = os.getenv("FRONTEND_URL")

if not FRONTEND_URL:
    raise ValueError("FRONTEND_URL is missing in backend .env")

app.add_middleware(
    CORSMiddleware,
     allow_origins=[
        FRONTEND_URL,
     
    ],
    allow_credentials=True,
    allow_methods=["*"],
    allow_headers=["*"],
)


# =====================================================
# Constants / Memory Store
# =====================================================

DEFAULT_TIMEZONE = 5.5

LUNAR_RETURN_RESULTS: Dict[str, Dict[str, Any]] = {}
SOLAR_RETURN_RESULTS: Dict[str, Dict[str, Any]] = {}


# =====================================================
# Load synastry-chart.py
# Because file name has hyphen, normal import will not work
# =====================================================

def load_synastry_module():
    file_path = os.path.join(BASE_DIR, "synastry-chart.py")

    if not os.path.exists(file_path):
        raise FileNotFoundError(f"synastry-chart.py not found at: {file_path}")

    spec = importlib.util.spec_from_file_location(
        "synastry_chart_module",
        file_path
    )

    if spec is None or spec.loader is None:
        raise ImportError("Could not load synastry-chart.py")

    module = importlib.util.module_from_spec(spec)
    spec.loader.exec_module(module)

    return module


synastry_module = load_synastry_module()

SynastryChartInput = synastry_module.SynastryChartInput
generate_synastry_chart_svg = synastry_module.generate_synastry_chart_svg


# =====================================================
# Request Models
# =====================================================

class BirthDetails(BaseModel):
    name: str
    year: int
    month: int
    day: int
    hour: int
    minute: int
    second: int = 0
    latitude: float
    longitude: float
    timezone: float = DEFAULT_TIMEZONE

class ProgressedDetails(BaseModel):
    year: int
    month: int
    day: int
    hour: int
    minute: int
    second: int = 0
    latitude: float
    longitude: float
    timezone: float = DEFAULT_TIMEZONE

class ChartInput(BaseModel):
    year: int
    month: int
    day: int
    hour: int
    minute: int
    second: int = 0
    latitude: float
    longitude: float
    timezone: float = DEFAULT_TIMEZONE


class SynastryAspectInput(BaseModel):
    user_planets: list
    partner_planets: list


class SynastryPatternInput(BaseModel):
    user_planets: list
    partner_planets: list


class LunarReturnRequest(BaseModel):
    name: str = "User"

    # Birth details
    year: int
    month: int
    day: int
    hour: int
    minute: int
    second: int = 0

    latitude: float
    longitude: float
    timezone: float = DEFAULT_TIMEZONE
    birth_place: str = ""

    # Lunar return target
    return_year: int
    return_month: int

    return_latitude: float
    return_longitude: float
    return_timezone: float = DEFAULT_TIMEZONE
    return_place: str = ""


class SolarReturnRequest(BaseModel):
    name: str = "User"

    # Birth details
    year: int
    month: int
    day: int
    hour: int
    minute: int
    second: int = 0

    latitude: float
    longitude: float
    timezone: float = DEFAULT_TIMEZONE
    birth_place: str = ""

    # Solar return target
    return_year: int

    return_latitude: float
    return_longitude: float
    return_timezone: float = DEFAULT_TIMEZONE
    return_place: str = ""


# =====================================================
# Optional Legacy Chart Helpers
# These are only for old frontend calls:
# /solar-return-chart/generate
# /lunar-return-chart/generate
#
# Correct order:
# first_obj  = natal_subject      inner wheel
# second_obj = return_subject     outer wheel
# =====================================================

def generate_solar_return_chart(
    birth_year,
    birth_month,
    birth_day,
    birth_hour,
    birth_minute,
    return_year,
    return_month,
    return_day,
    return_hour,
    return_minute,
    latitude,
    longitude,
    name="User",
):
    safe_name = "".join(
        c for c in name if c.isalnum() or c in (" ", "-", "_")
    ).strip() or "User"

    charts_dir = os.path.join(CHARTS_DIR, "solar-return")
    os.makedirs(charts_dir, exist_ok=True)

    natal_subject = AstrologicalSubject(
        name=f"{safe_name} Natal",
        year=birth_year,
        month=birth_month,
        day=birth_day,
        hour=birth_hour,
        minute=birth_minute,
        lat=latitude,
        lng=longitude,
        tz_str="Asia/Kolkata",
    )

    return_subject = AstrologicalSubject(
        name=f"{safe_name} Solar Return {return_year}",
        year=return_year,
        month=return_month,
        day=return_day,
        hour=return_hour,
        minute=return_minute,
        lat=latitude,
        lng=longitude,
        tz_str="Asia/Kolkata",
    )

    # IMPORTANT FIX:
    # first_obj = Natal
    # second_obj = Solar Return
    chart = KerykeionChartSVG(
        first_obj=natal_subject,
        chart_type="Transit",
        second_obj=return_subject,
        new_output_directory=charts_dir,
    )

    chart.makeSVG()

    svg_files = glob.glob(os.path.join(charts_dir, "*.svg"))
    if svg_files:
        return max(svg_files, key=os.path.getmtime)

    return os.path.join(charts_dir, f"{safe_name}_Solar_Return.svg")



# =====================================================
# Root
# =====================================================

@app.get("/")
def root():
    return {
        "message": "Astrology API is running",
        "docs": "/docs",
        "birth_chart_api": "/calculators/birth-chart",
        "chart_data_api": "/chart-data",
        "synastry_chart_api": "/synastry-chart",
        "synastry_aspects_api": "/synastry-aspects",
        "composite_chart_api": "/calculators/composite-chart",
        "lunar_return_api": "/calculators/lunar-return",
        "solar_return_api": "/calculators/solar-return",
        "charts_static_url": "/charts",
    }


# =====================================================
# Synastry Chart API
# =====================================================

@app.post("/synastry-chart")
def create_synastry_chart(data: SynastryChartInput):
    try:
        return generate_synastry_chart_svg(data, CHARTS_DIR)

    except Exception as e:
        return JSONResponse(
            {"error": str(e)},
            status_code=500,
        )


# =====================================================
# Lunar Return API
# Main API.
# This should now return chart_url from calculate_lunar_return().
# =====================================================

@router.post("/calculators/lunar-return")
def lunar_return_chart(req: LunarReturnRequest):
    try:
        result = calculate_lunar_return(req)

        result_id = str(uuid.uuid4().int)[:7]
        result["result_id"] = result_id

        LUNAR_RETURN_RESULTS[result_id] = result

        return {
            "success": True,
            "result_id": result_id,
            "redirect_url": f"/calculators/lunar-return-chart/{result_id}",
            "data": result,
        }

    except Exception as e:
        return JSONResponse(
            {
                "success": False,
                "error": str(e),
            },
            status_code=500,
        )

@router.get("/calculators/lunar-return-chart/{result_id}")
def read_lunar_return_chart(result_id: str):
    result = LUNAR_RETURN_RESULTS.get(result_id)

    if not result:
        raise HTTPException(
            status_code=404,
            detail="Lunar return result not found.",
        )

    return result


# =====================================================
# Solar Return API
# Main API.
# This should return chart_url from calculate_solar_return().
# =====================================================

@router.post("/calculators/solar-return")
def solar_return_chart(req: SolarReturnRequest):
    try:
        result = calculate_solar_return(req)

        result_id = str(uuid.uuid4().int)[:7]
        result["result_id"] = result_id

        SOLAR_RETURN_RESULTS[result_id] = result

        return {
            "success": True,
            "result_id": result_id,
            "redirect_url": f"/calculators/solar-return-chart/{result_id}",
            "data": result,
        }

    except Exception as e:
        return JSONResponse(
            {
                "success": False,
                "error": str(e),
            },
            status_code=500,
        )


@router.get("/calculators/solar-return-chart/{result_id}")
def read_solar_return_chart(result_id: str):
    result = SOLAR_RETURN_RESULTS.get(result_id)

    if not result:
        raise HTTPException(
            status_code=404,
            detail="Solar return result not found.",
        )

    return result


# =====================================================
# Legacy Solar Return Chart API
# Use only if your old frontend still calls this.
# Better frontend should use /calculators/solar-return.
# =====================================================

@router.post("/solar-return-chart/generate")
def create_solar_return_chart(data: dict):
    try:
        chart_path = generate_solar_return_chart(
            data.get("birth_year"),
            data.get("birth_month"),
            data.get("birth_day"),
            data.get("birth_hour"),
            data.get("birth_minute"),
            data.get("return_year"),
            data.get("return_month"),
            data.get("return_day"),
            data.get("return_hour"),
            data.get("return_minute"),
            data.get("latitude"),
            data.get("longitude"),
            data.get("name", "User"),
        )

        filename = os.path.basename(chart_path)

        return {
            "chart": f"/charts/solar-return/{filename}",
        }

    except Exception as e:
        return JSONResponse(
            {"error": str(e)},
            status_code=500,
        )





# =====================================================
# Birth Chart API
# =====================================================

@app.post("/calculators/birth-chart")
def create_chart(data: BirthDetails):
    try:
        result = generate_birthday_chart(
            data.name,
            data.year,
            data.month,
            data.day,
            data.hour,
            data.minute,
            data.second,
            data.latitude,
            data.longitude,
            data.timezone,
        )

        planets = result.get("planets", {})

        cleaned_planets = [
            {
                "planet": name.replace("_", " ").title(),
                "sign": point.get("sign"),
                "degree": point.get("degree"),
                "house": point.get("house"),
                "longitude": point.get("longitude"),
            }
            for name, point in planets.items()
        ]

        aspect_data = calculate_aspects(cleaned_planets)
        upcoming_aspects = calculate_upcoming_aspects(planets, days=14)
        chart_patterns = detect_chart_patterns(planets)

        return {
            "chart": result.get("chart"),
            "ascendant": result.get("ascendant"),
            "ascendant_sign": result.get("ascendant_sign"),
            "midheaven": result.get("midheaven"),
            "imum_coeli": result.get("imum_coeli"),
            "descendant": result.get("descendant"),
            "vertex": result.get("vertex"),
            "planets": cleaned_planets,
            "moon_phase": result.get("moon_phase"),
            "major_aspects": aspect_data["major_aspects"],
            "minor_aspects": aspect_data["minor_aspects"],
            "aspects": aspect_data["aspects"],
            "upcoming_aspects": upcoming_aspects,
            "chart_patterns": chart_patterns,
        }

    except Exception as e:
        return JSONResponse(
            {"error": str(e)},
            status_code=500,
        )


# =====================================================
# Progressed Chart API
# =====================================================

@app.post("/calculators/progressed-chart")
def create_progressed_chart(data: ProgressedDetails):
    try:
        result = generate_progressed_chart(
            data.year,
            data.month,
            data.day,
            data.hour,
            data.minute,
            data.second,
            data.latitude,
            data.longitude,
            data.timezone,
        )

        return result

    except Exception as e:
        return JSONResponse(
            {"success": False, "error": str(e)},
            status_code=500,
        )

# =====================================================
# Chart Data API
# =====================================================

@app.post("/chart-data")
def chart_data(data: ChartInput):
    try:
        result = calculate_planets(
            data.year,
            data.month,
            data.day,
            data.hour,
            data.minute,
            data.second,
            data.latitude,
            data.longitude,
            tz=data.timezone,
        )

        planets = result.get("planets", {})

        cleaned = [
            {
                "id": planet_name,
                "planet": planet_name.replace("_", " ").title(),
                "sign": planet_data.get("sign"),
                "degree": planet_data.get("degree"),
                "house": planet_data.get("house"),
                "retrograde": planet_data.get("retrograde", False),
                "longitude": planet_data.get("longitude"),
            }
            for planet_name, planet_data in planets.items()
        ]

        return {
            "planets": cleaned,
            "moon_phase": result.get("moon_phase"),
        }

    except Exception as e:
        return JSONResponse(
            {"error": f"Calculation failed: {str(e)}"},
            status_code=500,
        )


# =====================================================
# Synastry Aspects API
# =====================================================

@app.post("/synastry-aspects")
def synastry_aspects(data: SynastryAspectInput):
    try:
        from Natal_Planet_Aspects import calculate_synastry_aspects

        aspects = calculate_synastry_aspects(
            data.user_planets,
            data.partner_planets,
        )

        return aspects

    except Exception as e:
        return JSONResponse(
            {"error": str(e)},
            status_code=500,
        )


# =====================================================
# Synastry Chart Patterns API
# =====================================================

@app.post("/synastry-patterns")
def synastry_patterns(data: SynastryPatternInput):
    try:
        from synastry_patterns import detect_synastry_chart_patterns

        return detect_synastry_chart_patterns(
            data.user_planets,
            data.partner_planets,
        )

    except Exception as e:
        return JSONResponse(
            {"error": str(e)},
            status_code=500,
        )


# =====================================================
# Moon Phase Calculator API
# =====================================================

@app.post("/calculator/moon-phase")
def moon_phase_calculator(data: ChartInput):
    try:
        result = calculate_planets(
            data.year,
            data.month,
            data.day,
            data.hour,
            data.minute,
            data.second,
            data.latitude,
            data.longitude,
            tz=data.timezone,
        )

        moon = result.get("planets", {}).get("moon")
        moon_phase = result.get("moon_phase")

        if not moon:
            return JSONResponse(
                {"error": "Moon data not found"},
                status_code=404,
            )

        return {
            "planet": "Moon",
            "sign": moon.get("sign"),
            "degree": moon.get("degree"),
            "house": moon.get("house"),
            "moon_phase": moon_phase,
        }

    except Exception as e:
        return JSONResponse(
            {"error": f"Moon phase calculation failed: {str(e)}"},
            status_code=500,
        )


# =====================================================
# Single Planet / Point Calculator API
# =====================================================

@app.post("/calculator/{planet}")
def calculator(planet: str, data: ChartInput):
    try:
        result = calculate_planets(
            data.year,
            data.month,
            data.day,
            data.hour,
            data.minute,
            data.second,
            data.latitude,
            data.longitude,
            tz=data.timezone,
        )

        planet_slug = planet.lower().replace("-", "").replace("_", "")

        aliases = {
            "northnode": "north_node",
            "southnode": "south_node",
            "blackmoonlilith": "black_moon_lilith",
            "partoffortune": "part_of_fortune",
            "fortune": "part_of_fortune",

            "midheaven": "midheaven",
            "mc": "midheaven",

            "whitemoonselena": "white_moon_selena",
            "whitemoonselenasign": "white_moon_selena",
            "selena": "white_moon_selena",

            "imumcoeli": "imum_coeli",
            "ic": "imum_coeli",

            "rising": "ascendant",
            "ascendant": "ascendant",

            "descendant": "descendant",
            "vertex": "vertex",
        }

        key = aliases.get(planet_slug, planet_slug)

        item_data = result.get("planets", {}).get(key)

        if not item_data:
            if key == "ascendant":
                item_data = result.get("ascendant")
            elif key == "midheaven":
                item_data = result.get("midheaven")
            elif key == "imum_coeli":
                item_data = result.get("imum_coeli")
            elif key == "descendant":
                item_data = result.get("descendant")
            elif key == "vertex":
                item_data = result.get("vertex")

        if not item_data:
            return JSONResponse(
                {"error": f"Invalid planet or point: {planet}"},
                status_code=400,
            )

        if isinstance(item_data, dict) and item_data.get("error"):
            return JSONResponse(
                {"error": item_data["error"]},
                status_code=400,
            )

        return {
            "planet": planet.replace("-", " ").replace("_", " ").title(),
            "sign": item_data.get("sign"),
            "degree": item_data.get("degree"),
            "house": item_data.get("house"),
        }

    except Exception as e:
        return JSONResponse(
            {"error": f"Calculation failed: {str(e)}"},
            status_code=500,
        )

class TransitChartRequest(BaseModel):
    name: str = "User"

    birth_year: int
    birth_month: int
    birth_day: int
    birth_hour: int
    birth_minute: int
    birth_latitude: float
    birth_longitude: float
    birth_timezone: str = "Asia/Kolkata"

    transit_year: int
    transit_month: int
    transit_day: int
    transit_hour: int = 12
    transit_minute: int = 0
    transit_latitude: float
    transit_longitude: float
    transit_timezone: str = "Asia/Kolkata"


@app.post("/calculators/transit-chart/generate")
def generate_transit_chart(req: TransitChartRequest):
    try:
        result = generate_transit_chart_svg(req)

        return {
            "status": "success",
            "chart_url": result["chart_url"],
            "filename": result["filename"],
            "message": "Transit chart generated successfully",
        }

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))




# =====================================================
# Include Routers
# =====================================================

app.include_router(router)

if saturn_return_router:
    app.include_router(saturn_return_router)
if composite_chart_router:
    app.include_router(composite_chart_router)


app.mount("/charts", StaticFiles(directory=CHARTS_DIR), name="charts")

