"""Revenue Cockpit — DB operations, import, tracking, agent tasks."""
from __future__ import annotations

import json
from datetime import date, datetime, timezone
from typing import Any, Optional

from app.db import execute, fetch_all, fetch_one
from app.services import agent_integration
from app.services.excel_import import DEFAULT_FILE, DEFAULT_SHEET, parse_revenue_sheet


def fetch_excel_parse(path: str = DEFAULT_FILE, sheet: str | None = DEFAULT_SHEET) -> dict[str, Any]:
    result = parse_revenue_sheet(path, sheet)
    if not result.get("ok"):
        raise RuntimeError(result.get("error") or "Excel parse failed")
    return result


async def fetch_excel_parse_async(path: str = DEFAULT_FILE, sheet: str | None = DEFAULT_SHEET) -> dict[str, Any]:
    return fetch_excel_parse(path, sheet)


def _ser(row: dict[str, Any] | None) -> dict[str, Any] | None:
    if not row:
        return None
    out = dict(row)
    for k, v in list(out.items()):
        if hasattr(v, "isoformat"):
            out[k] = v.isoformat()
    return out


def _log_change(entity_type: str, entity_id: int, field: str, old: Any, new: Any, by: str = "ceo") -> None:
    execute(
        """
        INSERT INTO revenue_change_log (entity_type, entity_id, field_name, old_value, new_value, changed_by)
        VALUES (%s, %s, %s, %s, %s, %s)
        """,
        (entity_type, entity_id, field, str(old) if old is not None else None, str(new) if new is not None else None, by),
    )


def import_from_parsed(parsed: dict[str, Any], imported_by: str = "ceo", replace: bool = True) -> dict[str, Any]:
    if replace:
        execute("DELETE FROM revenue_objectives WHERE project_id IN (SELECT id FROM revenue_projects)")
        execute("DELETE FROM revenue_projects")
        execute("UPDATE revenue_cockpit_goals SET is_active = FALSE WHERE is_active = TRUE")

    goals = parsed.get("goals") or {}
    g = fetch_one(
        """
        INSERT INTO revenue_cockpit_goals (vision_text, horizon_text, mid_text, tagline, is_active)
        VALUES (%s, %s, %s, %s, TRUE)
        RETURNING *
        """,
        (
            goals.get("vision_text", ""),
            goals.get("horizon_text", ""),
            goals.get("mid_text", ""),
            goals.get("tagline", ""),
        ),
    )

    count = 0
    for p in parsed.get("projects") or []:
        row = fetch_one(
            """
            INSERT INTO revenue_projects
              (name, category, margin_month, margin_year, target_revenue, next_steps, status, sort_order, metadata)
            VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s::jsonb)
            RETURNING id
            """,
            (
                p["name"],
                p.get("category", "deal"),
                p.get("margin_month"),
                p.get("margin_year"),
                p.get("target_revenue"),
                p.get("next_steps"),
                p.get("status", "active"),
                p.get("sort_order", count),
                json.dumps({
                    "source_row": p.get("source_row"),
                    "row_style": p.get("row_style", "white"),
                    "imported": True,
                }),
            ),
        )
        pid = row["id"] if row else None
        if pid and p.get("next_steps"):
            for i, line in enumerate([ln.strip() for ln in p["next_steps"].split("\n") if ln.strip()][:5]):
                execute(
                    """
                    INSERT INTO revenue_objectives (project_id, title, description, status, sort_order)
                    VALUES (%s, %s, %s, 'open', %s)
                    """,
                    (pid, line[:255], p["next_steps"] if i == 0 else None, i),
                )
        count += 1

    run = fetch_one(
        """
        INSERT INTO revenue_import_runs (source_file, sheet_name, rows_imported, goals_imported, imported_by, metadata)
        VALUES (%s, %s, %s, TRUE, %s, %s::jsonb)
        RETURNING *
        """,
        (
            parsed.get("source_file", ""),
            parsed.get("sheet_name", ""),
            count,
            imported_by,
            json.dumps({"parsed_at": parsed.get("parsed_at"), "file_mtime": parsed.get("file_mtime")}),
        ),
    )
    take_snapshot()
    return {"ok": True, "projects_imported": count, "goals": _ser(g), "import_run": _ser(run)}


def get_active_goals() -> dict[str, Any] | None:
    return _ser(fetch_one("SELECT * FROM revenue_cockpit_goals WHERE is_active = TRUE ORDER BY id DESC LIMIT 1"))


def update_goals(data: dict[str, Any], changed_by: str = "ceo") -> dict[str, Any] | None:
    old = fetch_one("SELECT * FROM revenue_cockpit_goals WHERE is_active = TRUE ORDER BY id DESC LIMIT 1")
    if not old:
        return None
    fields = ("vision_text", "horizon_text", "mid_text", "tagline")
    sets, params = [], []
    for k in fields:
        if k in data:
            sets.append(f"{k} = %s")
            params.append(data[k])
            if str(old.get(k)) != str(data[k]):
                _log_change("goals", old["id"], k, old.get(k), data[k], changed_by)
    if not sets:
        return get_active_goals()
    params.append(old["id"])
    execute(
        f"UPDATE revenue_cockpit_goals SET {', '.join(sets)}, updated_at = NOW() WHERE id = %s",
        tuple(params),
    )
    return get_active_goals()


def list_projects(status: str | None = None) -> list[dict[str, Any]]:
    clauses, params = [], []
    if status:
        clauses.append("status = %s")
        params.append(status)
    where = (" WHERE " + " AND ".join(clauses)) if clauses else ""
    rows = fetch_all(
        f"""
        SELECT p.*,
               (SELECT COUNT(*) FROM revenue_objectives o WHERE o.project_id = p.id AND o.status = 'open') AS open_objectives,
               (SELECT COUNT(*) FROM agent_tasks t WHERE t.revenue_project_id = p.id AND t.status NOT IN ('completed','cancelled')) AS open_tasks
        FROM revenue_projects p
        {where}
        ORDER BY p.sort_order ASC, p.name ASC
        """,
        tuple(params) if params else None,
    )
    return [_ser({**r, "row_style": _infer_row_style(r.get("name"), r.get("margin_month"), r.get("metadata"))}) for r in rows]


def get_project(project_id: int) -> dict[str, Any] | None:
    p = _ser(fetch_one("SELECT * FROM revenue_projects WHERE id = %s", (project_id,)))
    if not p:
        return None
    p["objectives"] = [_ser(r) for r in fetch_all(
        "SELECT * FROM revenue_objectives WHERE project_id = %s ORDER BY sort_order, id",
        (project_id,),
    )]
    p["tasks"] = [_ser(r) for r in fetch_all(
        """
        SELECT * FROM agent_tasks WHERE revenue_project_id = %s
        ORDER BY created_at DESC LIMIT 50
        """,
        (project_id,),
    )]
    p["history"] = [_ser(r) for r in fetch_all(
        """
        SELECT * FROM revenue_change_log
        WHERE (entity_type = 'project' AND entity_id = %s)
           OR (entity_type = 'objective' AND entity_id IN (
               SELECT id FROM revenue_objectives WHERE project_id = %s))
        ORDER BY created_at DESC LIMIT 30
        """,
        (project_id, project_id),
    )]
    p["row_style"] = _infer_row_style(p.get("name"), p.get("margin_month"), p.get("metadata"))
    return p


def set_project_row_style(project_id: int, row_style: str) -> None:
    old = fetch_one("SELECT metadata FROM revenue_projects WHERE id = %s", (project_id,))
    meta = dict(old.get("metadata") or {}) if old else {}
    meta["row_style"] = row_style
    execute(
        "UPDATE revenue_projects SET metadata = %s::jsonb, updated_at = NOW() WHERE id = %s",
        (json.dumps(meta), project_id),
    )


def update_project(project_id: int, data: dict[str, Any], changed_by: str = "ceo") -> dict[str, Any] | None:
    old = fetch_one("SELECT * FROM revenue_projects WHERE id = %s", (project_id,))
    if not old:
        return None
    fields = {
        "name": data.get("name"),
        "category": data.get("category"),
        "margin_month": data.get("margin_month"),
        "margin_year": data.get("margin_year"),
        "target_revenue": data.get("target_revenue"),
        "next_steps": data.get("next_steps"),
        "status": data.get("status"),
        "priority": data.get("priority"),
    }
    sets, params = [], []
    for k, v in fields.items():
        if k in data:
            sets.append(f"{k} = %s")
            params.append(v)
            if str(old.get(k)) != str(v):
                _log_change("project", project_id, k, old.get(k), v, changed_by)
    if not sets:
        return get_project(project_id)
    params.append(project_id)
    execute(f"UPDATE revenue_projects SET {', '.join(sets)}, updated_at = NOW() WHERE id = %s", tuple(params))
    return get_project(project_id)


def _infer_row_style(name: str, margin_month: Any, metadata: Any) -> str:
    if isinstance(metadata, dict) and metadata.get("row_style"):
        return str(metadata["row_style"])
    n = (name or "").lower()
    if "foodlinkk food marketing" in n or "total earnings" in n or "loonkosten aissa" in n:
        return "yellow"
    if "foodservice" in n or n.strip() == "dirk":
        return "orange"
    if margin_month is not None and margin_month > 0:
        return "green"
    if any(x in n for x in ("plus", "vomar", "hoogvliet", "deka", "spar", "doner palace", "zakat")):
        return "red"
    return "white"


def create_objective(project_id: int, title: str, description: str | None = None, priority: str = "normal") -> dict[str, Any]:
    row = fetch_one(
        """
        INSERT INTO revenue_objectives (project_id, title, description, priority)
        VALUES (%s, %s, %s, %s)
        RETURNING *
        """,
        (project_id, title.strip(), description, priority),
    )
    _log_change("objective", row["id"], "created", None, title, "ceo")
    return _ser(row) or {}


def update_objective(objective_id: int, data: dict[str, Any]) -> dict[str, Any] | None:
    old = fetch_one("SELECT * FROM revenue_objectives WHERE id = %s", (objective_id,))
    if not old:
        return None
    for k in ("title", "description", "status", "priority", "due_date"):
        if k in data:
            execute(
                f"UPDATE revenue_objectives SET {k} = %s, updated_at = NOW() WHERE id = %s",
                (data[k], objective_id),
            )
            _log_change("objective", objective_id, k, old.get(k), data[k])
    return _ser(fetch_one("SELECT * FROM revenue_objectives WHERE id = %s", (objective_id,)))


def dashboard_stats() -> dict[str, Any]:
    goals = get_active_goals()
    totals = fetch_one(
        """
        SELECT
          COUNT(*) FILTER (WHERE status = 'active') AS active_projects,
          COALESCE(SUM(margin_year) FILTER (WHERE status = 'active'), 0) AS total_margin_year,
          COALESCE(SUM(margin_month) FILTER (WHERE status = 'active'), 0) AS total_margin_month,
          COUNT(*) FILTER (WHERE category = 'deal' AND status = 'active') AS active_deals
        FROM revenue_projects
        """
    ) or {}
    open_obj = fetch_one("SELECT COUNT(*) AS n FROM revenue_objectives WHERE status = 'open'") or {"n": 0}
    open_tasks = fetch_one(
        "SELECT COUNT(*) AS n FROM agent_tasks WHERE status NOT IN ('completed','cancelled') AND revenue_project_id IS NOT NULL"
    ) or {"n": 0}
    prev = fetch_one(
        "SELECT * FROM revenue_snapshots WHERE snapshot_date < CURRENT_DATE ORDER BY snapshot_date DESC LIMIT 1"
    )
    return {
        "goals": goals,
        "active_projects": int(totals.get("active_projects") or 0),
        "active_deals": int(totals.get("active_deals") or 0),
        "total_margin_year": float(totals.get("total_margin_year") or 0),
        "total_margin_month": float(totals.get("total_margin_month") or 0),
        "open_objectives": int(open_obj.get("n") or 0),
        "open_agent_tasks": int(open_tasks.get("n") or 0),
        "previous_snapshot": _ser(prev),
    }


def take_snapshot() -> dict[str, Any]:
    stats = dashboard_stats()
    row = fetch_one(
        """
        INSERT INTO revenue_snapshots
          (snapshot_date, total_margin_year, total_margin_month, active_projects, open_objectives, open_agent_tasks, payload)
        VALUES (CURRENT_DATE, %s, %s, %s, %s, %s, %s::jsonb)
        ON CONFLICT (snapshot_date) DO UPDATE SET
          total_margin_year = EXCLUDED.total_margin_year,
          total_margin_month = EXCLUDED.total_margin_month,
          active_projects = EXCLUDED.active_projects,
          open_objectives = EXCLUDED.open_objectives,
          open_agent_tasks = EXCLUDED.open_agent_tasks,
          payload = EXCLUDED.payload,
          created_at = NOW()
        RETURNING *
        """,
        (
            stats["total_margin_year"],
            stats["total_margin_month"],
            stats["active_projects"],
            stats["open_objectives"],
            stats["open_agent_tasks"],
            json.dumps({"goals_id": (stats.get("goals") or {}).get("id")}),
        ),
    )
    return _ser(row) or {}


def list_snapshots(limit: int = 90) -> list[dict[str, Any]]:
    rows = fetch_all(
        "SELECT * FROM revenue_snapshots ORDER BY snapshot_date DESC LIMIT %s",
        (max(1, min(limit, 365)),),
    )
    return [_ser(r) for r in rows]


def assign_agent_task(
    project_id: int,
    agent_name: str,
    title: str,
    description: str | None = None,
    objective_id: int | None = None,
    priority: str = "normal",
    delegate_herman: bool = True,
) -> dict[str, Any]:
    project = fetch_one("SELECT name FROM revenue_projects WHERE id = %s", (project_id,))
    if not project:
        raise ValueError("Project not found")
    agent = agent_name.strip().lower()
    desc = description or ""
    row = fetch_one(
        """
        INSERT INTO agent_tasks
          (agent_name, title, description, status, priority, assigned_by, revenue_project_id, revenue_objective_id, source)
        VALUES (%s, %s, %s, 'pending', %s, 'ceo', %s, %s, 'revenue_cockpit')
        RETURNING *
        """,
        (agent, title.strip(), desc, priority, project_id, objective_id),
    )
    task = _ser(row) or {}
    body = f"Revenue Cockpit · {project['name']}: {title}"
    if desc:
        body += f"\n{desc}"
    try:
        execute(
            """
            INSERT INTO agent_events (agent_name, agent_type, event_type, title, body, status, channel, metadata, related_table, related_id)
            VALUES (%s, 'revenue_cockpit', 'task_assigned', %s, %s, 'pending', 'revenue_cockpit', %s::jsonb, 'agent_tasks', %s)
            """,
            (
                agent,
                title.strip(),
                body,
                json.dumps({"project_id": project_id, "objective_id": objective_id, "task_id": task.get("id")}),
                task.get("id"),
            ),
        )
    except Exception:
        pass
    if delegate_herman and agent != "herman":
        try:
            agent_integration.create_handoff(
                "ceo",
                agent,
                handoff_type="task",
                payload={"task_id": task.get("id"), "project_id": project_id, "title": title},
                status="pending",
            )
        except Exception:
            pass
    _log_change("project", project_id, "agent_task", None, title, "ceo")
    return task


def list_agent_tasks(limit: int = 50) -> list[dict[str, Any]]:
    rows = fetch_all(
        """
        SELECT t.*, p.name AS project_name
        FROM agent_tasks t
        LEFT JOIN revenue_projects p ON p.id = t.revenue_project_id
        WHERE t.revenue_project_id IS NOT NULL
        ORDER BY t.created_at DESC
        LIMIT %s
        """,
        (max(1, min(limit, 200)),),
    )
    return [_ser(r) for r in rows]


async def delegate_via_herman(project_id: int, message: str) -> dict[str, Any]:
    from app.services import herman as herman_service

    project = get_project(project_id)
    if not project:
        raise ValueError("Project not found")
    prompt = f"[Revenue Cockpit · {project['name']}] {message}"
    result = await herman_service.chat(prompt)
    return {"ok": True, "result": result, "project_id": project_id}
