"""Multi-variant edition engine for Athena.

One data pipeline → multiple audience-specific editions.
Each variant is a YAML config that defines filters, ranking, and display.

World Monitor pattern: 6 variants from 1 codebase.
"""
import os
import sqlite3
import yaml
from datetime import datetime, timezone
from pathlib import Path
from typing import Optional

from oracle.config import ROOT, DB_PATH, VERDICT_THRESHOLDS


# ── Variant config defaults ────────────────────────────────────────────────

DEFAULT_CONFIG = {
    "name": "Athena AI News",
    "description": "",
    "output": "index.html",
    "filters": {
        "verdicts": ["PUBLISH", "WATCH", "ARCHIVE", "DROP"],
        "sources": [],
        "min_score": 0,
        "max_age_h": 0,
        "max_items": 0,
    },
    "ranking": {
        "by": "clickability",
        "half_life_h": 18,
    },
    "display": {
        "top_n": 8,
        "show_summary": True,
        "show_score": True,
        "show_tier": False,
        "show_verdict": False,
        "theme": "dark",
        "accent": "#5b8cff",
        "logo": "🏛️",
    },
}


def load_variant(name: str) -> dict:
    """Load a variant config by name, merging with defaults.

    Looks in variants/<name>.yaml relative to project root.
    Returns merged config dict.
    """
    variants_dir = ROOT / "variants"
    variant_file = variants_dir / f"{name}.yaml"

    if not variant_file.exists():
        available = [p.stem for p in sorted(variants_dir.glob("*.yaml"))]
        raise FileNotFoundError(
            f"Variant '{name}' not found. Available: {', '.join(available)}"
        )

    with open(variant_file) as f:
        raw = yaml.safe_load(f) or {}

    # Deep merge with defaults
    config = _deep_merge(DEFAULT_CONFIG, raw)
    config["_name"] = name
    config["_file"] = str(variant_file)
    return config


def list_variants() -> list[str]:
    """List all available variant names."""
    variants_dir = ROOT / "variants"
    if not variants_dir.exists():
        return []
    return sorted(p.stem for p in variants_dir.glob("*.yaml"))


def apply_filters(conn: sqlite3.Connection, variant: dict) -> list[dict]:
    """Fetch entries from DB filtered by variant config.

    Returns list of entry dicts matching the variant's filter criteria.
    """
    f = variant["filters"]

    # Build query
    clauses = []
    params = []

    # Verdict filter
    if f.get("verdicts"):
        verdicts = [v for v in f["verdicts"] if v]  # strip empties
        if verdicts:
            placeholders = ", ".join("?" for _ in verdicts)
            clauses.append(f"verdict IN ({placeholders})")
            params.extend(verdicts)

    # Source filter (empty = all)
    if f.get("sources"):
        sources = [s for s in f["sources"] if s]
        if sources:
            placeholders = ", ".join("?" for _ in sources)
            clauses.append(f"source IN ({placeholders})")
            params.extend(sources)

    # Min signal score
    if f.get("min_score", 0) > 0:
        clauses.append("COALESCE(signal_score, 0) >= ?")
        params.append(f["min_score"])

    # Max age
    if f.get("max_age_h", 0) > 0:
        max_age = f["max_age_h"]
        cutoff = datetime.now(timezone.utc).timestamp() - (max_age * 3600)
        clauses.append("first_seen >= datetime(?, 'unixepoch')")
        params.append(cutoff)

    # Base query
    sql = "SELECT * FROM entries"
    if clauses:
        sql += " WHERE " + " AND ".join(clauses)

    sql += " ORDER BY first_seen DESC"

    # Max items
    if f.get("max_items", 0) > 0:
        sql += f" LIMIT {int(f['max_items'])}"

    conn.row_factory = sqlite3.Row
    cur = conn.cursor()
    cur.execute(sql, params)
    rows = cur.fetchall()

    return [dict(r) for r in rows]


def rank_items(items: list[dict], variant: dict) -> list[dict]:
    """Rank items according to variant ranking config.

    Supports: clickability, signal_score, verdict_priority, freshness
    """
    ranking = variant["ranking"]
    by = ranking.get("by", "clickability")
    half_life = ranking.get("half_life_h", 18)

    if by == "clickability":
        # Use clickability module for decayed scoring
        from oracle.clickability import compute_index, decay_index
        items = compute_index(items)
        items = decay_index(items, half_life)
        items.sort(key=lambda x: x.get("clickability_decayed", 0), reverse=True)

    elif by == "signal_score":
        items.sort(key=lambda x: float(x.get("signal_score") or 0), reverse=True)

    elif by == "verdict_priority":
        # PUBLISH > WATCH > ARCHIVE > DROP
        priority = {"PUBLISH": 0, "WATCH": 1, "ARCHIVE": 2, "DROP": 3}
        items.sort(key=lambda x: (
            priority.get(x.get("verdict", "DROP"), 4),
            float(x.get("signal_score") or 0),
        ), reverse=False)

    elif by == "freshness":
        items.sort(key=lambda x: x.get("first_seen", ""), reverse=True)

    else:
        # Default fallback: signal score
        items.sort(key=lambda x: float(x.get("signal_score") or 0), reverse=True)

    return items


def render_variant(variant: dict, dry_run: bool = False, webroot: Optional[str] = None):
    """Full render pipeline for one variant.

    Load config → filter → rank → render HTML.
    Returns (output_path, item_count).
    """
    from oracle.render import render_variant_html, render_variant_json

    conn = sqlite3.connect(str(DB_PATH))
    conn.row_factory = sqlite3.Row

    items = apply_filters(conn, variant)
    items = rank_items(items, variant)
    conn.close()

    if not items:
        print(f"[variant:{variant['_name']}] No items matched filters")
        return None, 0

    page = render_variant_html(items, variant)

    # Determine output path
    output_rel = variant.get("output", "index.html")

    if dry_run:
        out_dir = ROOT / "_preview" / variant["_name"]
        out_dir.mkdir(parents=True, exist_ok=True)

        # Use just the filename for dry-run (output_rel may have subdirs)
        dry_filename = os.path.basename(output_rel)
        out_path = str(out_dir / dry_filename)

        with open(out_path, "w") as f:
            f.write(page)

        # Also write JSON feed
        json_path = str(out_dir / "feed.json")
        render_variant_json(items, variant, json_path)

        display = variant["display"]
        top_n = display.get("top_n", 8)
        top = items[:top_n]
        print(f"[variant:{variant['_name']}] wrote {out_path} ({len(items)} items)")
        print(f"  TOP {top_n}:")
        for i, it in enumerate(top, 1):
            score = f" sig={it.get('signal_score', 0):.1f}" if display.get("show_score") else ""
            tier = f" tier={it.get('source_tier', '?')}" if display.get("show_tier") else ""
            verdict = f" [{it.get('verdict', '?')}]" if display.get("show_verdict") else ""
            print(f"    {i}. {it['title'][:60]}{score}{tier}{verdict}")

        return out_path, len(items)

    # Production render
    target = webroot or str(ROOT / "site")
    out_path = os.path.join(target, output_rel)
    os.makedirs(os.path.dirname(out_path) or ".", exist_ok=True)

    with open(out_path, "w") as f:
        f.write(page)

    # JSON feed alongside
    json_path = os.path.join(os.path.dirname(out_path), "feed.json")
    render_variant_json(items, variant, json_path)

    print(f"[variant:{variant['_name']}] wrote {out_path} ({len(items)} items)")
    return out_path, len(items)


# ── Helpers ────────────────────────────────────────────────────────────────

def _deep_merge(base: dict, override: dict) -> dict:
    """Recursively merge override into base dict."""
    merged = base.copy()
    for key, value in override.items():
        if key in merged and isinstance(merged[key], dict) and isinstance(value, dict):
            merged[key] = _deep_merge(merged[key], value)
        else:
            merged[key] = value
    return merged
