"""
Market Events — unified admin API for market_events + instrument impacts.
Prefix: /api/market-events

Merges what was split across /api/timeline and /api/impact.
"""
import json
import logging
from typing import Any, Dict, List, Optional

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

logger = logging.getLogger(__name__)
router = APIRouter(prefix="/api/market-events", tags=["Market Events"])


# ── Schemas ───────────────────────────────────────────────────────────────────

class SourceRef(BaseModel):
    title: str
    source: str = ""
    url: str = ""
    date: str = ""
    original_score: float = 0.0


class EventCreate(BaseModel):
    name: str
    start_date: str
    end_date: Optional[str] = None
    level: str = "short"
    category: str = "fundamental"
    sub_type: str = ""
    description: str = ""
    market_impact: str = ""
    affected_assets: List[str] = []
    impact_score: float = 0.5
    absorption_pct: Optional[float] = None
    parent_event_id: Optional[int] = None
    source_refs: List[SourceRef] = []


class EventUpdate(EventCreate):
    pass


class InstrumentImpactCreate(BaseModel):
    instrument_id: str
    impact_score: float = 0.5
    direction: str = "neutral"
    rationale: str = ""
    confidence: float = 0.7


class InstrumentImpactAdjust(BaseModel):
    adjusted_score: float
    adjusted_direction: str
    override_rationale: str = ""


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

def _parse_event(ev: Dict) -> Dict:
    """Normalize JSON fields on a raw DB row."""
    for field in ("affected_assets", "source_refs", "relevant_indicators"):
        val = ev.get(field)
        if isinstance(val, str):
            try:
                ev[field] = json.loads(val) if val else []
            except Exception:
                ev[field] = []
        elif val is None:
            ev[field] = []
    return ev


# ── List / filter ─────────────────────────────────────────────────────────────

_SORT_COLS = {
    "date":             "me.start_date",
    "score":            "me.impact_score",
    "name":             "me.name",
    "instrument_score": "COALESCE(ii.adjusted_score, ii.impact_score)",
}

@router.get("/db-status")
def db_status() -> Dict[str, Any]:
    """Quick health-check of all tables needed for market event generation."""
    from services.database import get_conn
    conn = get_conn()
    status: Dict[str, Any] = {}

    tables = {
        "market_events":        "SELECT COUNT(*), MAX(created_at) FROM market_events",
        "economic_events":      "SELECT COUNT(*), MAX(fetched_at) FROM economic_events",
        "macro_gauge_snapshots":"SELECT COUNT(*), MAX(snapshot_date) FROM macro_gauge_snapshots",
        "macro_regime_history": "SELECT COUNT(*), MAX(timestamp) FROM macro_regime_history",
        "institutional_reports":"SELECT COUNT(*), MAX(fetch_date) FROM institutional_reports",
        "ai_desks":             "SELECT COUNT(*), NULL FROM ai_desks",
    }
    for table, sql in tables.items():
        try:
            row = conn.execute(sql).fetchone()
            status[table] = {"count": row[0], "latest": row[1]}
        except Exception as e:
            status[table] = {"count": 0, "latest": None, "error": str(e)}

    # Check market_events columns
    existing_cols = {row[1] for row in conn.execute("PRAGMA table_info(market_events)").fetchall()}
    required_cols = ["sub_type", "origin", "source_refs", "actual_value", "expected_value", "surprise_pct"]
    status["market_events"]["columns_ok"] = all(c in existing_cols for c in required_cols)
    status["market_events"]["missing_columns"] = [c for c in required_cols if c not in existing_cols]

    # Summary of ai_desks types
    try:
        desks = conn.execute("SELECT type, active FROM ai_desks").fetchall()
        status["ai_desks"]["desks"] = [{"type": r[0], "active": bool(r[1])} for r in desks]
    except Exception:
        pass

    conn.close()
    return status


@router.get("")
def list_events(
    # text / category / level
    search:    Optional[str]  = Query(None),
    category:  Optional[str]  = Query(None),
    level:     Optional[str]  = Query(None),
    origin:    Optional[str]  = Query(None),
    # event date range (start_date)
    date_from: Optional[str]  = Query(None),
    date_to:   Optional[str]  = Query(None),
    # generation date range (created_at)
    gen_date_from: Optional[str] = Query(None, description="Filter by generation date (created_at) from"),
    gen_date_to:   Optional[str] = Query(None, description="Filter by generation date (created_at) to"),
    # global impact score
    min_score: float           = Query(0.0, ge=0.0, le=1.0),
    # instrument impact filter
    instrument:            Optional[str]  = Query(None, description="Filter by impact on this ticker"),
    min_instrument_score:  float          = Query(0.0, ge=0.0, le=1.0),
    instrument_direction:  Optional[str]  = Query(None, description="bullish|bearish|neutral"),
    # evaluation state
    evaluated: Optional[bool]  = Query(None),
    # sort
    sort_by:   str             = Query("date",  description="date|score|name|instrument_score"),
    sort_dir:  str             = Query("desc",  description="asc|desc"),
    # pagination
    limit:     int             = Query(200, ge=1, le=1000),
    offset:    int             = Query(0,   ge=0),
) -> Dict[str, Any]:
    from services.database import get_conn

    conn = get_conn()

    # Decide join type: INNER when filtering by instrument, LEFT otherwise
    join_type = "INNER" if instrument else "LEFT"
    join_clause = (
        f"{join_type} JOIN instrument_impacts ii "
        "ON ii.source_type='event' AND ii.source_id=me.id"
        + (f" AND ii.instrument_id='{instrument}'" if instrument else "")
    )

    where_parts: List[str] = []
    params:      List[Any] = []

    if search:
        where_parts.append("(me.name LIKE ? OR me.description LIKE ? OR me.sub_type LIKE ?)")
        like = f"%{search}%"
        params += [like, like, like]
    if category:
        where_parts.append("me.category=?")
        params.append(category)
    if level:
        where_parts.append("me.level=?")
        params.append(level)
    if origin:
        where_parts.append("me.origin=?")
        params.append(origin)
    if date_from:
        where_parts.append("me.start_date>=?")
        params.append(date_from)
    if date_to:
        where_parts.append("me.start_date<=?")
        params.append(date_to)
    if gen_date_from:
        where_parts.append("date(me.created_at)>=?")
        params.append(gen_date_from)
    if gen_date_to:
        where_parts.append("date(me.created_at)<=?")
        params.append(gen_date_to)
    if min_score > 0:
        where_parts.append("me.impact_score>=?")
        params.append(min_score)
    if instrument and min_instrument_score > 0:
        where_parts.append("COALESCE(ii.adjusted_score, ii.impact_score)>=?")
        params.append(min_instrument_score)
    if instrument and instrument_direction:
        where_parts.append("ii.direction=?")
        params.append(instrument_direction)
    if evaluated is True:
        where_parts.append(
            "EXISTS (SELECT 1 FROM instrument_impacts x WHERE x.source_type='event' AND x.source_id=me.id)"
        )
    if evaluated is False:
        where_parts.append(
            "NOT EXISTS (SELECT 1 FROM instrument_impacts x WHERE x.source_type='event' AND x.source_id=me.id)"
        )

    where_sql = ("WHERE " + " AND ".join(where_parts)) if where_parts else ""

    sort_col = _SORT_COLS.get(sort_by, "me.start_date")
    if sort_by == "instrument_score" and not instrument:
        sort_col = "me.impact_score"
    order_sql = f"ORDER BY {sort_col} {'DESC' if sort_dir == 'desc' else 'ASC'}"

    # Always GROUP BY — the LEFT JOIN on instrument_impacts creates duplicate rows
    # when an event has multiple impacts (one row per impact without GROUP BY)
    group_sql = "GROUP BY me.id"

    # Extra columns: evaluated flag + optional instrument score
    evaluated_col = (
        ", (CASE WHEN EXISTS ("
        "SELECT 1 FROM instrument_impacts _ei "
        "WHERE _ei.source_type='event' AND _ei.source_id=me.id"
        ") THEN 1 ELSE 0 END) as evaluated"
    )
    inst_col = (
        ", MAX(COALESCE(ii.adjusted_score, ii.impact_score)) as inst_score"
        ", ii.direction as inst_direction"
    ) if instrument else ", NULL as inst_score, NULL as inst_direction"

    count_sql = f"""
        SELECT COUNT(DISTINCT me.id) FROM market_events me {join_clause} {where_sql}
    """
    data_sql = f"""
        SELECT me.* {evaluated_col} {inst_col}
        FROM market_events me {join_clause}
        {where_sql} {group_sql} {order_sql}
        LIMIT ? OFFSET ?
    """

    try:
        total = conn.execute(count_sql, params).fetchone()[0]
        rows  = conn.execute(data_sql, params + [limit, offset]).fetchall()
    finally:
        conn.close()

    events = []
    for row in rows:
        ev = _parse_event(dict(row))
        if instrument and ev.get("inst_score") is not None:
            ev["instrument_filter"] = {
                "ticker": instrument,
                "score":  round(float(ev.pop("inst_score")), 3),
                "direction": ev.pop("inst_direction", None),
            }
        else:
            ev.pop("inst_score", None)
            ev.pop("inst_direction", None)
        events.append(ev)

    return {"total": total, "offset": offset, "limit": limit, "events": events}


# ── CRUD ──────────────────────────────────────────────────────────────────────

@router.post("", status_code=201)
def create_event(body: EventCreate) -> Dict[str, Any]:
    from services.database import save_market_event
    data = body.dict()
    data["source_refs"] = [r.dict() for r in body.source_refs]
    data.setdefault("origin", "manual")
    new_id = save_market_event(data)
    return {"id": new_id, "status": "created"}


@router.get("/{event_id}")
def get_event(event_id: int) -> Dict[str, Any]:
    from services.database import get_all_market_events, get_impacts_for_source
    events = get_all_market_events()
    ev = next((e for e in events if e["id"] == event_id), None)
    if not ev:
        raise HTTPException(404, f"Event {event_id} not found")
    ev = _parse_event(ev)
    impacts = get_impacts_for_source("event", event_id)
    ev["impacts"] = impacts
    ev["evaluated"] = len(impacts) > 0
    return ev


@router.put("/{event_id}")
def update_event(event_id: int, body: EventUpdate) -> Dict[str, Any]:
    from services.database import update_market_event
    data = body.dict()
    data["source_refs"] = [r.dict() for r in body.source_refs]
    ok = update_market_event(event_id, data)
    if not ok:
        raise HTTPException(404, "Event not found")
    return {"status": "updated"}


@router.delete("/{event_id}")
def delete_event(event_id: int) -> Dict[str, Any]:
    from services.database import delete_market_event, delete_impacts_for_source
    delete_impacts_for_source("event", event_id)
    delete_market_event(event_id)
    return {"status": "deleted"}


# ── AI evaluation ─────────────────────────────────────────────────────────────

@router.post("/{event_id}/evaluate")
def evaluate_event(
    event_id: int,
    force: bool = Query(False),
) -> Dict[str, Any]:
    """Trigger AI impact evaluation — evaluates instrument-level impacts."""
    from services.impact_service import evaluate_event_impacts
    try:
        result = evaluate_event_impacts(event_id, force=force)
        if "error" in result:
            raise HTTPException(400, result["error"])
        return result
    except HTTPException:
        raise
    except Exception as e:
        logger.error(f"[market_events] evaluate {event_id} failed: {e}")
        raise HTTPException(500, str(e))


@router.post("/bulk-evaluate")
def bulk_evaluate(
    event_ids: List[int],
    force: bool = Query(False),
) -> Dict[str, Any]:
    from services.impact_service import evaluate_event_impacts
    results, errors = [], []
    for eid in event_ids[:30]:
        try:
            r = evaluate_event_impacts(eid, force=force)
            if "error" not in r:
                results.append({"event_id": eid, "n_instruments": r.get("n_instruments", 0)})
            else:
                errors.append({"event_id": eid, "error": r["error"]})
        except Exception as e:
            errors.append({"event_id": eid, "error": str(e)})
    return {"evaluated": len(results), "errors": len(errors), "results": results, "error_details": errors}


# ── Instrument impacts ────────────────────────────────────────────────────────

@router.get("/{event_id}/impacts")
def get_impacts(event_id: int) -> List[Dict[str, Any]]:
    from services.database import get_impacts_for_source
    return get_impacts_for_source("event", event_id)


@router.post("/{event_id}/impacts", status_code=201)
def add_impact(event_id: int, body: InstrumentImpactCreate) -> Dict[str, Any]:
    """Manually add an instrument impact (not AI-generated)."""
    from services.database import get_all_market_events, save_instrument_impacts
    events = get_all_market_events()
    ev = next((e for e in events if e["id"] == event_id), None)
    if not ev:
        raise HTTPException(404, "Event not found")
    save_instrument_impacts([{
        "source_type":   "event",
        "source_id":     event_id,
        "source_name":   ev["name"],
        "source_date":   ev["start_date"],
        "category_name": ev.get("sub_type") or ev.get("category", ""),
        "instrument_id": body.instrument_id,
        "impact_score":  body.impact_score,
        "direction":     body.direction,
        "rationale":     body.rationale,
        "confidence":    body.confidence,
        "ai_generated":  0,
    }])
    return {"status": "added"}


@router.put("/{event_id}/impacts/{impact_id}")
def adjust_impact(event_id: int, impact_id: int, body: InstrumentImpactAdjust) -> Dict[str, Any]:
    from services.database import update_impact_adjustment
    ok = update_impact_adjustment(impact_id, body.adjusted_score, body.adjusted_direction, body.override_rationale)
    if not ok:
        raise HTTPException(404, f"Impact {impact_id} not found")
    return {"id": impact_id, "status": "adjusted"}


@router.delete("/{event_id}/impacts/{impact_id}")
def delete_impact(event_id: int, impact_id: int) -> Dict[str, Any]:
    from services.database import get_conn
    conn = get_conn()
    try:
        conn.execute("DELETE FROM instrument_impacts WHERE id=? AND source_id=?", (impact_id, event_id))
        conn.commit()
    finally:
        conn.close()
    return {"status": "deleted"}
