"""Phase 79: MCP Microservice — Governance & Guardrails.

Constitution enforcement, file locking, proposals, violations, and pardons.
"""

import asyncio
import json
import uuid

from mcp.types import TextContent, Tool, ToolAnnotations

_SAFE_WRITE = ToolAnnotations(readOnlyHint=False, destructiveHint=False, idempotentHint=False, openWorldHint=False)
_SAFE_READ = ToolAnnotations(readOnlyHint=True, destructiveHint=False, idempotentHint=True, openWorldHint=False)

TOOLS = [
    Tool(
        name="broker_claim_file",
        description="Claim exclusive edit rights on a file. Other agents cannot edit it until you release it. Prevents merge conflicts.",
        inputSchema={
            "type": "object",
            "properties": {
                "filepath": {"type": "string", "description": "Path to the file to lock (e.g. 'src/brynq/server.py')"},
                "reason": {"type": "string", "description": "Why you need this file (e.g. 'adding guardrail tools')", "default": ""},
            },
            "required": ["filepath"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="broker_release_file",
        description="Release a file lock so other agents can edit it. Only the lock holder can release.",
        inputSchema={
            "type": "object",
            "properties": {
                "filepath": {"type": "string", "description": "Path to the file to unlock"},
            },
            "required": ["filepath"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="broker_check_lock",
        description="Check if a file is locked by another agent before editing. Always call this before editing shared files.",
        inputSchema={
            "type": "object",
            "properties": {
                "filepath": {"type": "string", "description": "Path to check"},
            },
            "required": ["filepath"],
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="broker_list_locks",
        description="List all active file locks across all agents. Shows who owns what.",
        inputSchema={"type": "object", "properties": {}},
        annotations=_SAFE_READ,
    ),
    Tool(
        name="broker_propose",
        description="Submit a structured proposal when agents disagree. Use instead of arguing. Other agents reply with their preferred option number.",
        inputSchema={
            "type": "object",
            "properties": {
                "channel": {"type": "string", "description": "Channel to post proposal", "default": "general"},
                "title": {"type": "string", "description": "Proposal title"},
                "description": {"type": "string", "description": "What needs to be decided"},
                "options": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "List of options to vote on",
                },
            },
            "required": ["title", "description", "options"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="broker_my_standing",
        description="Check your constitutional standing — violations, enforcement level, and status.",
        inputSchema={"type": "object", "properties": {}},
        annotations=_SAFE_READ,
    ),
    Tool(
        name="broker_violations",
        description="View detailed violation history for a session.",
        inputSchema={
            "type": "object",
            "properties": {
                "session_id": {"type": "string", "description": "Session to check (omit for your own)"},
            },
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="broker_pardon",
        description="Pardon a session's violations (admin action). Clears their enforcement level.",
        inputSchema={
            "type": "object",
            "properties": {
                "session_id": {"type": "string", "description": "Session ID to pardon"},
            },
            "required": ["session_id"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="broker_constitution",
        description="Display a summary of the Brynq Constitution — the laws all agents must follow.",
        inputSchema={"type": "object", "properties": {}},
        annotations=_SAFE_READ,
    ),
    Tool(
        name="broker_submit_for_review",
        description="Submit work for peer review before it can be merged or shipped.",
        inputSchema={
            "type": "object",
            "properties": {
                "task_id": {"type": "string", "description": "Task ID to submit for review"},
                "summary": {"type": "string", "description": "Summary of changes made"},
            },
            "required": ["task_id", "summary"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="broker_accept_review",
        description="Accept a peer review assignment.",
        inputSchema={
            "type": "object",
            "properties": {
                "task_id": {"type": "string", "description": "Task ID to review"},
            },
            "required": ["task_id"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="broker_approve_task",
        description="Approve a task after peer review.",
        inputSchema={
            "type": "object",
            "properties": {
                "task_id": {"type": "string", "description": "Task ID to approve"},
                "comments": {"type": "string", "description": "Review comments", "default": ""},
            },
            "required": ["task_id"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="broker_reject_task",
        description="Reject a task during peer review with feedback.",
        inputSchema={
            "type": "object",
            "properties": {
                "task_id": {"type": "string", "description": "Task ID to reject"},
                "reason": {"type": "string", "description": "Why the task is being rejected"},
            },
            "required": ["task_id", "reason"],
        },
        annotations=_SAFE_WRITE,
    ),
]


async def _handle_claim_file(args: dict) -> list[TextContent]:
    from brynq.guardrails import claim_file
    filepath = args.get("filepath", "")
    reason = args.get("reason", "")
    success, msg = await asyncio.to_thread(claim_file, filepath, reason)
    return [TextContent(type="text", text=msg)]


async def _handle_release_file(args: dict) -> list[TextContent]:
    from brynq.guardrails import release_file
    filepath = args.get("filepath", "")
    success, msg = await asyncio.to_thread(release_file, filepath)
    return [TextContent(type="text", text=msg)]


async def _handle_check_lock(args: dict) -> list[TextContent]:
    from brynq.guardrails import check_file_lock
    filepath = args.get("filepath", "")
    lock = await asyncio.to_thread(check_file_lock, filepath)
    if not lock:
        return [TextContent(type="text", text=f"{filepath} is UNLOCKED — safe to edit")]
    holder = lock.get("session_name", "unknown")
    age = lock.get("age_seconds", 0)
    reason = lock.get("reason", "")
    return [TextContent(type="text", text=f"{filepath} is LOCKED by {holder} ({age}s ago). Reason: {reason}")]


async def _handle_list_locks(args: dict) -> list[TextContent]:
    from brynq.guardrails import get_all_locks
    locks = await asyncio.to_thread(get_all_locks)
    if not locks:
        return [TextContent(type="text", text="No active file locks.")]
    lines = ["Active file locks:"]
    for lk in locks:
        mine = " <-- YOU" if lk.get("is_mine") else ""
        lines.append(f"  {lk['filepath']} — {lk['session_name']}@{lk.get('platform','?')} ({lk['age_seconds']}s){mine}")
    return [TextContent(type="text", text="\n".join(lines))]


async def _handle_propose(args: dict) -> list[TextContent]:
    from brynq.guardrails import format_proposal
    from brynq.server import SESSION_ID, SESSION_NAME, PLATFORM, CHANNELS_DIR, _append_jsonl
    from datetime import datetime, timezone

    channel = args.get("channel", "general")
    title = args.get("title", "")
    description = args.get("description", "")
    options = args.get("options", [])
    proposal = format_proposal(title, description, options)

    entry = {
        "id": uuid.uuid4().hex[:12],
        "timestamp": datetime.now(timezone.utc).isoformat(),
        "session_id": SESSION_ID,
        "session_name": SESSION_NAME,
        "platform": PLATFORM,
        "channel": channel,
        "msg_type": "proposal",
        "message": proposal,
    }
    await _append_jsonl(CHANNELS_DIR / f"{channel}.jsonl", entry)
    return [TextContent(type="text", text=f"Proposal posted to #{channel}: {title}")]


async def _handle_my_standing(args: dict) -> list[TextContent]:
    from brynq.enforcer import get_violation_summary
    summary = await asyncio.to_thread(get_violation_summary)
    status = summary["status"]
    total = summary["total_violations"]
    level = summary["enforcement_level"]
    icon = {"good_standing": "OK", "cooldown": "COOLDOWN", "blocked": "BLOCKED"}.get(status, "?")
    lines = [f"Constitutional Standing: {icon}", f"  Violations: {total}", f"  Level: {level}"]
    if summary["recent_violations"]:
        lines.append("  Recent:")
        for v in summary["recent_violations"][-3:]:
            lines.append(f"    Law {v['law']}: {v['description'][:60]}")
    return [TextContent(type="text", text="\n".join(lines))]


async def _handle_violations(args: dict) -> list[TextContent]:
    from brynq.enforcer import get_violation_summary
    from brynq.server import SESSION_ID
    sid = args.get("session_id") or SESSION_ID
    summary = await asyncio.to_thread(get_violation_summary, sid)
    return [TextContent(type="text", text=json.dumps(summary, indent=2, default=str))]


async def _handle_pardon(args: dict) -> list[TextContent]:
    from brynq.enforcer import pardon
    sid = args.get("session_id", "")
    if not sid:
        return [TextContent(type="text", text="Error: session_id required")]
    result = await asyncio.to_thread(pardon, sid)
    return [TextContent(type="text", text=f"PARDONED: {sid} cleared of {result['pardoned']} violations. Status: {result['status']}")]


async def _handle_constitution(args: dict) -> list[TextContent]:
    summary = """BRYNQ CONSTITUTION v1.0 — 45 Laws, 10 Articles

KEY LAWS EVERY AGENT MUST FOLLOW:
  Law 1:  True Identity — no impersonation
  Law 5:  Clarity Over Volume — concise, actionable messages
  Law 6:  Rate Limits Are Sacred — 10 msgs/60s
  Law 8:  No Arguments — use broker_propose
  Law 10: Message Length Limit — 5,000 chars max
  Law 11: Claim Before Edit — broker_claim_file first
  Law 16: Clear Task Specs — unambiguous instructions
  Law 17: Accept Before Working — broker_accept_task first
  Law 20: One Task At A Time — complete before accepting more
  Law 26: Human Supremacy — human overrides everything
  Law 28: No Credential Sharing — use env vars
  Law 37: No Platform Discrimination — skills > platform

ENFORCEMENT:
  Warning (1-2) → Cooldown (3-5) → Escalation (6-8) → Disconnect (12+)

Full text: CONSTITUTION.md"""
    return [TextContent(type="text", text=summary)]


async def _handle_submit_for_review(args: dict) -> list[TextContent]:
    from brynq.tasks import submit_for_review
    task_id = args.get("task_id", "")
    summary = args.get("summary", "")
    try:
        result = await asyncio.to_thread(submit_for_review, task_id, summary)
        return [TextContent(type="text", text=f"Task {task_id} submitted for peer review.")]
    except Exception as e:
        return [TextContent(type="text", text=f"Error: {str(e)}")]


async def _handle_accept_review(args: dict) -> list[TextContent]:
    from brynq.tasks import accept_review
    task_id = args.get("task_id", "")
    try:
        result = await asyncio.to_thread(accept_review, task_id)
        return [TextContent(type="text", text=f"Review accepted for task {task_id}.")]
    except Exception as e:
        return [TextContent(type="text", text=f"Error: {str(e)}")]


async def _handle_approve_task(args: dict) -> list[TextContent]:
    from brynq.tasks import approve_task
    task_id = args.get("task_id", "")
    comments = args.get("comments", "")
    try:
        result = await asyncio.to_thread(approve_task, task_id, comments)
        return [TextContent(type="text", text=f"Task {task_id} APPROVED.")]
    except Exception as e:
        return [TextContent(type="text", text=f"Error: {str(e)}")]


async def _handle_reject_task(args: dict) -> list[TextContent]:
    from brynq.tasks import reject_task
    task_id = args.get("task_id", "")
    reason = args.get("reason", "")
    try:
        result = await asyncio.to_thread(reject_task, task_id, reason)
        return [TextContent(type="text", text=f"Task {task_id} REJECTED: {reason}")]
    except Exception as e:
        return [TextContent(type="text", text=f"Error: {str(e)}")]


HANDLERS = {
    "broker_claim_file": _handle_claim_file,
    "broker_release_file": _handle_release_file,
    "broker_check_lock": _handle_check_lock,
    "broker_list_locks": _handle_list_locks,
    "broker_propose": _handle_propose,
    "broker_my_standing": _handle_my_standing,
    "broker_violations": _handle_violations,
    "broker_pardon": _handle_pardon,
    "broker_constitution": _handle_constitution,
    "broker_submit_for_review": _handle_submit_for_review,
    "broker_accept_review": _handle_accept_review,
    "broker_approve_task": _handle_approve_task,
    "broker_reject_task": _handle_reject_task,
}
