from __future__ import annotations

import json
import os
import time
import uuid
from pathlib import Path
from typing import Any

SYSTEM_STATE_SCHEMA_VERSION = "system.state.v1"
SYSTEM_EVENT_SCHEMA_VERSION = "system.event.v1"
SYSTEM_CONTRACT_VERSION = "1.0.0"
SYSTEM_EVENT_LOG_MAX_BYTES = 8_000_000
SYSTEM_EVENT_LOG_MAX_LINES = 20_000


def _now_iso() -> str:
    return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())


def _json_load(path: Path) -> dict[str, Any]:
    if not path.exists():
        return {}
    try:
        return json.loads(path.read_text(encoding="utf-8"))
    except Exception:
        return {}


def _atomic_write(path: Path, payload: dict[str, Any]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    tmp = path.with_name(f"{path.name}.{os.getpid()}.{time.time_ns()}.tmp")
    tmp.write_text(json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8")
    tmp.replace(path)


def _append_jsonl(path: Path, payload: dict[str, Any]) -> None:
    path.parent.mkdir(parents=True, exist_ok=True)
    with open(path, "a", encoding="utf-8") as f:
        f.write(json.dumps(payload, ensure_ascii=False) + "\n")
    _trim_jsonl(path)


def _trim_jsonl(path: Path, max_bytes: int = SYSTEM_EVENT_LOG_MAX_BYTES, max_lines: int = SYSTEM_EVENT_LOG_MAX_LINES) -> None:
    try:
        if not path.exists() or path.stat().st_size <= max_bytes:
            return
        lines = path.read_text(encoding="utf-8").splitlines()
        if len(lines) > max_lines:
            lines = lines[-max_lines:]
        path.write_text(("\n".join(lines) + "\n") if lines else "", encoding="utf-8")
    except Exception:
        return


def _default_state(tenant_id: str) -> dict[str, Any]:
    return {
        "contract_version": SYSTEM_CONTRACT_VERSION,
        "state_schema_version": SYSTEM_STATE_SCHEMA_VERSION,
        "event_schema_version": SYSTEM_EVENT_SCHEMA_VERSION,
        "tenant_id": tenant_id or "default",
        "generated_at": _now_iso(),
        "updated_at": _now_iso(),
        "last_event_id": "",
        "sections": {
            "runtime": {},
            "governance": {},
            "goals": {},
            "evolution_evidence": {},
            "autonomy_loop": {},
        },
    }


def _state_and_event_paths(root: Path, tenant_id: str) -> tuple[Path, Path]:
    try:
        from tenant_manager import tenant_paths
    except ModuleNotFoundError:
        from src.tenant_manager import tenant_paths
    paths = tenant_paths(root, tenant_id)
    runtime_path = paths["runtime"]
    log_dir = runtime_path.parent.parent / "logs"
    state_path = log_dir / "system_state.json"
    event_path = paths["audit"].parent / "system_events.ndjson"
    return state_path, event_path


def load_system_state(root: Path, tenant_id: str = "default") -> dict[str, Any]:
    state_path, _ = _state_and_event_paths(root, tenant_id)
    payload = _json_load(state_path)
    if not isinstance(payload, dict) or not payload:
        payload = _default_state(tenant_id)
        _atomic_write(state_path, payload)
    if "sections" not in payload or not isinstance(payload.get("sections"), dict):
        payload["sections"] = _default_state(tenant_id)["sections"]
    return payload


def append_system_event(
    root: Path,
    tenant_id: str,
    source: str,
    event_type: str,
    payload: dict[str, Any],
    decision_id: str = "",
) -> dict[str, Any]:
    _, event_path = _state_and_event_paths(root, tenant_id)
    event = {
        "event_id": f"evt_{uuid.uuid4().hex[:16]}",
        "ts": _now_iso(),
        "tenant_id": tenant_id or "default",
        "source": source,
        "event_type": event_type,
        "contract_version": SYSTEM_CONTRACT_VERSION,
        "event_schema_version": SYSTEM_EVENT_SCHEMA_VERSION,
        "decision_id": decision_id,
        "payload": payload if isinstance(payload, dict) else {"value": payload},
    }
    _append_jsonl(event_path, event)
    return event


def append_trade_event(
    root: Path,
    tenant_id: str,
    payload: dict[str, Any],
    source: str = "bridge.trade_feedback",
) -> dict[str, Any]:
    trade_payload = payload if isinstance(payload, dict) else {"value": payload}
    decision_id = str(
        trade_payload.get("decision_id")
        or trade_payload.get("trace_id")
        or trade_payload.get("signal_id")
        or ""
    ).strip()
    return append_system_event(
        root=root,
        tenant_id=tenant_id,
        source=source,
        event_type="trade_feedback",
        payload=trade_payload,
        decision_id=decision_id,
    )


def update_system_section(
    root: Path,
    tenant_id: str,
    section: str,
    patch: dict[str, Any],
    source: str,
    event_type: str,
    decision_id: str = "",
) -> dict[str, Any]:
    state_path, _ = _state_and_event_paths(root, tenant_id)
    state = load_system_state(root, tenant_id)
    sections = state.get("sections", {})
    if not isinstance(sections, dict):
        sections = {}
        state["sections"] = sections
    current = sections.get(section, {})
    if not isinstance(current, dict):
        current = {}
    if isinstance(patch, dict):
        current.update(patch)
    sections[section] = current
    evt = append_system_event(
        root=root,
        tenant_id=tenant_id,
        source=source,
        event_type=event_type,
        payload={"section": section, "patch": patch if isinstance(patch, dict) else {}},
        decision_id=decision_id,
    )
    state["last_event_id"] = evt["event_id"]
    state["updated_at"] = _now_iso()
    _atomic_write(state_path, state)
    return state
