"""Proaktivní notifikace "auto doma + nepíchnuté + levné/přebytek → píchni ho".

Tenký orchestrátor: veškerá doménová logika (kdo je doma, odpojený, výhodná cena,
SoC pod cílem) i dedup jsou v ems.fn_ev_presence_nudge_due(). Python jen zavolá
funkci pro každou aktivní lokalitu a pro každý vrácený (= nově due, ještě
neposlaný) řádek pošle jeden Discord nudge.

Dedup je čistě v DB: funkce zapíše řádek do ems.ev_presence_nudge_sent
(on conflict do nothing) a vrátí jen ty, kterým insert skutečně prošel — tedy
jeden nudge na "epizodu" auta doma+odpojeno. Opakované 20–30min ticky proto
nespamují, dokud se auto nepíchne nebo neodjede (čímž se klíč epizody změní).

DEFAULT-OFF: funkce nevrátí nic, dokud není na vozidle
asset_vehicle.presence_nudge_enabled = true. Job tedy běží inertně.
"""

from __future__ import annotations

import logging
from typing import Any

import asyncpg

from app.db_json import fetch_json
from services.notification_service import send_discord

logger = logging.getLogger(__name__)


def _fmt_price(value: Any) -> str:
    try:
        return f"{float(value):.2f}"
    except (TypeError, ValueError):
        return "?"


def _build_message(row: asyncpg.Record) -> str:
    name = row["vehicle_name"] or "EV"
    reason = str(row["trigger_reason"] or "")
    sell = row["effective_sell_price_czk_kwh"]
    buy = row["effective_buy_price_czk_kwh"]
    soc = row["battery_level_pct"]
    tgt = row["target_soc_pct"]

    if reason == "NEG_OR_ZERO_SELL":
        why = f"výkup je teď {_fmt_price(sell)} Kč/kWh (≤ 0) — přebytek se hodí do auta"
    else:
        why = f"nákup je teď levný: {_fmt_price(buy)} Kč/kWh"

    soc_line = ""
    if soc is not None:
        soc_line = f"\nBaterie auta: **{_fmt_price(soc)} %**" + (
            f" (cíl {_fmt_price(tgt)} %)" if tgt is not None else ""
        )

    return (
        f"🚗 **{name} je doma a nepíchnuté** — {why}.{soc_line}\n"
        f"Píchni ho a plán se o zbytek postará (přebytky / levné sloty)."
    )


async def run_ev_presence_nudge_for_site(
    site_id: int, conn: asyncpg.Connection
) -> int:
    """Jedna lokalita: zavolá fn (dedup v DB) a pošle Discord pro každé due vozidlo.

    Vrátí počet odeslaných notifikací.
    """
    try:
        rows = await conn.fetch(
            "select * from ems.fn_ev_presence_nudge_due($1::int)",
            site_id,
        )
    except Exception:
        logger.exception(
            "ev_presence_nudge: fn_ev_presence_nudge_due failed site=%s", site_id
        )
        return 0

    sent = 0
    for row in rows:
        try:
            await send_discord(conn, site_id, _build_message(row), level="info")
            sent += 1
            logger.info(
                "ev_presence_nudge sent site=%s vehicle=%s reason=%s",
                site_id,
                row["vehicle_id"],
                row["trigger_reason"],
            )
        except Exception:
            logger.exception(
                "ev_presence_nudge: Discord send failed site=%s vehicle=%s",
                site_id,
                row["vehicle_id"],
            )
    return sent


async def run_ev_presence_nudge_for_all_active_sites(pool: asyncpg.Pool) -> None:
    """Scheduler entrypoint: projde aktivní lokality a pošle proaktivní nudge."""
    async with pool.acquire() as conn:
        raw = await fetch_json(conn, "select ems.fn_vw_site_directory_active()")
        sites = raw if isinstance(raw, list) else []
        for site in sites:
            if not isinstance(site, dict) or site.get("id") is None:
                continue
            site_id = int(site["id"])
            try:
                await run_ev_presence_nudge_for_site(site_id, conn)
            except Exception:
                logger.exception("ev_presence_nudge site=%s failed", site_id)
