"""Fathom API client — fetches meetings, transcripts, and manages webhooks."""

import json
import os
import sqlite3
import time
from datetime import datetime, timezone, timedelta

import httpx

_FATHOM_BASE = "https://api.fathom.ai/external/v1"
_fathom_headers = lambda: {
    "X-Api-Key": os.getenv("FATHOM_API_KEY", ""),
    "Content-Type": "application/json",
}

_FATHOM_DB = os.path.join(os.path.dirname(os.path.abspath(__file__)), "fathom.db")
_COMPANY_DOMAIN = "codebuddy.co"

# IST timezone
IST = timezone(timedelta(hours=5, minutes=30))


def _init_fathom_db():
    """Create fathom DB tables if they don't exist."""
    conn = sqlite3.connect(_FATHOM_DB)
    conn.executescript("""
        CREATE TABLE IF NOT EXISTS meeting_moms (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            recording_id TEXT UNIQUE NOT NULL,
            title TEXT,
            meeting_date TEXT,
            attendees TEXT,
            transcript TEXT,
            mom_text TEXT,
            status TEXT DEFAULT 'pending',
            created_at REAL,
            share_url TEXT DEFAULT '',
            project_name TEXT DEFAULT '',
            meeting_type TEXT DEFAULT ''
        );
        CREATE TABLE IF NOT EXISTS fathom_sync (
            key TEXT PRIMARY KEY,
            value TEXT
        );
        CREATE TABLE IF NOT EXISTS projects (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT UNIQUE NOT NULL,
            created_at REAL
        );
    """)
    # Migration: add columns if they don't exist (for existing DBs)
    try:
        conn.execute("SELECT share_url FROM meeting_moms LIMIT 1")
    except sqlite3.OperationalError:
        conn.execute("ALTER TABLE meeting_moms ADD COLUMN share_url TEXT DEFAULT ''")
    try:
        conn.execute("SELECT project_name FROM meeting_moms LIMIT 1")
    except sqlite3.OperationalError:
        conn.execute("ALTER TABLE meeting_moms ADD COLUMN project_name TEXT DEFAULT ''")
    try:
        conn.execute("SELECT meeting_type FROM meeting_moms LIMIT 1")
    except sqlite3.OperationalError:
        conn.execute("ALTER TABLE meeting_moms ADD COLUMN meeting_type TEXT DEFAULT ''")
    conn.commit()
    conn.close()


_init_fathom_db()


def _get_last_sync_time() -> str:
    """Get the last time we checked for new meetings."""
    conn = sqlite3.connect(_FATHOM_DB)
    row = conn.execute("SELECT value FROM fathom_sync WHERE key = 'last_check'").fetchone()
    conn.close()
    return row[0] if row else ""


def _set_last_sync_time(ts: str):
    conn = sqlite3.connect(_FATHOM_DB)
    conn.execute("INSERT OR REPLACE INTO fathom_sync (key, value) VALUES ('last_check', ?)", (ts,))
    conn.commit()
    conn.close()


def _is_processed(recording_id: str) -> bool:
    conn = sqlite3.connect(_FATHOM_DB)
    row = conn.execute("SELECT 1 FROM meeting_moms WHERE recording_id = ?", (recording_id,)).fetchone()
    conn.close()
    return row is not None


def classify_meeting_type(meeting: dict, transcript: list[dict]) -> str:
    """Classify meeting as 'internal' or 'external' based on attendee email domains."""
    domains = set()
    for inv in meeting.get("calendar_invitees", []):
        domain = inv.get("email_domain", "")
        if not domain:
            email = inv.get("email", "")
            if "@" in email:
                domain = email.split("@")[1].lower()
        if domain:
            domains.add(domain.lower())
    # If any domain is outside the company domain, it's external
    for d in domains:
        if d and d != _COMPANY_DOMAIN:
            return "external"
    return "internal"


def save_mom(recording_id: str, title: str, meeting_date: str, attendees: str,
             transcript: str, mom_text: str, status: str = "completed",
             share_url: str = "", project_name: str = "", meeting_type: str = ""):
    conn = sqlite3.connect(_FATHOM_DB)
    conn.execute("""
        INSERT OR REPLACE INTO meeting_moms
        (recording_id, title, meeting_date, attendees, transcript, mom_text, status, created_at, share_url, project_name, meeting_type)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
    """, (recording_id, title, meeting_date, attendees, transcript, mom_text, status, time.time(), share_url, project_name, meeting_type))
    conn.commit()
    conn.close()


def update_mom_field(mom_id: int, field: str, value: str) -> bool:
    """Update a single field on a MOM. Allowed fields: project_name, meeting_type, mom_text."""
    allowed = {"project_name", "meeting_type", "mom_text"}
    if field not in allowed:
        return False
    conn = sqlite3.connect(_FATHOM_DB)
    conn.execute(f"UPDATE meeting_moms SET {field} = ? WHERE id = ?", (value, mom_id))
    conn.commit()
    conn.close()
    return True


def get_all_moms() -> list[dict]:
    """Get all MOMs from the database, newest first."""
    conn = sqlite3.connect(_FATHOM_DB)
    conn.row_factory = sqlite3.Row
    rows = conn.execute("SELECT * FROM meeting_moms ORDER BY meeting_date DESC, created_at DESC").fetchall()
    conn.close()
    return [dict(r) for r in rows]


def get_mom_by_id(mom_id: int) -> dict | None:
    conn = sqlite3.connect(_FATHOM_DB)
    conn.row_factory = sqlite3.Row
    row = conn.execute("SELECT * FROM meeting_moms WHERE id = ?", (mom_id,)).fetchone()
    conn.close()
    return dict(row) if row else None


def get_mom_by_recording_id(recording_id: str) -> dict | None:
    conn = sqlite3.connect(_FATHOM_DB)
    conn.row_factory = sqlite3.Row
    row = conn.execute("SELECT * FROM meeting_moms WHERE recording_id = ?", (recording_id,)).fetchone()
    conn.close()
    return dict(row) if row else None


# --- Project CRUD ---

def get_all_projects() -> list[dict]:
    conn = sqlite3.connect(_FATHOM_DB)
    conn.row_factory = sqlite3.Row
    rows = conn.execute("SELECT * FROM projects ORDER BY name").fetchall()
    conn.close()
    return [dict(r) for r in rows]


def create_project(name: str) -> dict:
    conn = sqlite3.connect(_FATHOM_DB)
    try:
        conn.execute("INSERT INTO projects (name, created_at) VALUES (?, ?)", (name.strip(), time.time()))
        conn.commit()
        row = conn.execute("SELECT * FROM projects WHERE name = ?", (name.strip(),)).fetchone()
        conn.close()
        return {"ok": True, "project": {"id": row[0], "name": row[1]}}
    except sqlite3.IntegrityError:
        conn.close()
        return {"ok": False, "error": "Project already exists"}


def delete_project(project_id: int) -> bool:
    conn = sqlite3.connect(_FATHOM_DB)
    conn.execute("DELETE FROM projects WHERE id = ?", (project_id,))
    conn.commit()
    conn.close()
    return True


def guess_project_name(title: str, existing_projects: list[str]) -> str:
    """Try to match meeting title to an existing project (case-insensitive)."""
    title_lower = title.lower()
    for p in existing_projects:
        if p.lower() in title_lower:
            return p
    # Try prefix matching: "MedSpace - Standup" → "MedSpace"
    for sep in [" - ", ": ", " – ", " — "]:
        if sep in title:
            prefix = title.split(sep)[0].strip()
            for p in existing_projects:
                if p.lower() == prefix.lower():
                    return p
    return ""


# --- Fathom API calls ---

def fathom_list_meetings(created_after: str = "") -> list[dict]:
    """List meetings from Fathom. created_after is ISO 8601 format."""
    params = {}
    if created_after:
        params["created_after"] = created_after
    resp = httpx.get(f"{_FATHOM_BASE}/meetings", headers=_fathom_headers(), params=params, timeout=30)
    resp.raise_for_status()
    data = resp.json()
    # Fathom returns {"items": [...], "next_cursor": ...}
    if isinstance(data, dict):
        return data.get("items", data.get("meetings", data.get("data", [])))
    return data


def fathom_get_transcript(recording_id: str) -> list[dict]:
    """Fetch transcript for a recording. Returns list of {speaker, text, timestamp}."""
    resp = httpx.get(f"{_FATHOM_BASE}/recordings/{recording_id}/transcript", headers=_fathom_headers(), timeout=30)
    resp.raise_for_status()
    data = resp.json()
    return data if isinstance(data, list) else data.get("transcript", data.get("data", []))


def _get_speaker_name(entry: dict) -> str:
    """Extract speaker name from transcript entry. Speaker can be a string or nested object."""
    speaker = entry.get("speaker", "Unknown")
    if isinstance(speaker, dict):
        return speaker.get("display_name", speaker.get("name", "Unknown"))
    return entry.get("speaker_name", speaker)


def _get_speaker_email(entry: dict) -> str:
    """Extract speaker email from transcript entry."""
    speaker = entry.get("speaker", {})
    if isinstance(speaker, dict):
        return speaker.get("matched_calendar_invitee_email", "")
    return ""


def format_transcript(transcript: list[dict]) -> str:
    """Format raw transcript into readable text."""
    lines = []
    for entry in transcript:
        speaker = _get_speaker_name(entry)
        text = entry.get("text", entry.get("content", ""))
        timestamp = entry.get("timestamp", entry.get("start_time", ""))
        if timestamp:
            lines.append(f"[{timestamp}] {speaker}: {text}")
        else:
            lines.append(f"{speaker}: {text}")
    return "\n".join(lines)


def extract_attendees(transcript: list[dict]) -> list[str]:
    """Extract unique speaker names from transcript."""
    speakers = set()
    for entry in transcript:
        speaker = _get_speaker_name(entry)
        if speaker and speaker != "Unknown":
            speakers.add(speaker)
    return sorted(speakers)


def extract_attendees_from_meeting(meeting: dict, transcript: list[dict]) -> list[str]:
    """Extract attendees from meeting invitees + transcript speakers."""
    attendees = set()
    # From calendar invitees
    for inv in meeting.get("calendar_invitees", []):
        name = inv.get("name", inv.get("email", ""))
        if name:
            attendees.add(name)
    # From transcript speakers
    for entry in transcript:
        speaker = _get_speaker_name(entry)
        if speaker and speaker != "Unknown":
            attendees.add(speaker)
    return sorted(attendees)


def _get_pending_moms() -> list[dict]:
    """Get all MOMs with status 'pending' (transcript wasn't ready earlier)."""
    conn = sqlite3.connect(_FATHOM_DB)
    conn.row_factory = sqlite3.Row
    rows = conn.execute("SELECT recording_id, title, meeting_date, share_url, project_name, meeting_type FROM meeting_moms WHERE status = 'pending'").fetchall()
    conn.close()
    return [dict(r) for r in rows]


def _process_meeting(recording_id: str, title: str, meeting_date: str, share_url: str,
                     meeting_data: dict | None, projects: list[str], generate_mom_fn) -> dict | None:
    """Try to fetch transcript and generate MOM for a single meeting.
    Returns processed info dict on success, None if transcript not ready."""
    meeting_type = classify_meeting_type(meeting_data or {}, [])
    project_name = guess_project_name(title, projects)

    try:
        transcript = fathom_get_transcript(recording_id)
        if not transcript:
            # Save as pending so we retry on next poll
            save_mom(recording_id, title, meeting_date, "", "", "",
                     status="pending", share_url=share_url, project_name=project_name, meeting_type=meeting_type)
            return None

        transcript_text = format_transcript(transcript)
        attendees = extract_attendees_from_meeting(meeting_data or {}, transcript)
        attendees_str = ", ".join(attendees)

        if not meeting_type or meeting_type == "internal":
            meeting_type = classify_meeting_type(meeting_data or {}, transcript)

        mom_text = generate_mom_fn(title, transcript_text, attendees_str)

        save_mom(recording_id, title, meeting_date, attendees_str, transcript_text, mom_text,
                 status="completed", share_url=share_url, project_name=project_name, meeting_type=meeting_type)
        return {"recording_id": recording_id, "title": title, "date": meeting_date}
    except Exception as e:
        save_mom(recording_id, title, meeting_date, "", "", str(e),
                 status="failed", share_url=share_url, project_name=project_name, meeting_type=meeting_type)
        return None


def poll_and_process(generate_mom_fn) -> list[dict]:
    """Poll Fathom for new meetings and generate MOMs.

    generate_mom_fn: sync function that takes (title, transcript_text, attendees) and returns MOM text.
    Returns list of newly processed meetings.
    """
    last_check = _get_last_sync_time()
    now = datetime.now(timezone.utc).isoformat()

    processed = []

    # Get existing project names for auto-matching
    projects = [p["name"] for p in get_all_projects()]

    # Step 1: Retry any pending MOMs (transcript wasn't ready before)
    for pending in _get_pending_moms():
        result = _process_meeting(
            pending["recording_id"], pending["title"], pending["meeting_date"],
            pending.get("share_url", ""), None, projects, generate_mom_fn
        )
        if result:
            processed.append(result)

    # Step 2: Check for new meetings from Fathom
    meetings = fathom_list_meetings(created_after=last_check)

    for meeting in meetings:
        recording_id = str(meeting.get("recording_id", meeting.get("id", "")))
        if not recording_id or _is_processed(recording_id):
            continue

        title = meeting.get("title", meeting.get("meeting_title", "Untitled Meeting"))
        meeting_date = meeting.get("created_at", meeting.get("date", now))
        share_url = meeting.get("share_url", "")

        result = _process_meeting(
            recording_id, title, meeting_date, share_url,
            meeting, projects, generate_mom_fn
        )
        if result:
            processed.append(result)

    _set_last_sync_time(now)
    return processed
