from __future__ import annotations

import hashlib
import json
import time
from collections import defaultdict
from pathlib import Path

from .entities import entity_type
from .models import AnomalyFinding


def build_incidents(anomalies: list[AnomalyFinding], field_deviations: dict[str, list[dict[str, object]]], correlations: list[dict[str, object]]) -> list[dict[str, object]]:
    """Build investigation units from any log source, not only network source IPs."""
    groups: dict[str, dict[str, object]] = defaultdict(lambda: {"score": 0, "evidence": [], "fields": [], "correlations": [], "timeline": []})
    entity_labels = {
        str(item.get("entity") or item.get("source_ip")): {
            "entity_display": str(item.get("entity_display") or item.get("entity") or item.get("source_ip") or ""),
            "entity_label": str(item.get("entity_label") or item.get("entity_display") or item.get("entity") or item.get("source_ip") or ""),
            "entity_detail": str(item.get("entity_detail") or ""),
        }
        for item in correlations
        if item.get("entity") or item.get("source_ip")
    }
    for anomaly in anomalies:
        group = groups[anomaly.subject]
        group["score"] = max(int(group["score"]), anomaly.score)
        group["evidence"].extend(anomaly.reasons)
    for entity, deviations in field_deviations.items():
        group = groups[entity]
        group["fields"].extend(deviations)
        active = [item for item in deviations if item.get("feedback") not in {"expected", "false_positive"}]
        group["score"] = min(100, int(group["score"]) + min(30, sum(int(item.get("score", 0)) for item in active)))
        group["evidence"].extend(str(item.get("reason", "")) for item in active)
        for item in active:
            group["timeline"].extend(item.get("sample_events", []))
    for correlation in correlations:
        entity = str(correlation.get("entity") or correlation.get("source_ip") or "")
        if not entity:
            continue
        group = groups[entity]
        group["correlations"].append(correlation)
        group["score"] = min(100, int(group["score"]) + 10 + min(15, int(correlation.get("security_events", 0)) * 2))
        group["evidence"].append(f"observed across {len(correlation.get('streams', []))} streams")
        group["timeline"].extend(correlation.get("samples", []))

    incidents = []
    for entity, group in groups.items():
        if not group["evidence"]:
            continue
        score = int(group["score"])
        severity = "critical" if score >= 85 else "high" if score >= 60 else "medium" if score >= 35 else "low"
        streams = sorted({stream for item in group["correlations"] for stream in item.get("streams", [])} | {str(item.get("stream_name") or item.get("stream_title") or item.get("stream_id", "")) for item in group["fields"] if item.get("stream_id") or item.get("stream_name") or item.get("stream_title")})
        timeline = sorted(group["timeline"], key=lambda item: str(item.get("timestamp", "")))[:20]
        incident_id = hashlib.sha256(json.dumps({"entity": entity, "streams": streams}, sort_keys=True).encode("utf-8")).hexdigest()[:16]
        incidents.append({
            "id": incident_id,
            "entity": entity,
            **entity_labels.get(entity, {"entity_display": entity, "entity_label": entity, "entity_detail": ""}),
            "entity_type": entity_type(entity),
            "score": score,
            "severity": severity,
            "evidence": list(dict.fromkeys(str(item) for item in group["evidence"] if item))[:8],
            "field_deviations": len(group["fields"]),
            "correlated_streams": streams,
            "timeline": timeline,
            "first_seen": timeline[0].get("timestamp", "") if timeline else "",
            "last_seen": timeline[-1].get("timestamp", "") if timeline else "",
        })
    return sorted(incidents, key=lambda item: int(item["score"]), reverse=True)


class IncidentStore:
    def __init__(self, path: str = "state/signalscope-incidents.json") -> None:
        self.path = Path(path)

    def entries(self) -> dict[str, dict[str, object]]:
        try:
            items = json.loads(self.path.read_text(encoding="utf-8"))
        except (FileNotFoundError, json.JSONDecodeError):
            return {}
        return {str(key): value for key, value in items.items() if isinstance(value, dict)} if isinstance(items, dict) else {}

    def apply(self, incidents: list[dict[str, object]]) -> list[dict[str, object]]:
        states = self.entries()
        now = int(time.time())
        changed = False
        for incident in incidents:
            incident_id = str(incident.get("id", ""))
            if not incident_id:
                continue
            state = states.get(incident_id)
            if not state:
                state = {"status": "open", "note": "", "created_at": now, "updated_at": now}
                states[incident_id] = state
                changed = True
            incident["lifecycle_status"] = str(state.get("status", "open"))
            incident["note"] = str(state.get("note", ""))
            incident["updated_at"] = int(state.get("updated_at", 0) or 0)
        if changed:
            self._write(states)
        return incidents

    def update(self, incident_id: str, status: str, note: str = "") -> dict[str, object]:
        status = status.lower()
        if status not in {"open", "acknowledged", "resolved"}:
            raise ValueError("invalid incident status")
        states = self.entries()
        current = states.get(incident_id, {"created_at": int(time.time())})
        entry = {**current, "status": status, "note": note, "updated_at": int(time.time())}
        states[incident_id] = entry
        self._write(states)
        return {"id": incident_id, **entry}

    def _write(self, states: dict[str, dict[str, object]]) -> None:
        self.path.parent.mkdir(parents=True, exist_ok=True)
        self.path.write_text(json.dumps(states, indent=2, sort_keys=True), encoding="utf-8")
