"""
Refresh template_data rows from DuckDB analytics serving layer.

Reads per-city market data from the `serving.planner_defaults` serving table
and overwrites matching static values in `template_data.data_json`.  This keeps
article financial model inputs in sync with the real-world data pipeline output.

Usage:
    # Dry-run — show what would change without writing:
    uv run python -m padelnomics.scripts.refresh_from_daas --dry-run

    # Apply updates:
    uv run python -m padelnomics.scripts.refresh_from_daas

    # Also re-generate articles after updating:
    uv run python -m padelnomics.scripts.refresh_from_daas --generate

Graceful degradation:
    - If DUCKDB_PATH is unset or the file doesn't exist, exits 0 with a warning.
    - Cities not found in the analytics layer are left unchanged.
    - Only fields with non-null values in the analytics layer are updated.

Fields mapped (DuckDB → data_json camelCase key):
    rate_peak       → ratePeak
    rate_off_peak   → rateOffPeak
    court_cost_dbl  → courtCostDbl
    court_cost_sgl  → courtCostSgl
    rent_sqm        → rentSqm
    insurance       → insurance
    electricity     → electricity
    maintenance     → maintenance
    marketing       → marketing
"""

import argparse
import json
import logging
import os
import sqlite3
from pathlib import Path

from dotenv import load_dotenv

logger = logging.getLogger(__name__)

load_dotenv()

DATABASE_PATH = os.getenv("DATABASE_PATH", "data/app.db")
DUCKDB_PATH = os.getenv("DUCKDB_PATH", "data/lakehouse.duckdb")

# DuckDB column → data_json camelCase key
_FIELD_MAP: dict[str, str] = {
    "rate_peak":      "ratePeak",
    "rate_off_peak":  "rateOffPeak",
    "court_cost_dbl": "courtCostDbl",
    "court_cost_sgl": "courtCostSgl",
    "rent_sqm":       "rentSqm",
    "insurance":      "insurance",
    "electricity":    "electricity",
    "maintenance":    "maintenance",
    "marketing":      "marketing",
}


def _load_analytics(city_slugs: list[str]) -> dict[str, dict]:
    """Query DuckDB for planner_defaults rows matching the given city slugs.

    Returns dict of city_slug → {camelCase key: value}.
    Returns {} if DuckDB is unavailable.
    """
    path = Path(DUCKDB_PATH)
    if not path.exists():
        logger.warning("DuckDB not found at %s — skipping analytics refresh.", path)
        return {}

    try:
        import duckdb
    except ImportError:
        logger.warning("duckdb not installed — skipping analytics refresh.")
        return {}

    result: dict[str, dict] = {}
    try:
        conn = duckdb.connect(str(path), read_only=True)
        placeholders = ", ".join(["?"] * len(city_slugs))
        rows = conn.execute(
            f"SELECT * FROM serving.planner_defaults WHERE city_slug IN ({placeholders})",
            city_slugs,
        ).fetchall()
        cols = [d[0] for d in conn.description]
        conn.close()

        for row in rows:
            row_dict = dict(zip(cols, row))
            slug = row_dict["city_slug"]
            overrides: dict = {}
            for col, key in _FIELD_MAP.items():
                val = row_dict.get(col)
                if val is not None:
                    overrides[key] = round(float(val))
            result[slug] = overrides

    except Exception as exc:
        logger.error("DuckDB query failed: %s", exc)

    return result


def refresh(dry_run: bool = False) -> int:
    """Update template_data rows from analytics. Returns count of rows updated."""
    conn = sqlite3.connect(DATABASE_PATH)
    conn.row_factory = sqlite3.Row

    # Fetch all template_data rows that have a city_slug field
    rows = conn.execute(
        "SELECT id, template_id, data_json FROM template_data"
    ).fetchall()

    city_slug_to_ids: dict[str, list[int]] = {}
    for row in rows:
        try:
            data = json.loads(row["data_json"])
        except (json.JSONDecodeError, TypeError):
            continue
        slug = data.get("city_slug", "")
        if slug:
            city_slug_to_ids.setdefault(slug, []).append(row["id"])

    if not city_slug_to_ids:
        logger.info("No template_data rows with city_slug found.")
        conn.close()
        return 0

    analytics = _load_analytics(list(city_slug_to_ids.keys()))
    if not analytics:
        logger.info("No analytics data found — nothing to update.")
        conn.close()
        return 0

    updated = 0
    for slug, overrides in analytics.items():
        ids = city_slug_to_ids.get(slug, [])
        if not ids:
            continue

        for row_id in ids:
            row = conn.execute(
                "SELECT data_json FROM template_data WHERE id = ?", (row_id,)
            ).fetchone()
            if not row:
                continue

            data = json.loads(row["data_json"])
            changed = {k: v for k, v in overrides.items() if data.get(k) != v}
            if not changed:
                continue

            data.update(overrides)
            if dry_run:
                logger.info("[dry-run] id=%s city_slug=%s: %s", row_id, slug, changed)
            else:
                conn.execute(
                    "UPDATE template_data SET data_json = ?, updated_at = datetime('now') WHERE id = ?",
                    (json.dumps(data), row_id),
                )
                logger.info("Updated id=%s city_slug=%s: %s", row_id, slug, list(changed.keys()))
            updated += 1

    if not dry_run:
        conn.commit()

    conn.close()
    return updated


def _trigger_generation() -> None:
    """Trigger article generation via admin endpoint (requires running app)."""
    import urllib.request
    admin_key = os.getenv("ADMIN_API_KEY", "")
    base_url = os.getenv("BASE_URL", "http://localhost:5000")
    url = f"{base_url}/admin/generate-all"
    assert admin_key, "ADMIN_API_KEY env var required to trigger generation"

    req = urllib.request.Request(
        url,
        method="POST",
        headers={"X-Admin-Key": admin_key},
    )
    with urllib.request.urlopen(req, timeout=120) as resp:
        logger.info("Generation triggered: HTTP %s", resp.status)


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--dry-run", action="store_true",
                        help="Print changes without writing to DB")
    parser.add_argument("--generate", action="store_true",
                        help="Trigger article re-generation after updating")
    args = parser.parse_args()

    prefix = "[DRY RUN] " if args.dry_run else ""
    logger.info("%sRefreshing template_data from DuckDB...", prefix)
    count = refresh(dry_run=args.dry_run)
    action = "Would update" if args.dry_run else "Updated"
    logger.info("%s %s rows.", action, count)

    if args.generate and count > 0 and not args.dry_run:
        logger.info("Triggering article generation...")
        _trigger_generation()


if __name__ == "__main__":
    logging.basicConfig(level=logging.INFO, format="%(levelname)-8s %(message)s")
    main()
