"""REST API – denní ekonomické vyhodnocení provozu."""

from __future__ import annotations

import json
import logging
from datetime import date, datetime
from typing import Annotated, Any

import asyncpg
from fastapi import APIRouter, Depends, HTTPException, Query
from pydantic import BaseModel

from app.db_json import fetch_json
from app.deps import get_pg_pool

router = APIRouter(
    prefix="/sites/{site_id}/economics",
    tags=["economics"],
)

logger = logging.getLogger(__name__)


class DailyEconomics(BaseModel):
    day: date
    interval_count: int
    import_kwh: float
    export_kwh: float
    pv_kwh: float
    load_kwh: float
    pv_self_consumption_kwh: float
    ev_kwh: float
    hp_kwh: float
    import_cost_czk: float
    export_revenue_czk: float
    grid_import_cashflow_czk: float
    grid_export_revenue_czk: float
    net_cost_czk: float
    green_bonus_czk: float
    total_balance_czk: float
    planned_balance_czk: float | None
    deviation_cost_czk: float | None
    is_locked: bool


class DailyEconomicsResponse(BaseModel):
    days: list[DailyEconomics]
    has_green_bonus: bool


class IntervalEconomics(BaseModel):
    interval_start: str
    import_kwh: float
    export_kwh: float
    dynamic_cost_czk: float | None
    grid_import_cashflow_czk: float | None
    grid_export_revenue_czk: float | None
    stored_cost_czk: float | None
    green_bonus_czk: float | None
    planned_cost_czk: float | None
    planned_grid_w: int | None
    actual_grid_power_w: int | None
    effective_buy_price: float | None
    effective_sell_price: float | None
    planned_buy_price: float | None
    planned_sell_price: float | None
    actual_pv_power_w: int | None
    actual_load_power_w: int | None
    actual_battery_power_w: int | None
    actual_battery_soc_pct: float | None


class ChartDayPoint(BaseModel):
    day: date
    daily_balance_czk: float
    daily_grid_balance_czk: float
    daily_green_bonus_czk: float
    daily_import_cost_czk: float
    daily_export_revenue_czk: float
    cumulative_balance_czk: float
    cumulative_grid_balance_czk: float


class LockResponse(BaseModel):
    locked: bool
    day: date


def _num(val: Any) -> float:
    if val is None:
        return 0.0
    return float(val)


def _opt(val: Any) -> float | None:
    if val is None:
        return None
    return float(val)


async def _check_site(conn: asyncpg.Connection, site_id: int) -> None:
    ok = await conn.fetchval(
        "SELECT EXISTS(SELECT 1 FROM ems.site WHERE id = $1)", site_id
    )
    if not ok:
        raise HTTPException(status_code=404, detail="Site not found")


def _parse_day(val: Any) -> date:
    if isinstance(val, datetime):
        return val.date()
    if isinstance(val, date):
        return val
    if isinstance(val, str):
        return date.fromisoformat(val[:10])
    raise ValueError(val)


@router.get("/daily", response_model=DailyEconomicsResponse)
async def get_economics_daily(
    site_id: int,
    db: Annotated[asyncpg.Pool, Depends(get_pg_pool)],
    month: str = Query(
        ...,
        description="YYYY-MM (měsíc pro zobrazení)",
        pattern=r"^\d{4}-\d{2}$",
    ),
) -> DailyEconomicsResponse:
    try:
        year, mon = month.split("-")
        month_start = date(int(year), int(mon), 1)
        if int(mon) == 12:
            month_end = date(int(year) + 1, 1, 1)
        else:
            month_end = date(int(year), int(mon) + 1, 1)
    except (ValueError, IndexError):
        raise HTTPException(status_code=400, detail="Invalid month, expected YYYY-MM")

    async with db.acquire() as conn:
        await _check_site(conn, site_id)
        raw = await fetch_json(
            conn,
            "select ems.fn_economics_daily_month($1::int, $2::date, $3::date)",
            site_id,
            month_start,
            month_end,
        )
    if not isinstance(raw, dict):
        raw = json.loads(raw)
    days_in: list[Any] = list(raw.get("days") or [])
    days: list[DailyEconomics] = []
    for d in days_in:
        if not isinstance(d, dict):
            continue
        days.append(
            DailyEconomics(
                day=_parse_day(d.get("day")),
                interval_count=int(d.get("interval_count") or 0),
                import_kwh=_num(d.get("import_kwh")),
                export_kwh=_num(d.get("export_kwh")),
                pv_kwh=_num(d.get("pv_kwh")),
                load_kwh=_num(d.get("load_kwh")),
                pv_self_consumption_kwh=_num(d.get("pv_self_consumption_kwh")),
                ev_kwh=_num(d.get("ev_kwh")),
                hp_kwh=_num(d.get("hp_kwh")),
                import_cost_czk=_num(d.get("import_cost_czk")),
                export_revenue_czk=_num(d.get("export_revenue_czk")),
                grid_import_cashflow_czk=_num(d.get("grid_import_cashflow_czk")),
                grid_export_revenue_czk=_num(d.get("grid_export_revenue_czk")),
                net_cost_czk=_num(d.get("net_cost_czk")),
                green_bonus_czk=_num(d.get("green_bonus_czk")),
                total_balance_czk=_num(d.get("total_balance_czk")),
                planned_balance_czk=_opt(d.get("planned_balance_czk")),
                deviation_cost_czk=_opt(d.get("deviation_cost_czk")),
                is_locked=bool(d.get("is_locked")),
            )
        )
    return DailyEconomicsResponse(
        days=days,
        has_green_bonus=bool(raw.get("has_green_bonus")),
    )


@router.get("/daily/{day}/intervals", response_model=list[IntervalEconomics])
async def get_economics_intervals(
    site_id: int,
    day: date,
    db: Annotated[asyncpg.Pool, Depends(get_pg_pool)],
) -> list[IntervalEconomics]:
    async with db.acquire() as conn:
        await _check_site(conn, site_id)
        rows = await conn.fetch(
            """
            SELECT *
            FROM ems.vw_economics_interval
            WHERE site_id = $1
              AND date_trunc('day', interval_start AT TIME ZONE 'Europe/Prague')::date = $2
            ORDER BY interval_start
            """,
            site_id,
            day,
        )

    return [
        IntervalEconomics(
            interval_start=r["interval_start"].isoformat(),
            import_kwh=_num(r["import_kwh"]),
            export_kwh=_num(r["export_kwh"]),
            dynamic_cost_czk=_opt(r["dynamic_cost_czk"]),
            grid_import_cashflow_czk=_opt(r["grid_import_cashflow_czk"]),
            grid_export_revenue_czk=_opt(r["grid_export_revenue_czk"]),
            stored_cost_czk=_opt(r["stored_cost_czk"]),
            green_bonus_czk=_opt(r["green_bonus_czk"]),
            planned_cost_czk=_opt(r["planned_cost_czk"]),
            planned_grid_w=int(r["planned_grid_w"]) if r["planned_grid_w"] is not None else None,
            actual_grid_power_w=int(r["actual_grid_power_w"]) if r["actual_grid_power_w"] is not None else None,
            effective_buy_price=_opt(r["effective_buy_price_czk_kwh"]),
            effective_sell_price=_opt(r["effective_sell_price_czk_kwh"]),
            planned_buy_price=_opt(r["planned_buy_price"]),
            planned_sell_price=_opt(r["planned_sell_price"]),
            actual_pv_power_w=int(r["actual_pv_power_w"]) if r["actual_pv_power_w"] is not None else None,
            actual_load_power_w=int(r["actual_load_power_w"]) if r["actual_load_power_w"] is not None else None,
            actual_battery_power_w=int(r["actual_battery_power_w"]) if r["actual_battery_power_w"] is not None else None,
            actual_battery_soc_pct=_opt(r["actual_battery_soc_pct"]),
        )
        for r in rows
    ]


@router.post("/daily/{day}/lock", response_model=LockResponse)
async def lock_day(
    site_id: int,
    day: date,
    db: Annotated[asyncpg.Pool, Depends(get_pg_pool)],
) -> LockResponse:
    async with db.acquire() as conn:
        await _check_site(conn, site_id)
        raw = await fetch_json(
            conn,
            "select ems.fn_economics_lock_day($1::int, $2::date)",
            site_id,
            day,
        )
    if not isinstance(raw, dict):
        raw = json.loads(raw)
    if raw.get("locked") is not True:
        raise HTTPException(
            status_code=404,
            detail=f"No economics data for {day.isoformat()}",
        )

    return LockResponse(locked=True, day=day)


@router.delete("/daily/{day}/lock", response_model=LockResponse)
async def unlock_day(
    site_id: int,
    day: date,
    db: Annotated[asyncpg.Pool, Depends(get_pg_pool)],
) -> LockResponse:
    async with db.acquire() as conn:
        await _check_site(conn, site_id)
        await fetch_json(
            conn,
            "select ems.fn_economics_unlock_day($1::int, $2::date)",
            site_id,
            day,
        )
    return LockResponse(locked=False, day=day)


@router.get("/monthly-chart", response_model=list[ChartDayPoint])
async def get_monthly_chart(
    site_id: int,
    db: Annotated[asyncpg.Pool, Depends(get_pg_pool)],
    month: str = Query(
        ...,
        description="YYYY-MM",
        pattern=r"^\d{4}-\d{2}$",
    ),
) -> list[ChartDayPoint]:
    try:
        year, mon = month.split("-")
        month_start = date(int(year), int(mon), 1)
        if int(mon) == 12:
            month_end = date(int(year) + 1, 1, 1)
        else:
            month_end = date(int(year), int(mon) + 1, 1)
    except (ValueError, IndexError):
        raise HTTPException(status_code=400, detail="Invalid month, expected YYYY-MM")

    async with db.acquire() as conn:
        await _check_site(conn, site_id)
        arr = await fetch_json(
            conn,
            "select ems.fn_economics_monthly_chart($1::int, $2::date, $3::date)",
            site_id,
            month_start,
            month_end,
        )
    if not isinstance(arr, list):
        arr = json.loads(arr) if isinstance(arr, str) else []
    points: list[ChartDayPoint] = []
    for r in arr:
        if not isinstance(r, dict):
            continue
        points.append(
            ChartDayPoint(
                day=_parse_day(r.get("day")),
                daily_balance_czk=float(r.get("daily_balance_czk") or 0),
                daily_grid_balance_czk=float(r.get("daily_grid_balance_czk") or 0),
                daily_green_bonus_czk=float(r.get("daily_green_bonus_czk") or 0),
                daily_import_cost_czk=float(r.get("daily_import_cost_czk") or 0),
                daily_export_revenue_czk=float(r.get("daily_export_revenue_czk") or 0),
                cumulative_balance_czk=float(r.get("cumulative_balance_czk") or 0),
                cumulative_grid_balance_czk=float(r.get("cumulative_grid_balance_czk") or 0),
            )
        )
    return points
