#!/usr/bin/env python3
# Copyright (c) 2026 PULSE Contributors. Proprietary. All rights reserved.
# See LICENSE file for details.
"""
Conflict Resolver: Detect and resolve multi-agent file conflicts.

Monitors recent file modifications and detects when multiple agents
write to the same file within a short window. Auto-merges brain files,
escalates non-brain conflicts to the task board.

Usage:
    python conflict_resolver.py --scan         # Scan for recent conflicts
    python conflict_resolver.py --resolve      # Auto-resolve brain file conflicts
    python conflict_resolver.py --report       # Show conflict history

Dependencies: Python stdlib only (Law 4)
"""

import json
import os
import sys
import time
from datetime import datetime, timezone, timedelta
from pathlib import Path

SCRIPT_DIR = Path(__file__).resolve().parent
ROOT_DIR = SCRIPT_DIR.parent.parent.parent
STATE_DIR = ROOT_DIR / "state"
CONFLICT_LOG = STATE_DIR / "conflict_log.json"
HIPPOCAMPUS_DIR = ROOT_DIR / "Hippocampus"

# Files commonly modified by multiple agents
WATCHED_PATHS = [
    HIPPOCAMPUS_DIR / "Lessons" / "auto_lessons.md",
    HIPPOCAMPUS_DIR / "Failures" / "auto_fails.md",
    HIPPOCAMPUS_DIR / "Patterns" / "auto_patterns.md",
    HIPPOCAMPUS_DIR / "Evidence" / "Sessions" / "agent_sessions.json",
    HIPPOCAMPUS_DIR / "Evidence" / "Sessions" / "current_session.json",
    HIPPOCAMPUS_DIR / "Evidence" / "Decisions" / "auto_decisions.json",
    STATE_DIR / "task_board.json",
    STATE_DIR / "agent_registry.json",
]

# Conflict window: if 2+ agents write within this many seconds, it's a conflict
CONFLICT_WINDOW_S = 10


def _load_conflict_log() -> list[dict]:
    """Load conflict history."""
    if not CONFLICT_LOG.exists():
        return []
    try:
        data = json.loads(CONFLICT_LOG.read_text(encoding="utf-8"))
        return data if isinstance(data, list) else []
    except (json.JSONDecodeError, Exception):
        return []


def _save_conflict_log(entries: list[dict]) -> None:
    """Save conflict log (keep last 200 entries)."""
    CONFLICT_LOG.parent.mkdir(parents=True, exist_ok=True)
    entries = entries[-200:]
    CONFLICT_LOG.write_text(json.dumps(entries, indent=1), encoding="utf-8")


def scan_conflicts() -> list[dict]:
    """
    Scan watched files for recent modification conflicts.

    Checks mtime of watched files and cross-references with
    agent_sessions.json to detect concurrent writes.
    """
    conflicts = []
    now = time.time()

    # Load recent sessions to find who wrote what
    sessions_file = HIPPOCAMPUS_DIR / "Evidence" / "agent_sessions.json"
    recent_sessions = []
    if sessions_file.exists():
        try:
            data = json.loads(sessions_file.read_text(encoding="utf-8"))
            if isinstance(data, list):
                for s in data:
                    ts = s.get("timestamp", "")
                    if ts:
                        try:
                            dt = datetime.fromisoformat(ts.replace("Z", "+00:00"))
                            age_s = (datetime.now(timezone.utc) - dt).total_seconds()
                            if age_s < 3600:  # Last hour
                                recent_sessions.append(s)
                        except (ValueError, TypeError):
                            pass
        except Exception:
            pass

    # Check for overlapping writes
    for filepath in WATCHED_PATHS:
        if not filepath.exists():
            continue

        mtime = filepath.stat().st_mtime
        age_s = now - mtime

        if age_s > 300:  # Only check files modified in last 5 min
            continue

        # Find sessions that touched this file recently
        touching_agents = set()
        for session in recent_sessions:
            files = session.get("files_touched", [])
            agent = session.get("agent", "unknown")
            for f in files:
                if filepath.name in f or f in str(filepath):
                    touching_agents.add(agent)

        if len(touching_agents) >= 2:
            conflicts.append({
                "file": str(filepath.relative_to(ROOT_DIR)),
                "agents": list(touching_agents),
                "mtime": datetime.fromtimestamp(mtime, tz=timezone.utc).isoformat(),
                "age_seconds": round(age_s),
                "is_brain_file": "Hippocampus" in str(filepath),
                "auto_resolvable": "Hippocampus" in str(filepath),
            })

    return conflicts


def resolve_brain_conflicts(conflicts: list[dict]) -> list[dict]:
    """
    Auto-resolve brain file conflicts.

    Brain files (lessons, fails, patterns) are append-only, so
    concurrent writes are safe if using atomic operations.
    Non-brain conflicts are logged for manual resolution.
    """
    resolved = []
    escalated = []

    for conflict in conflicts:
        if conflict.get("auto_resolvable"):
            # Brain files are append-only — just log it
            conflict["resolution"] = "auto-safe (append-only file, using atomic ops)"
            conflict["resolved_at"] = datetime.now(timezone.utc).isoformat()
            resolved.append(conflict)
        else:
            # Non-brain file — escalate
            conflict["resolution"] = "NEEDS_MANUAL_REVIEW"
            conflict["resolved_at"] = None
            escalated.append(conflict)

    # Log all conflicts
    log = _load_conflict_log()
    log.extend(resolved)
    log.extend(escalated)
    _save_conflict_log(log)

    return resolved + escalated


def format_report() -> str:
    """Format conflict log as human-readable report."""
    log = _load_conflict_log()
    if not log:
        return "No conflicts recorded."

    lines = [
        f"Conflict Report ({len(log)} total conflicts)",
        "=" * 50,
        "",
    ]

    # Group by file
    by_file: dict[str, list] = {}
    for entry in log:
        f = entry.get("file", "unknown")
        by_file.setdefault(f, []).append(entry)

    for filepath, entries in sorted(by_file.items()):
        lines.append(f"{filepath} ({len(entries)} conflicts)")
        for e in entries[-3:]:  # Show last 3
            agents = ", ".join(e.get("agents", []))
            resolution = e.get("resolution", "?")
            ts = e.get("mtime", "?")[:19]
            lines.append(f"  [{ts}] Agents: {agents} → {resolution}")
        lines.append("")

    # Summary
    auto_resolved = sum(1 for e in log if "auto-safe" in (e.get("resolution") or ""))
    manual = sum(1 for e in log if "MANUAL" in (e.get("resolution") or ""))
    lines.append(f"Auto-resolved: {auto_resolved} | Needs review: {manual}")

    return "\n".join(lines)


if __name__ == "__main__":
    if "--scan" in sys.argv:
        conflicts = scan_conflicts()
        if conflicts:
            print(f"Found {len(conflicts)} potential conflicts:")
            for c in conflicts:
                print(f"  {c['file']}: {', '.join(c['agents'])} ({c['age_seconds']}s ago)")
        else:
            print("No conflicts detected.")

    elif "--resolve" in sys.argv:
        conflicts = scan_conflicts()
        if conflicts:
            results = resolve_brain_conflicts(conflicts)
            for r in results:
                print(f"  {r['file']}: {r['resolution']}")
        else:
            print("No conflicts to resolve.")

    elif "--report" in sys.argv:
        print(format_report())

    else:
        print("Usage:")
        print("  python conflict_resolver.py --scan     # Detect conflicts")
        print("  python conflict_resolver.py --resolve   # Auto-resolve")
        print("  python conflict_resolver.py --report    # Show history")
