"""
Phase 98: Incident Management for Platform Outages & Issues.

Full lifecycle incident tracking: create, investigate, assign, resolve,
and postmortem. Storage: state/broker/incidents/
"""

import json
import time
import uuid
from datetime import datetime, timezone
from pathlib import Path
from typing import Dict, List, Optional

from brynq.server import BROKER_DIR, _write_json_sync, _read_jsonl_sync, _append_jsonl_sync

# ── Paths ──────────────────────────────────────────────────────────────────

INCIDENTS_DIR = BROKER_DIR / "incidents"
INDEX_FILE = INCIDENTS_DIR / "index.jsonl"

INCIDENTS_DIR.mkdir(parents=True, exist_ok=True)

VALID_SEVERITIES = ("critical", "high", "medium", "low")
VALID_STATUSES = ("open", "investigating", "identified", "monitoring", "resolved")


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

def _incident_path(incident_id: str) -> Path:
    return INCIDENTS_DIR / f"{incident_id}.json"


def _load_incident(incident_id: str) -> Optional[dict]:
    p = _incident_path(incident_id)
    if not p.exists():
        return None
    try:
        return json.loads(p.read_text("utf-8"))
    except (json.JSONDecodeError, OSError):
        return None


def _save_incident(incident: dict) -> None:
    _write_json_sync(_incident_path(incident["incident_id"]), incident)


# ── Public API ─────────────────────────────────────────────────────────────

def create_incident(title: str, severity: str, description: str = "",
                    reporter_id: Optional[str] = None) -> dict:
    """Create a new incident. Severity: critical, high, medium, low."""
    if severity not in VALID_SEVERITIES:
        raise ValueError(f"Invalid severity '{severity}'. Must be one of {VALID_SEVERITIES}")
    now = datetime.now(timezone.utc).isoformat()
    incident_id = "INC-" + uuid.uuid4().hex[:8]
    incident = {
        "incident_id": incident_id,
        "title": title,
        "severity": severity,
        "description": description,
        "status": "open",
        "reporter_id": reporter_id,
        "assigned_to": None,
        "created_at": now,
        "updated_at": now,
        "resolved_at": None,
        "resolution": None,
        "root_cause": None,
        "timeline": [
            {"action": "created", "timestamp": now, "message": f"Incident created: {title}"}
        ],
    }
    _save_incident(incident)
    _append_jsonl_sync(INDEX_FILE, {
        "incident_id": incident_id, "title": title, "severity": severity,
        "status": "open", "created_at": now,
    })
    return incident


def update_incident(incident_id: str, status: Optional[str] = None,
                    update_message: Optional[str] = None) -> dict:
    """Update an incident's status and/or add a timeline message."""
    incident = _load_incident(incident_id)
    if incident is None:
        raise KeyError(f"Incident '{incident_id}' not found")
    now = datetime.now(timezone.utc).isoformat()
    if status is not None:
        if status not in VALID_STATUSES:
            raise ValueError(f"Invalid status '{status}'. Must be one of {VALID_STATUSES}")
        if incident["status"] == "resolved" and status != "resolved":
            raise ValueError("Cannot reopen a resolved incident")
        incident["status"] = status
        incident["timeline"].append({
            "action": "status_change",
            "timestamp": now,
            "message": f"Status changed to {status}",
        })
    if update_message:
        incident["timeline"].append({
            "action": "update",
            "timestamp": now,
            "message": update_message,
        })
    incident["updated_at"] = now
    _save_incident(incident)
    return incident


def assign_incident(incident_id: str, agent_id: str) -> dict:
    """Assign an agent to investigate an incident."""
    incident = _load_incident(incident_id)
    if incident is None:
        raise KeyError(f"Incident '{incident_id}' not found")
    now = datetime.now(timezone.utc).isoformat()
    incident["assigned_to"] = agent_id
    incident["updated_at"] = now
    incident["timeline"].append({
        "action": "assigned",
        "timestamp": now,
        "message": f"Assigned to agent {agent_id}",
    })
    _save_incident(incident)
    return incident


def resolve_incident(incident_id: str, resolution: str,
                     root_cause: str = "") -> dict:
    """Close an incident with resolution and optional root cause."""
    incident = _load_incident(incident_id)
    if incident is None:
        raise KeyError(f"Incident '{incident_id}' not found")
    if incident["status"] == "resolved":
        raise ValueError(f"Incident '{incident_id}' is already resolved")
    now = datetime.now(timezone.utc).isoformat()
    incident["status"] = "resolved"
    incident["resolution"] = resolution
    incident["root_cause"] = root_cause or None
    incident["resolved_at"] = now
    incident["updated_at"] = now
    incident["timeline"].append({
        "action": "resolved",
        "timestamp": now,
        "message": f"Resolved: {resolution}",
    })
    _save_incident(incident)
    return incident


def get_incident(incident_id: str) -> dict:
    """Retrieve a single incident."""
    incident = _load_incident(incident_id)
    if incident is None:
        raise KeyError(f"Incident '{incident_id}' not found")
    return incident


def list_incidents(status: Optional[str] = None,
                   severity: Optional[str] = None) -> List[dict]:
    """List all incidents with optional filters."""
    results = []
    if not INCIDENTS_DIR.exists():
        return results
    for f in INCIDENTS_DIR.iterdir():
        if not f.name.endswith(".json"):
            continue
        try:
            inc = json.loads(f.read_text("utf-8"))
        except (json.JSONDecodeError, OSError):
            continue
        if status and inc.get("status") != status:
            continue
        if severity and inc.get("severity") != severity:
            continue
        results.append(inc)
    results.sort(key=lambda x: x.get("created_at", ""), reverse=True)
    return results


def get_incident_timeline(incident_id: str) -> List[dict]:
    """Return all updates for an incident chronologically."""
    incident = _load_incident(incident_id)
    if incident is None:
        raise KeyError(f"Incident '{incident_id}' not found")
    return incident.get("timeline", [])


def get_incident_stats() -> dict:
    """Open/resolved counts, avg resolution time, breakdown by severity."""
    incidents = list_incidents()
    open_count = sum(1 for i in incidents if i.get("status") != "resolved")
    resolved_count = sum(1 for i in incidents if i.get("status") == "resolved")
    by_severity: Dict[str, int] = {s: 0 for s in VALID_SEVERITIES}
    resolution_times: List[float] = []
    for inc in incidents:
        sev = inc.get("severity", "low")
        if sev in by_severity:
            by_severity[sev] += 1
        if inc.get("resolved_at") and inc.get("created_at"):
            try:
                created = datetime.fromisoformat(inc["created_at"])
                resolved = datetime.fromisoformat(inc["resolved_at"])
                resolution_times.append((resolved - created).total_seconds())
            except (ValueError, TypeError):
                pass
    avg_resolution = (
        round(sum(resolution_times) / len(resolution_times), 2)
        if resolution_times else 0
    )
    return {
        "total": len(incidents),
        "open": open_count,
        "resolved": resolved_count,
        "avg_resolution_seconds": avg_resolution,
        "by_severity": by_severity,
    }


def get_postmortem(incident_id: str) -> dict:
    """Generate a postmortem summary for a resolved incident."""
    incident = _load_incident(incident_id)
    if incident is None:
        raise KeyError(f"Incident '{incident_id}' not found")
    timeline = incident.get("timeline", [])
    duration = None
    if incident.get("resolved_at") and incident.get("created_at"):
        try:
            created = datetime.fromisoformat(incident["created_at"])
            resolved = datetime.fromisoformat(incident["resolved_at"])
            duration = round((resolved - created).total_seconds(), 2)
        except (ValueError, TypeError):
            pass
    return {
        "incident_id": incident["incident_id"],
        "title": incident["title"],
        "severity": incident["severity"],
        "status": incident["status"],
        "root_cause": incident.get("root_cause") or "Not identified",
        "resolution": incident.get("resolution") or "Pending",
        "duration_seconds": duration,
        "timeline_entries": len(timeline),
        "assigned_to": incident.get("assigned_to"),
        "created_at": incident.get("created_at"),
        "resolved_at": incident.get("resolved_at"),
    }
