"""SQLite CRUD helpers for proactive signal-based selling."""

from __future__ import annotations

import json
import time
import uuid
from typing import Any, Optional

from .schema import get_db


# ──────────────────────────────────────────────
# Signals
# ──────────────────────────────────────────────

def save_signal(
    signal_type: str,
    source: str,
    *,
    prospect_id: str | None = None,
    prospect_name: str | None = None,
    prospect_title: str | None = None,
    linkedin_id: str | None = None,
    campaign_id: str | None = None,
    content: str | None = None,
    post_id: str | None = None,
    metadata_json: str | None = None,
    expires_at: int | None = None,
) -> str:
    """Save a new signal. Returns the signal ID."""
    signal_id = uuid.uuid4().hex[:12]
    now = int(time.time())

    # Default expiry if not provided
    if expires_at is None:
        from ..constants import SIGNAL_TTL_DEFAULT
        expires_at = now + SIGNAL_TTL_DEFAULT

    db = get_db()
    db.execute(
        """INSERT INTO signals
           (id, signal_type, source, prospect_id, prospect_name, prospect_title,
            linkedin_id, campaign_id, content, post_id, metadata_json,
            status, expires_at, detected_at)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, 'new', ?, ?)""",
        (
            signal_id, signal_type, source,
            prospect_id, prospect_name, prospect_title,
            linkedin_id, campaign_id,
            content, post_id, metadata_json,
            expires_at, now,
        ),
    )
    db.commit()
    db.close()
    return signal_id


def get_signal(signal_id: str) -> dict[str, Any] | None:
    """Get a signal by ID."""
    db = get_db()
    row = db.execute("SELECT * FROM signals WHERE id = ?", (signal_id,)).fetchone()
    db.close()
    return dict(row) if row else None


def list_signals(
    *,
    status: str | None = None,
    signal_type: str | None = None,
    linkedin_id: str | None = None,
    campaign_id: str | None = None,
    limit: int = 50,
    offset: int = 0,
    order_by: str = "detected_at DESC",
) -> list[dict[str, Any]]:
    """List signals with optional filters."""
    conditions: list[str] = []
    params: list[Any] = []

    if status:
        conditions.append("status = ?")
        params.append(status)
    if signal_type:
        conditions.append("signal_type = ?")
        params.append(signal_type)
    if linkedin_id:
        conditions.append("linkedin_id = ?")
        params.append(linkedin_id)
    if campaign_id:
        conditions.append("campaign_id = ?")
        params.append(campaign_id)

    where = " AND ".join(conditions) if conditions else "1=1"
    # Whitelist order_by to prevent injection
    allowed_orders = {
        "detected_at DESC", "detected_at ASC",
        "signal_score DESC", "signal_score ASC",
        "confidence DESC", "confidence ASC",
    }
    if order_by not in allowed_orders:
        order_by = "detected_at DESC"

    query = f"SELECT * FROM signals WHERE {where} ORDER BY {order_by} LIMIT ? OFFSET ?"
    params.extend([limit, offset])

    db = get_db()
    rows = db.execute(query, params).fetchall()
    db.close()
    return [dict(r) for r in rows]


def update_signal(signal_id: str, **kwargs: Any) -> None:
    """Update signal fields."""
    if not kwargs:
        return
    sets = ", ".join(f"{k} = ?" for k in kwargs)
    vals = list(kwargs.values())
    vals.append(signal_id)

    db = get_db()
    db.execute(f"UPDATE signals SET {sets} WHERE id = ?", vals)
    db.commit()
    db.close()


def signal_exists(
    signal_type: str,
    linkedin_id: str | None = None,
    post_id: str | None = None,
    *,
    lookback_seconds: int | None = None,
) -> bool:
    """Check if a signal already exists (for dedup).

    Matches on signal_type + (linkedin_id or post_id).
    Optionally limits check to recent signals within lookback_seconds.
    """
    conditions = ["signal_type = ?"]
    params: list[Any] = [signal_type]

    if linkedin_id:
        conditions.append("linkedin_id = ?")
        params.append(linkedin_id)
    if post_id:
        conditions.append("post_id = ?")
        params.append(post_id)

    if lookback_seconds:
        cutoff = int(time.time()) - lookback_seconds
        conditions.append("detected_at >= ?")
        params.append(cutoff)

    where = " AND ".join(conditions)
    db = get_db()
    row = db.execute(f"SELECT 1 FROM signals WHERE {where} LIMIT 1", params).fetchone()
    db.close()
    return row is not None


def count_signals_by_type(
    *,
    days: int = 7,
    campaign_id: str | None = None,
) -> dict[str, int]:
    """Count signals by type within the given time window."""
    cutoff = int(time.time()) - (days * 86400)
    conditions = ["detected_at >= ?"]
    params: list[Any] = [cutoff]

    if campaign_id:
        conditions.append("campaign_id = ?")
        params.append(campaign_id)

    where = " AND ".join(conditions)
    db = get_db()
    rows = db.execute(
        f"SELECT signal_type, COUNT(*) as cnt FROM signals WHERE {where} GROUP BY signal_type",
        params,
    ).fetchall()
    db.close()
    return {row["signal_type"]: row["cnt"] for row in rows}


def get_signal_funnel_stats(days: int = 7) -> dict[str, Any]:
    """Get signal funnel stats for dashboard."""
    cutoff = int(time.time()) - (days * 86400)

    db = get_db()

    # By status
    status_rows = db.execute(
        "SELECT status, COUNT(*) as cnt FROM signals WHERE detected_at >= ? GROUP BY status",
        (cutoff,),
    ).fetchall()
    by_status = {r["status"]: r["cnt"] for r in status_rows}

    # By type
    type_rows = db.execute(
        "SELECT signal_type, COUNT(*) as cnt FROM signals WHERE detected_at >= ? GROUP BY signal_type",
        (cutoff,),
    ).fetchall()
    by_type = {r["signal_type"]: r["cnt"] for r in type_rows}

    # Top prospects by signal count
    top_rows = db.execute(
        """SELECT linkedin_id, prospect_name, prospect_title, COUNT(*) as cnt,
                  MAX(signal_score) as max_score
           FROM signals
           WHERE detected_at >= ? AND linkedin_id IS NOT NULL
           GROUP BY linkedin_id
           ORDER BY cnt DESC, max_score DESC
           LIMIT 10""",
        (cutoff,),
    ).fetchall()
    top_prospects = [dict(r) for r in top_rows]

    db.close()
    return {
        "by_status": by_status,
        "by_type": by_type,
        "top_prospects": top_prospects,
        "total": sum(by_status.values()),
    }


def expire_old_signals() -> int:
    """Move expired signals to 'expired' status. Returns count expired."""
    now = int(time.time())
    db = get_db()
    cursor = db.execute(
        "UPDATE signals SET status = 'expired' WHERE expires_at <= ? AND status NOT IN ('expired', 'actioned', 'dismissed')",
        (now,),
    )
    count = cursor.rowcount
    db.commit()
    db.close()
    return count


# ──────────────────────────────────────────────
# Signal Watchlists
# ──────────────────────────────────────────────

def save_watchlist(
    name: str,
    watch_type: str,
    keywords: list[str],
    *,
    campaign_id: str | None = None,
) -> str:
    """Create a new watchlist. Returns watchlist ID."""
    wl_id = uuid.uuid4().hex[:12]
    db = get_db()
    db.execute(
        """INSERT INTO signal_watchlists
           (id, name, watch_type, keywords, campaign_id, is_active, created_at)
           VALUES (?, ?, ?, ?, ?, 1, ?)""",
        (wl_id, name, watch_type, json.dumps(keywords), campaign_id, int(time.time())),
    )
    db.commit()
    db.close()
    return wl_id


def list_watchlists(
    *,
    is_active: bool | None = True,
    watch_type: str | None = None,
    campaign_id: str | None = None,
) -> list[dict[str, Any]]:
    """List watchlists with optional filters."""
    conditions: list[str] = []
    params: list[Any] = []

    if is_active is not None:
        conditions.append("is_active = ?")
        params.append(1 if is_active else 0)
    if watch_type:
        conditions.append("watch_type = ?")
        params.append(watch_type)
    if campaign_id:
        conditions.append("campaign_id = ?")
        params.append(campaign_id)

    where = " AND ".join(conditions) if conditions else "1=1"
    db = get_db()
    rows = db.execute(
        f"SELECT * FROM signal_watchlists WHERE {where} ORDER BY created_at DESC",
        params,
    ).fetchall()
    db.close()

    result = []
    for r in rows:
        d = dict(r)
        # Parse keywords JSON
        try:
            d["keywords_list"] = json.loads(d.get("keywords", "[]"))
        except (json.JSONDecodeError, TypeError):
            d["keywords_list"] = []
        result.append(d)
    return result


def get_watchlist(watchlist_id: str) -> dict[str, Any] | None:
    """Get a watchlist by ID."""
    db = get_db()
    row = db.execute("SELECT * FROM signal_watchlists WHERE id = ?", (watchlist_id,)).fetchone()
    db.close()
    if not row:
        return None
    d = dict(row)
    try:
        d["keywords_list"] = json.loads(d.get("keywords", "[]"))
    except (json.JSONDecodeError, TypeError):
        d["keywords_list"] = []
    return d


def update_watchlist(watchlist_id: str, **kwargs: Any) -> None:
    """Update watchlist fields. Handles keywords → JSON serialization."""
    if not kwargs:
        return
    # Serialize keywords list to JSON if provided
    if "keywords" in kwargs and isinstance(kwargs["keywords"], list):
        kwargs["keywords"] = json.dumps(kwargs["keywords"])

    sets = ", ".join(f"{k} = ?" for k in kwargs)
    vals = list(kwargs.values())
    vals.append(watchlist_id)

    db = get_db()
    db.execute(f"UPDATE signal_watchlists SET {sets} WHERE id = ?", vals)
    db.commit()
    db.close()


def delete_watchlist(watchlist_id: str) -> None:
    """Delete a watchlist."""
    db = get_db()
    db.execute("DELETE FROM signal_watchlists WHERE id = ?", (watchlist_id,))
    db.commit()
    db.close()


# ──────────────────────────────────────────────
# Signal Accounts (aggregated scores)
# ──────────────────────────────────────────────

def upsert_signal_account(
    linkedin_id: str,
    prospect_name: str | None = None,
    company: str | None = None,
) -> None:
    """Update or create signal account aggregation."""
    now = int(time.time())
    db = get_db()

    # Count signals and compute top signal type
    stats = db.execute(
        """SELECT COUNT(*) as total, signal_type, MAX(signal_score) as max_score
           FROM signals
           WHERE linkedin_id = ? AND status NOT IN ('expired', 'dismissed')
           GROUP BY signal_type
           ORDER BY max_score DESC""",
        (linkedin_id,),
    ).fetchall()

    total = sum(r["total"] for r in stats)
    top_type = stats[0]["signal_type"] if stats else None
    max_score = max((r["max_score"] for r in stats), default=0.0)

    # Get last signal timestamp
    last_row = db.execute(
        "SELECT MAX(detected_at) as last_at FROM signals WHERE linkedin_id = ?",
        (linkedin_id,),
    ).fetchone()
    last_at = last_row["last_at"] if last_row else None

    db.execute(
        """INSERT INTO signal_accounts
               (linkedin_id, prospect_name, company, total_signals, composite_score,
                top_signal_type, last_signal_at, updated_at)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?)
           ON CONFLICT(linkedin_id) DO UPDATE SET
               prospect_name = COALESCE(excluded.prospect_name, signal_accounts.prospect_name),
               company = COALESCE(excluded.company, signal_accounts.company),
               total_signals = excluded.total_signals,
               composite_score = excluded.composite_score,
               top_signal_type = excluded.top_signal_type,
               last_signal_at = excluded.last_signal_at,
               updated_at = excluded.updated_at""",
        (linkedin_id, prospect_name, company, total, max_score, top_type, last_at, now),
    )
    db.commit()
    db.close()


def list_signal_accounts(
    *,
    min_score: float = 0.0,
    limit: int = 20,
) -> list[dict[str, Any]]:
    """List signal accounts ordered by composite score."""
    db = get_db()
    rows = db.execute(
        """SELECT * FROM signal_accounts
           WHERE composite_score >= ?
           ORDER BY composite_score DESC, total_signals DESC
           LIMIT ?""",
        (min_score, limit),
    ).fetchall()
    db.close()
    return [dict(r) for r in rows]


def get_signal_account(linkedin_id: str) -> dict[str, Any] | None:
    """Get signal account by linkedin_id."""
    db = get_db()
    row = db.execute(
        "SELECT * FROM signal_accounts WHERE linkedin_id = ?",
        (linkedin_id,),
    ).fetchone()
    db.close()
    return dict(row) if row else None


def _update_signal_account_score(
    linkedin_id: str,
    composite_score: float,
    total_signals: int,
    top_signal_type: str,
) -> None:
    """Update signal account with computed composite score.

    Called by signal_scorer.recompute_all_signal_accounts() after
    computing weighted scores with freshness decay.
    """
    now = int(time.time())
    db = get_db()
    db.execute(
        """UPDATE signal_accounts
           SET composite_score = ?, total_signals = ?,
               top_signal_type = ?, updated_at = ?
           WHERE linkedin_id = ?""",
        (composite_score, total_signals, top_signal_type, now, linkedin_id),
    )
    db.commit()
    db.close()


# ──────────────────────────────────────────────
# Contact cross-reference helpers
# ──────────────────────────────────────────────

def get_contact_by_linkedin_id(linkedin_id: str) -> dict[str, Any] | None:
    """Find a contact by LinkedIn provider ID across all campaigns."""
    db = get_db()
    row = db.execute(
        "SELECT * FROM contacts WHERE linkedin_id = ? LIMIT 1",
        (linkedin_id,),
    ).fetchone()
    db.close()
    return dict(row) if row else None


def get_signal_performance(campaign_id: str = "") -> dict[str, Any]:
    """Get signal-triggered outreach performance vs cold outreach.

    Compares outreaches with signal_id (signal-triggered) against those
    without (cold). Returns acceptance rates, reply rates, and conversion.

    Args:
        campaign_id: Optional campaign filter. Analyzes all if empty.

    Returns:
        {
            "signal": {"total", "invited", "connected", "replied", "won", "acceptance_rate", "reply_rate"},
            "cold": {"total", "invited", "connected", "replied", "won", "acceptance_rate", "reply_rate"},
            "signal_lift": {"acceptance", "reply"},  # % improvement
            "by_signal_type": {type: {"total", "connected", "replied"}, ...},
        }
    """
    db = get_db()

    # Build WHERE clause
    where = ""
    params: list[Any] = []
    if campaign_id:
        where = " AND o.campaign_id = ?"
        params.append(campaign_id)

    # Signal-triggered outreaches
    signal_row = db.execute(
        f"""SELECT
            COUNT(*) as total,
            SUM(CASE WHEN o.status NOT IN ('pending', 'review_pending') THEN 1 ELSE 0 END) as invited,
            SUM(CASE WHEN o.status IN ('connected', 'messaged', 'replied', 'hot_lead', 'closed_happy', 'closed_unhappy') THEN 1 ELSE 0 END) as connected,
            SUM(CASE WHEN o.status IN ('replied', 'hot_lead', 'closed_happy', 'closed_unhappy') THEN 1 ELSE 0 END) as replied,
            SUM(CASE WHEN o.status = 'closed_happy' THEN 1 ELSE 0 END) as won
        FROM outreaches o
        WHERE o.signal_id IS NOT NULL{where}""",
        params,
    ).fetchone()

    # Cold outreaches
    cold_row = db.execute(
        f"""SELECT
            COUNT(*) as total,
            SUM(CASE WHEN o.status NOT IN ('pending', 'review_pending') THEN 1 ELSE 0 END) as invited,
            SUM(CASE WHEN o.status IN ('connected', 'messaged', 'replied', 'hot_lead', 'closed_happy', 'closed_unhappy') THEN 1 ELSE 0 END) as connected,
            SUM(CASE WHEN o.status IN ('replied', 'hot_lead', 'closed_happy', 'closed_unhappy') THEN 1 ELSE 0 END) as replied,
            SUM(CASE WHEN o.status = 'closed_happy' THEN 1 ELSE 0 END) as won
        FROM outreaches o
        WHERE o.signal_id IS NULL{where}""",
        params,
    ).fetchone()

    def _build_stats(row: Any) -> dict[str, Any]:
        total = row["total"] or 0
        invited = row["invited"] or 0
        connected = row["connected"] or 0
        replied = row["replied"] or 0
        won = row["won"] or 0
        return {
            "total": total,
            "invited": invited,
            "connected": connected,
            "replied": replied,
            "won": won,
            "acceptance_rate": connected / invited if invited > 0 else 0,
            "reply_rate": replied / connected if connected > 0 else 0,
        }

    signal_stats = _build_stats(signal_row)
    cold_stats = _build_stats(cold_row)

    # Calculate signal lift
    lift: dict[str, float] = {}
    if cold_stats["acceptance_rate"] > 0:
        lift["acceptance"] = (
            (signal_stats["acceptance_rate"] - cold_stats["acceptance_rate"])
            / cold_stats["acceptance_rate"]
        )
    if cold_stats["reply_rate"] > 0:
        lift["reply"] = (
            (signal_stats["reply_rate"] - cold_stats["reply_rate"])
            / cold_stats["reply_rate"]
        )

    # Breakdown by signal type
    by_type_rows = db.execute(
        f"""SELECT
            s.signal_type,
            COUNT(*) as total,
            SUM(CASE WHEN o.status IN ('connected', 'messaged', 'replied', 'hot_lead', 'closed_happy', 'closed_unhappy') THEN 1 ELSE 0 END) as connected,
            SUM(CASE WHEN o.status IN ('replied', 'hot_lead', 'closed_happy', 'closed_unhappy') THEN 1 ELSE 0 END) as replied,
            SUM(CASE WHEN o.status = 'closed_happy' THEN 1 ELSE 0 END) as won
        FROM outreaches o
        JOIN signals s ON o.signal_id = s.id
        WHERE o.signal_id IS NOT NULL{where}
        GROUP BY s.signal_type
        ORDER BY total DESC""",
        params,
    ).fetchall()

    by_type = {}
    for row in by_type_rows:
        by_type[row["signal_type"]] = {
            "total": row["total"] or 0,
            "connected": row["connected"] or 0,
            "replied": row["replied"] or 0,
            "won": row["won"] or 0,
        }

    db.close()

    return {
        "signal": signal_stats,
        "cold": cold_stats,
        "signal_lift": lift,
        "by_signal_type": by_type,
    }


def get_signal_to_conversion_funnel(
    days: int = 30,
    campaign_id: str = "",
) -> dict[str, Any]:
    """Get full signal-to-conversion funnel with per-signal-type breakdown.

    Tracks the journey: signal detected → classified → actioned →
    outreach created → invited → connected → replied → won.

    Returns:
        {
            "total_signals": int,
            "classified": int,
            "actioned": int,
            "outreaches_created": int,
            "funnel": {  # overall outreach funnel for signal-triggered
                "invited": int, "connected": int, "replied": int,
                "hot_leads": int, "won": int, "lost": int,
            },
            "by_type": {  # per signal type
                "keyword_mention": {
                    "signals": int, "actioned": int, "outreaches": int,
                    "connected": int, "replied": int, "won": int,
                    "activation_rate": float, "conversion_rate": float,
                },
                ...
            },
            "by_action": {  # count of each action_taken
                "outreach_created": int, "campaign_added": int,
                "priority_boosted": int, "below_threshold": int,
            },
            "avg_signal_to_outreach_hours": float | None,
        }
    """
    db = get_db()
    cutoff = int(time.time()) - (days * 86400)

    # Build WHERE
    sig_where = "s.detected_at >= ?"
    sig_params: list[Any] = [cutoff]
    out_where = "o.signal_id IS NOT NULL"
    out_params: list[Any] = []

    if campaign_id:
        sig_where += " AND s.campaign_id = ?"
        sig_params.append(campaign_id)
        out_where += " AND o.campaign_id = ?"
        out_params.append(campaign_id)

    # Signal pipeline stats
    pipeline_row = db.execute(
        f"""SELECT
            COUNT(*) as total,
            SUM(CASE WHEN status IN ('classified', 'actioned') THEN 1 ELSE 0 END) as classified,
            SUM(CASE WHEN status = 'actioned' THEN 1 ELSE 0 END) as actioned
        FROM signals s
        WHERE {sig_where}""",
        sig_params,
    ).fetchone()

    total_signals = (pipeline_row["total"] or 0) if pipeline_row else 0
    classified = (pipeline_row["classified"] or 0) if pipeline_row else 0
    actioned = (pipeline_row["actioned"] or 0) if pipeline_row else 0

    # Action breakdown
    action_rows = db.execute(
        f"""SELECT action_taken, COUNT(*) as cnt
        FROM signals s
        WHERE {sig_where} AND action_taken IS NOT NULL
        GROUP BY action_taken""",
        sig_params,
    ).fetchall()
    by_action = {r["action_taken"]: r["cnt"] for r in action_rows}

    # Outreach funnel for signal-triggered outreaches
    funnel_row = db.execute(
        f"""SELECT
            COUNT(*) as total,
            SUM(CASE WHEN o.status NOT IN ('pending', 'review_pending') THEN 1 ELSE 0 END) as invited,
            SUM(CASE WHEN o.status IN ('connected', 'messaged', 'replied', 'hot_lead', 'closed_happy', 'closed_unhappy') THEN 1 ELSE 0 END) as connected,
            SUM(CASE WHEN o.status IN ('replied', 'hot_lead', 'closed_happy', 'closed_unhappy') THEN 1 ELSE 0 END) as replied,
            SUM(CASE WHEN o.status = 'hot_lead' THEN 1 ELSE 0 END) as hot_leads,
            SUM(CASE WHEN o.status = 'closed_happy' THEN 1 ELSE 0 END) as won,
            SUM(CASE WHEN o.status = 'closed_unhappy' THEN 1 ELSE 0 END) as lost
        FROM outreaches o
        WHERE {out_where}""",
        out_params,
    ).fetchone()

    funnel = {
        "invited": (funnel_row["invited"] or 0) if funnel_row else 0,
        "connected": (funnel_row["connected"] or 0) if funnel_row else 0,
        "replied": (funnel_row["replied"] or 0) if funnel_row else 0,
        "hot_leads": (funnel_row["hot_leads"] or 0) if funnel_row else 0,
        "won": (funnel_row["won"] or 0) if funnel_row else 0,
        "lost": (funnel_row["lost"] or 0) if funnel_row else 0,
    }
    outreaches_total = (funnel_row["total"] or 0) if funnel_row else 0

    # Per-signal-type funnel (join signals → outreaches)
    type_rows = db.execute(
        f"""SELECT
            s.signal_type,
            COUNT(DISTINCT s.id) as signals,
            SUM(CASE WHEN s.status = 'actioned' THEN 1 ELSE 0 END) as actioned_signals,
            COUNT(DISTINCT o.id) as outreaches,
            SUM(CASE WHEN o.status IN ('connected', 'messaged', 'replied', 'hot_lead', 'closed_happy', 'closed_unhappy') THEN 1 ELSE 0 END) as connected,
            SUM(CASE WHEN o.status IN ('replied', 'hot_lead', 'closed_happy', 'closed_unhappy') THEN 1 ELSE 0 END) as replied,
            SUM(CASE WHEN o.status = 'closed_happy' THEN 1 ELSE 0 END) as won
        FROM signals s
        LEFT JOIN outreaches o ON o.signal_id = s.id
        WHERE {sig_where}
        GROUP BY s.signal_type
        ORDER BY signals DESC""",
        sig_params,
    ).fetchall()

    by_type: dict[str, dict[str, Any]] = {}
    for row in type_rows:
        sig_type = row["signal_type"]
        signals_count = row["signals"] or 0
        outreaches_count = row["outreaches"] or 0
        connected_count = row["connected"] or 0
        replied_count = row["replied"] or 0
        won_count = row["won"] or 0

        by_type[sig_type] = {
            "signals": signals_count,
            "actioned": row["actioned_signals"] or 0,
            "outreaches": outreaches_count,
            "connected": connected_count,
            "replied": replied_count,
            "won": won_count,
            "activation_rate": outreaches_count / signals_count if signals_count > 0 else 0,
            "conversion_rate": won_count / outreaches_count if outreaches_count > 0 else 0,
        }

    # Average time from signal detection to outreach creation
    avg_time_row = db.execute(
        f"""SELECT AVG(o.created_at - s.detected_at) as avg_seconds
        FROM outreaches o
        JOIN signals s ON o.signal_id = s.id
        WHERE {out_where} AND o.created_at > s.detected_at""",
        out_params,
    ).fetchone()

    avg_hours = None
    if avg_time_row and avg_time_row["avg_seconds"]:
        avg_hours = avg_time_row["avg_seconds"] / 3600

    db.close()

    return {
        "total_signals": total_signals,
        "classified": classified,
        "actioned": actioned,
        "outreaches_created": outreaches_total,
        "funnel": funnel,
        "by_type": by_type,
        "by_action": by_action,
        "avg_signal_to_outreach_hours": avg_hours,
    }


# ──────────────────────────────────────────────
# Intent Events (compound intent from stacked signals)
# ──────────────────────────────────────────────

def save_intent_event(
    linkedin_id: str,
    event_type: str,
    signal_ids: list[str],
    signal_types: list[str],
    composite_score: float,
    *,
    company: str | None = None,
    expires_at: int | None = None,
) -> str:
    """Save a compound intent event. Returns event ID."""
    event_id = uuid.uuid4().hex[:12]
    now = int(time.time())

    if expires_at is None:
        expires_at = now + 14 * 86400  # Default 14-day expiry

    db = get_db()
    db.execute(
        """INSERT INTO intent_events
           (id, linkedin_id, company, event_type, signal_ids, signal_types,
            composite_score, detected_at, expires_at)
           VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)""",
        (
            event_id, linkedin_id, company, event_type,
            json.dumps(signal_ids), json.dumps(signal_types),
            composite_score, now, expires_at,
        ),
    )
    db.commit()
    db.close()
    return event_id


def list_intent_events(
    *,
    linkedin_id: str | None = None,
    event_type: str | None = None,
    active_only: bool = True,
    limit: int = 50,
) -> list[dict[str, Any]]:
    """List intent events with optional filters."""
    conditions: list[str] = []
    params: list[Any] = []
    now = int(time.time())

    if linkedin_id:
        conditions.append("linkedin_id = ?")
        params.append(linkedin_id)
    if event_type:
        conditions.append("event_type = ?")
        params.append(event_type)
    if active_only:
        conditions.append("expires_at > ?")
        params.append(now)

    where = " AND ".join(conditions) if conditions else "1=1"
    db = get_db()
    rows = db.execute(
        f"""SELECT * FROM intent_events
            WHERE {where}
            ORDER BY composite_score DESC, detected_at DESC
            LIMIT ?""",
        params + [limit],
    ).fetchall()
    db.close()

    result = []
    for r in rows:
        d = dict(r)
        try:
            d["signal_ids_list"] = json.loads(d.get("signal_ids", "[]"))
        except (json.JSONDecodeError, TypeError):
            d["signal_ids_list"] = []
        try:
            d["signal_types_list"] = json.loads(d.get("signal_types", "[]"))
        except (json.JSONDecodeError, TypeError):
            d["signal_types_list"] = []
        result.append(d)
    return result


def intent_event_exists(
    linkedin_id: str,
    event_type: str,
    *,
    lookback_seconds: int = 14 * 86400,
) -> bool:
    """Check if a compound intent event already exists (for dedup)."""
    cutoff = int(time.time()) - lookback_seconds
    db = get_db()
    row = db.execute(
        """SELECT 1 FROM intent_events
           WHERE linkedin_id = ? AND event_type = ? AND detected_at >= ?
           LIMIT 1""",
        (linkedin_id, event_type, cutoff),
    ).fetchone()
    db.close()
    return row is not None


def get_intent_event_stats(days: int = 30) -> dict[str, Any]:
    """Get intent event stats for dashboard."""
    cutoff = int(time.time()) - (days * 86400)
    db = get_db()

    # By type
    type_rows = db.execute(
        """SELECT event_type, COUNT(*) as cnt, AVG(composite_score) as avg_score
           FROM intent_events
           WHERE detected_at >= ?
           GROUP BY event_type
           ORDER BY cnt DESC""",
        (cutoff,),
    ).fetchall()

    by_type = {}
    total = 0
    for r in type_rows:
        by_type[r["event_type"]] = {
            "count": r["cnt"],
            "avg_score": r["avg_score"] or 0,
        }
        total += r["cnt"]

    # By action
    action_rows = db.execute(
        """SELECT action_taken, COUNT(*) as cnt
           FROM intent_events
           WHERE detected_at >= ? AND action_taken IS NOT NULL
           GROUP BY action_taken""",
        (cutoff,),
    ).fetchall()
    by_action = {r["action_taken"]: r["cnt"] for r in action_rows}

    db.close()
    return {
        "total": total,
        "by_type": by_type,
        "by_action": by_action,
    }


def update_intent_event(event_id: str, **kwargs: Any) -> None:
    """Update intent event fields."""
    if not kwargs:
        return
    sets = ", ".join(f"{k} = ?" for k in kwargs)
    vals = list(kwargs.values())
    vals.append(event_id)
    db = get_db()
    db.execute(f"UPDATE intent_events SET {sets} WHERE id = ?", vals)
    db.commit()
    db.close()


def get_daily_signal_search_count(search_type: str = "keyword") -> int:
    """Count how many signal searches we've done today (for rate limiting)."""
    today_start = int(time.time()) - (int(time.time()) % 86400)  # Midnight UTC
    db = get_db()
    row = db.execute(
        """SELECT COUNT(*) as cnt FROM signals
           WHERE source = ? AND detected_at >= ?""",
        (f"{search_type}_search", today_start),
    ).fetchone()
    db.close()
    return row["cnt"] if row else 0
