from __future__ import annotations

import logging
from datetime import datetime, timedelta
from typing import Any
from zoneinfo import ZoneInfo

import aiosqlite
import httpx

from server.db import utc_now_iso
from server.messages import pick
from server.settings import settings
from server.stats import compute_stats

logger = logging.getLogger(__name__)

MOTIVATION_SYSTEM = (
    "Du schreibst Tages-Motivationssprüche für 'ADHD live, laugh, toaster bath motivational'. "
    "Stil: dark humor, Sarkasmus, passiv-aggressiv, kreativ-motivierend — nie gemein, nie medizinisch. "
    "Genau ein Satz auf Deutsch, max 25 Wörter. Keine Anführungszeichen um den Spruch."
)

ROAST_SYSTEM = (
    "Du bist ein sarkastischer, dark-humor Medikamenten-Coach für jemanden mit ADHS. "
    "Kurz (max 2 Sätze), deutsch, witzig aber nicht gemein. Keine medizinischen Ratschläge. "
    "Keine Anführungszeichen um den Spruch. Wiederhole keine Formulierungen oder Ideen aus der Historie."
)

ROAST_HISTORY_DAYS = 14

async def _get_cache(db: aiosqlite.Connection, key: str) -> str | None:
    cur = await db.execute("SELECT content FROM ai_cache WHERE key = ?", (key,))
    row = await cur.fetchone()
    await cur.close()
    return row[0] if row else None


async def _set_cache(db: aiosqlite.Connection, key: str, content: str) -> None:
    await db.execute(
        "INSERT INTO ai_cache (key, content, created_at) VALUES (?, ?, ?) ON CONFLICT(key) DO UPDATE SET content=excluded.content, created_at=excluded.created_at",
        (key, content, utc_now_iso()),
    )
    await db.commit()


async def _call_openrouter(prompt: str, *, system: str) -> str | None:
    if not settings.openrouter_api_key:
        logger.warning("OPENROUTER_API_KEY not set")
        return None
    try:
        async with httpx.AsyncClient(timeout=30.0) as client:
            resp = await client.post(
                "https://openrouter.ai/api/v1/chat/completions",
                headers={
                    "Authorization": f"Bearer {settings.openrouter_api_key}",
                    "HTTP-Referer": "https://medis.schwenk.online",
                    "X-Title": "TakeYourMeds",
                },
                json={
                    "model": settings.openrouter_model,
                    "messages": [
                        {"role": "system", "content": system},
                        {"role": "user", "content": prompt},
                    ],
                    "max_tokens": 512,
                },
            )
            resp.raise_for_status()
            data = resp.json()
            if data.get("error"):
                logger.error("OpenRouter API error (model=%s): %s", settings.openrouter_model, data["error"])
                return None
            choices = data.get("choices") or []
            if not choices:
                logger.error("OpenRouter returned no choices (model=%s): %s", settings.openrouter_model, data)
                return None
            message = choices[0].get("message") or {}
            content = message.get("content")
            if not content or not str(content).strip():
                logger.error(
                    "OpenRouter empty content (model=%s, finish_reason=%s): %s",
                    settings.openrouter_model,
                    choices[0].get("finish_reason"),
                    data,
                )
                return None
            return str(content).strip()
    except Exception:
        logger.exception("OpenRouter request failed (model=%s)", settings.openrouter_model)
        return None


def _motivation_key(day: str) -> str:
    return f"motivation:v2:{day}"


def _roast_key(day: str) -> str:
    return f"roast:{day}"


async def _recent_roasts(
    db: aiosqlite.Connection,
    *,
    before_day: str,
    days: int = ROAST_HISTORY_DAYS,
) -> list[str]:
    end = datetime.strptime(before_day, "%Y-%m-%d").date()
    roasts: list[str] = []
    for offset in range(1, days + 1):
        day = (end - timedelta(days=offset)).isoformat()
        text = await _get_cache(db, _roast_key(day))
        if text:
            roasts.append(text)
    return roasts


async def _cached_motivation(db: aiosqlite.Connection, day: str) -> str | None:
    cached = await _get_cache(db, _motivation_key(day))
    if cached:
        return cached
    return await _get_cache(db, f"roast:{day}")


async def generate_daily_motivation(
    db: aiosqlite.Connection,
    tz: ZoneInfo,
    day: str | None = None,
    *,
    force: bool = False,
) -> dict[str, Any]:
    day = day or datetime.now(tz).strftime("%Y-%m-%d")
    key = _motivation_key(day)
    if not force:
        cached = await _cached_motivation(db, day)
        if cached:
            return {"text": cached, "source": "cache", "day": day}

    stats = await compute_stats(db)
    prompt = (
        "Schreibe den Motivationsspruch für heute. "
        f"Streak: {stats['streak']} Tage. "
        f"Compliance über {stats['compliance_window_days']} aktive Tage: {stats['compliance_percent']}%. "
        "Beispiel-Stil: Dein Gehirn ist nicht kaputt. Es läuft nur auf einem anderen Betriebssystem — ohne Support."
    )
    text = await _call_openrouter(prompt, system=MOTIVATION_SYSTEM)
    if not text:
        text = pick("motivation_fallback")
        source = "fallback"
    else:
        source = "ai"
    await _set_cache(db, key, text)
    logger.info("Generated daily motivation for %s (%s)", day, source)
    return {"text": text, "source": source, "day": day}


async def get_daily_motivation(db: aiosqlite.Connection, tz: ZoneInfo) -> dict[str, Any]:
    day = datetime.now(tz).strftime("%Y-%m-%d")
    cached = await _cached_motivation(db, day)
    if cached:
        return {"text": cached, "source": "cache", "day": day}
    return {"text": pick("motivation_fallback"), "source": "fallback", "day": day}


async def generate_roast_of_the_day(
    db: aiosqlite.Connection,
    tz: ZoneInfo,
    day: str | None = None,
    *,
    force: bool = False,
) -> dict[str, Any]:
    day = day or datetime.now(tz).strftime("%Y-%m-%d")
    key = _roast_key(day)
    if not force:
        cached = await _get_cache(db, key)
        if cached:
            return {"text": cached, "source": "cache", "day": day}

    stats = await compute_stats(db)
    recent = await _recent_roasts(db, before_day=day)
    history = ""
    if recent:
        lines = "\n".join(f"- {text}" for text in recent)
        history = (
            f"\n\nDiese Roasts der letzten {ROAST_HISTORY_DAYS} Tage wurden bereits verwendet "
            f"(nicht wiederholen, neue Idee):\n{lines}"
        )
    prompt = (
        "Schreibe den Roast-of-the-Day. "
        f"Streak: {stats['streak']} Tage. "
        f"Compliance über {stats['compliance_window_days']} aktive Tage: {stats['compliance_percent']}%. "
        f"Ein sarkastischer Spruch im dark-humor Medikamenten-Coach Stil.{history}"
    )
    text = await _call_openrouter(prompt, system=ROAST_SYSTEM)
    if not text:
        text = pick("roast_fallback")
        source = "fallback"
    else:
        source = "ai"
    await _set_cache(db, key, text)
    logger.info("Generated roast of the day for %s (%s)", day, source)
    return {"text": text, "source": source, "day": day}


async def get_roast_of_the_day(db: aiosqlite.Connection, tz: ZoneInfo) -> dict[str, Any]:
    day = datetime.now(tz).strftime("%Y-%m-%d")
    cached = await _get_cache(db, _roast_key(day))
    if cached:
        return {"text": cached, "source": "cache", "day": day}
    return {"text": pick("roast_fallback"), "source": "fallback", "day": day}
