"""Config, inbox state, and history for Superpitch."""

from __future__ import annotations

import json
import os
import tempfile
import threading
from datetime import datetime

from superpitch.app_paths import CONFIG_FILE, HISTORY_FILE

_history_lock = threading.Lock()
_config_lock = threading.Lock()


def _atomic_write(path, content: str) -> None:
    """Write to a temp file then rename — avoids corruption on crash."""
    fd, tmp = tempfile.mkstemp(dir=path.parent, suffix=".tmp")
    closed = False
    try:
        os.write(fd, content.encode())
        os.close(fd)
        closed = True
        os.replace(tmp, path)
    except BaseException:
        if not closed:
            os.close(fd)
        try:
            os.unlink(tmp)
        except OSError:
            pass
        raise


def _safe_float(val: object) -> float:
    try:
        return float(val)
    except (TypeError, ValueError):
        return 0.0


# ── Config ───────────────────────────────────────────────────────────────────

_DEFAULT_CONFIG = {
    "onboarding_complete": False,
    "onboarding_version":  0,
    "signed_in":           False,
    "session_token":       "",
    "user_email":          "",
    "user_slug":           "",
    "thesis":              "",
    "anthropic_api_key":   "",
    "receive_address":     "",
    "last_synced_at":      "",
    "auto_reply":          True,
}


def load_config() -> dict:
    with _config_lock:
        if not CONFIG_FILE.exists():
            return {**_DEFAULT_CONFIG}
        with open(CONFIG_FILE) as f:
            return {**_DEFAULT_CONFIG, **json.load(f)}


def save_config(config: dict) -> None:
    with _config_lock:
        CONFIG_FILE.parent.mkdir(parents=True, exist_ok=True)
        _atomic_write(CONFIG_FILE, json.dumps(config, indent=2))


# ── History ──────────────────────────────────────────────────────────────────

def _read_history_raw() -> list[dict]:
    """Read raw history lines. Caller must hold _history_lock."""
    if not HISTORY_FILE.exists():
        return []
    items = []
    with open(HISTORY_FILE) as f:
        for line in f:
            line = line.strip()
            if line:
                items.append(json.loads(line))
    return items


def _write_history_raw(items: list[dict]) -> None:
    """Write items to history. Caller must hold _history_lock."""
    HISTORY_FILE.parent.mkdir(parents=True, exist_ok=True)
    _atomic_write(HISTORY_FILE, "".join(json.dumps(it) + "\n" for it in items))


def normalize_history_item(item: dict) -> dict:
    """Ensure all expected fields exist, handling both old and new schema."""
    action = item.get("action") or _migrate_action(item)
    return {
        **item,
        "action":        action,
        "confidence":    _safe_float(item.get("confidence", 0.0)),
        "action_reason": item.get("action_reason", ""),
        "company":       item.get("company") or item.get("one_liner", "")[:60],
        "one_liner":     item.get("one_liner", ""),
        "sector":        item.get("sector", ""),
        "stage":         item.get("stage", ""),
        "geography":     item.get("geography", ""),
        "ask":           item.get("ask"),
        "traction":      item.get("traction", ""),
        "team":          item.get("team", ""),
        "thesis_fit":    item.get("thesis_fit", ""),
        "research":      item.get("research") or [],
        "missing":       item.get("missing") or [],
        "questions":     item.get("questions") or [],
        "follow_up":     item.get("follow_up") or [],
        "verdict":       item.get("verdict"),
        "verdict_reasoning": item.get("verdict_reasoning"),
        "unsure":            item.get("unsure", False),
        "follow_up_sent_at": item.get("follow_up_sent_at") or item.get("questions_sent_at"),
        "awaiting_reply":    item.get("awaiting_reply", False),
        "reply_count":       item.get("reply_count", 0),
        "opened_at":         item.get("opened_at"),
        "decision_state": item.get("decision_state"),
        "decided_at":    item.get("decided_at"),
        "source_label":  item.get("source_label", ""),
        "raw_email_id":  item.get("raw_email_id"),
        "cloud_pitch_id": item.get("cloud_pitch_id"),
        "replies":       item.get("replies") or [],
    }


def _migrate_action(item: dict) -> str:
    old = item.get("final_action", "").upper()
    if old == "APPROVE":
        return "ESCALATE"
    if old == "PASS":
        return "PASS"
    return "REVIEW"


def load_history(limit: int | None = None) -> list[dict]:
    with _history_lock:
        items = [normalize_history_item(it) for it in _read_history_raw()]
    if limit is not None:
        return items[-limit:]
    return items


def update_history_item(index: int, item: dict) -> None:
    with _history_lock:
        items = _read_history_raw()
        if 0 <= index < len(items):
            items[index] = item
            _write_history_raw(items)


def save_evidence_entry(
    packet: dict,
    raw_email_id: str | None = None,
    source_label: str = "",
    cloud_pitch_id: str | None = None,
) -> dict | None:
    """Save a processed evidence packet to history. Returns None if duplicate."""
    with _history_lock:
        HISTORY_FILE.parent.mkdir(parents=True, exist_ok=True)
        # Dedup check
        existing: set[str] = set()
        for it in _read_history_raw():
            for key in ("raw_email_id", "cloud_pitch_id"):
                val = it.get(key)
                if val:
                    existing.add(val)
        if raw_email_id and raw_email_id in existing:
            return None
        if cloud_pitch_id and cloud_pitch_id in existing:
            return None

        record = {
            "timestamp":     datetime.now().isoformat(),
            "source_label":  source_label,
            "one_liner":     packet.get("one_liner", ""),
            "company":       packet.get("company", ""),
            "founder_name":  packet.get("founder_name"),
            "founder_email": packet.get("founder_email"),
            "stage":         packet.get("stage", ""),
            "sector":        packet.get("sector", ""),
            "geography":     packet.get("geography"),
            "ask":           packet.get("ask"),
            "traction":      packet.get("traction", ""),
            "team":          packet.get("team", ""),
            "business_model": packet.get("business_model", ""),
            "thesis_fit":    packet.get("thesis_fit", ""),
            "research":      packet.get("research") or [],
            "missing":       packet.get("missing") or [],
            "questions":     packet.get("questions") or [],
            "follow_up":     packet.get("follow_up") or [],
            "verdict":       packet.get("verdict"),
            "verdict_reasoning": packet.get("verdict_reasoning"),
            "action":        packet.get("action", "REVIEW"),
            "confidence":    _safe_float(packet.get("confidence", 0.0)),
            "action_reason": packet.get("action_reason", ""),
            "unsure":        bool(packet.get("unsure", False)),
            "opened_at":     None,
            "decision_state": None,
            "decided_at":    None,
            "follow_up_sent_at": packet.get("follow_up_sent_at"),
            "awaiting_reply":    bool(packet.get("awaiting_reply", False)),
            "reply_count":       int(packet.get("reply_count", 0)),
            "raw_email_id":  raw_email_id,
            "cloud_pitch_id": cloud_pitch_id,
        }
        with open(HISTORY_FILE, "a") as f:
            f.write(json.dumps(record) + "\n")
        return record


def mark_pitch_opened(real_index: int, item: dict) -> dict:
    if item.get("opened_at") is None:
        item["opened_at"] = datetime.now().isoformat()
        update_history_item(real_index, item)
    return item


def history_bucket(item: dict) -> str:
    if item.get("decision_state"):
        return "decided"
    if item.get("opened_at"):
        return "seen"
    return "new"


# ── Ranking ──────────────────────────────────────────────────────────────────

_ACTION_RANK = {"ESCALATE": 0, "REVIEW": 1, "PASS": 2}


def build_ranked_items(items: list[dict]) -> list[tuple[int, dict]]:
    """Return (real_index, item) sorted: undecided first by action + confidence, decided last."""
    queue, done = [], []
    for i, item in enumerate(items):
        (done if history_bucket(item) == "decided" else queue).append((i, item))

    queue.sort(key=lambda e: (
        _ACTION_RANK.get(e[1].get("action", "PASS"), 9),
        -float(e[1].get("confidence", 0)),
        e[1].get("timestamp", ""),
    ))
    done.sort(key=lambda e: e[1].get("decided_at") or e[1].get("timestamp", ""), reverse=True)
    return queue + done
