"""
Phase 79: Governance MCP Microservice Tools.

Constitutional governance, proposal voting, violation tracking, and
peer review workflows. Extends the existing broker_propose/broker_violations.
"""

import json
import uuid
from datetime import datetime, timezone

from mcp.types import TextContent, Tool, ToolAnnotations
from brynq.consensus import ConsensusManager

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

TOOLS = [
    Tool(
        name="gov_create_proposal",
        description="Create a formal proposal that requires multi-agent consensus before execution.",
        inputSchema={
            "type": "object",
            "properties": {
                "title": {"type": "string", "description": "Proposal title"},
                "description": {"type": "string", "description": "Detailed proposal description"},
                "author_id": {"type": "string", "description": "Who is proposing this"},
                "category": {
                    "type": "string",
                    "enum": ["architecture", "process", "feature", "hotfix", "policy"],
                    "description": "Proposal category",
                },
                "options": {
                    "type": "array",
                    "items": {"type": "string"},
                    "description": "List of options to vote on. Default: ['approve', 'reject']"
                },
                "required_approvals": {
                    "type": "integer",
                    "description": "Number of approvals needed (default: 2)",
                },
                "timeout_minutes": {
                    "type": "integer",
                    "description": "How long the vote is open for (default: 30)"
                }
            },
            "required": ["title", "description", "author_id"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="gov_vote",
        description="Vote on an active proposal.",
        inputSchema={
            "type": "object",
            "properties": {
                "proposal_id": {"type": "string"},
                "voter_id": {"type": "string"},
                "vote": {"type": "string"},
            },
            "required": ["proposal_id", "voter_id", "vote"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="gov_resolve_proposal",
        description="Force resolution of a proposal (check if quorum is met or timeout hit).",
        inputSchema={
            "type": "object",
            "properties": {
                "proposal_id": {"type": "string"},
            },
            "required": ["proposal_id"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="gov_get_proposal",
        description="Get a proposal with its current vote tally.",
        inputSchema={
            "type": "object",
            "properties": {
                "proposal_id": {"type": "string"},
            },
            "required": ["proposal_id"],
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="gov_list_proposals",
        description="List open or resolved proposals.",
        inputSchema={
            "type": "object",
            "properties": {
                "status": {
                    "type": "string",
                    "enum": ["open", "resolved", "expired", "cancelled"],
                },
            },
        },
        annotations=_SAFE_READ,
    ),
    Tool(
        name="gov_log_violation",
        description="Log a constitutional violation for tracking and review.",
        inputSchema={
            "type": "object",
            "properties": {
                "violator_id": {"type": "string", "description": "Who violated"},
                "rule": {"type": "string", "description": "Which rule was violated"},
                "description": {"type": "string", "description": "What happened"},
                "severity": {
                    "type": "string",
                    "enum": ["low", "medium", "high", "critical"],
                },
                "channel": {"type": "string"},
            },
            "required": ["violator_id", "rule", "description"],
        },
        annotations=_SAFE_WRITE,
    ),
    Tool(
        name="gov_violation_stats",
        description="Get violation statistics by agent, rule, and severity.",
        inputSchema={"type": "object", "properties": {}},
        annotations=_SAFE_READ,
    ),
]


# ── Handlers ───────────────────────────────────────────────────────────────

def _get_cm() -> ConsensusManager:
    return ConsensusManager()

def _violations_dir():
    from brynq.server import BROKER_DIR
    d = BROKER_DIR / "violations_log"
    d.mkdir(parents=True, exist_ok=True)
    return d


async def _handle_create_proposal(args: dict) -> list[TextContent]:
    cm = _get_cm()
    title = args.get("title", "")
    description = f"{title}\n{args.get('description', '')}"
    options = args.get("options", ["approve", "reject"])
    quorum = args.get("required_approvals", 2)
    duration_sec = args.get("timeout_minutes", 30) * 60
    
    result = cm.create_proposal(
        description=description,
        options=options,
        created_by=args.get("author_id", ""),
        duration_sec=duration_sec,
        quorum=quorum
    )
    
    if "error" in result:
        return [TextContent(type="text", text=f"Failed to create proposal: {result['error']}")]
        
    return [TextContent(type="text", text=f"Proposal created: {result['proposal_id']}. Expires at {result['expires_at']}")]


async def _handle_vote(args: dict) -> list[TextContent]:
    cm = _get_cm()
    result = cm.vote(
        proposal_id=args["proposal_id"],
        agent_id=args["voter_id"],
        option=args["vote"]
    )
    
    if not result.get("ok"):
        return [TextContent(type="text", text=f"Vote failed: {result.get('error')}")]
        
    # Auto-resolve check
    res_status = cm.resolve(args["proposal_id"])
    
    msg = f"Vote recorded. Current total votes: {result['total_votes']}."
    if res_status.get("quorum_met") and res_status.get("winner"):
        msg += f"\nProposal resolved! Winner: {res_status['winner']}"
        
    return [TextContent(type="text", text=msg)]


async def _handle_resolve_proposal(args: dict) -> list[TextContent]:
    cm = _get_cm()
    result = cm.resolve(args["proposal_id"])
    return [TextContent(type="text", text=json.dumps(result, indent=2))]


async def _handle_get_proposal(args: dict) -> list[TextContent]:
    cm = _get_cm()
    proposal = cm.get_proposal(args["proposal_id"])
    if not proposal:
        return [TextContent(type="text", text=f"Proposal {args['proposal_id']} not found")]
    return [TextContent(type="text", text=json.dumps(proposal, indent=2))]


async def _handle_list_proposals(args: dict) -> list[TextContent]:
    cm = _get_cm()
    status = args.get("status", "open")
    if status == "open":
        proposals = cm.list_open()
    else:
        # Pass a larger limit to get all resolved/expired/cancelled
        proposals = [p for p in cm.list_resolved(limit=100) if p.get("status") == status]
        
    return [TextContent(type="text", text=json.dumps(proposals, indent=2))]


async def _handle_log_violation(args: dict) -> list[TextContent]:
    from brynq.server import _write_json_sync
    vid = uuid.uuid4().hex[:12]
    violation = {
        "violation_id": vid,
        "violator_id": args["violator_id"],
        "rule": args["rule"],
        "description": args["description"],
        "severity": args.get("severity", "medium"),
        "channel": args.get("channel", ""),
        "logged_at": datetime.now(timezone.utc).isoformat(),
    }
    _write_json_sync(_violations_dir() / f"{vid}.json", violation)
    return [TextContent(type="text", text=f"Violation logged: {vid}")]


async def _handle_violation_stats(args: dict) -> list[TextContent]:
    by_agent: dict[str, int] = {}
    by_rule: dict[str, int] = {}
    by_severity: dict[str, int] = {}
    total = 0

    for f in _violations_dir().glob("*.json"):
        try:
            v = json.loads(f.read_text("utf-8"))
        except (json.JSONDecodeError, OSError):
            continue
        total += 1
        agent = v.get("violator_id", "unknown")
        by_agent[agent] = by_agent.get(agent, 0) + 1
        rule = v.get("rule", "unknown")
        by_rule[rule] = by_rule.get(rule, 0) + 1
        sev = v.get("severity", "medium")
        by_severity[sev] = by_severity.get(sev, 0) + 1

    result = {
        "total_violations": total,
        "by_agent": by_agent,
        "by_rule": by_rule,
        "by_severity": by_severity,
    }
    return [TextContent(type="text", text=json.dumps(result, indent=2))]


HANDLERS = {
    "gov_create_proposal": _handle_create_proposal,
    "gov_vote": _handle_vote,
    "gov_resolve_proposal": _handle_resolve_proposal,
    "gov_get_proposal": _handle_get_proposal,
    "gov_list_proposals": _handle_list_proposals,
    "gov_log_violation": _handle_log_violation,
    "gov_violation_stats": _handle_violation_stats,
}
