"""
Brynq Decentralized Autonomous Swarm (Phase 12.B)
Implements dynamic swarm formation, role assignment, and consensus mechanisms.
"""

import json
import uuid
import random
from typing import List, Dict, Optional
from datetime import datetime, timezone
from pathlib import Path
from .server import BROKER_DIR, SESSION_ID, PLATFORM, _write_json_sync

SWARM_DIR = BROKER_DIR / "swarms"

def _ensure_swarm_dir():
    SWARM_DIR.mkdir(parents=True, exist_ok=True)

def create_swarm(task_id: str, required_skills: List[str], max_size: int = 3) -> str:
    """Initialize a new dynamic swarm for a specific task."""
    _ensure_swarm_dir()
    swarm_id = uuid.uuid4().hex[:12]
    
    swarm = {
        "swarm_id": swarm_id,
        "task_id": task_id,
        "status": "forming",
        "required_skills": required_skills,
        "max_size": max_size,
        "members": {}, # session_id -> role ("lead", "worker", "reviewer")
        "bids": [],    # list of dicts: {"session_id": "...", "reputation": 85, "skill_match": 1.0}
        "created_at": datetime.now(timezone.utc).isoformat(),
        "consensus_votes": {} # vote tracking for cross-agent decisions
    }
    
    _write_json_sync(SWARM_DIR / f"{swarm_id}.json", swarm)
    return swarm_id

def submit_bid(swarm_id: str, session_id: str, reputation: int, skill_match: float) -> bool:
    """An agent bids to join a forming swarm based on their profile."""
    _ensure_swarm_dir()
    path = SWARM_DIR / f"{swarm_id}.json"
    if not path.exists():
        return False
        
    try:
        swarm = json.loads(path.read_text("utf-8"))
        if swarm["status"] != "forming":
            return False
            
        swarm["bids"].append({
            "session_id": session_id,
            "reputation": reputation,
            "skill_match": skill_match,
            "timestamp": datetime.now(timezone.utc).isoformat()
        })
        _write_json_sync(path, swarm)
        return True
    except (json.JSONDecodeError, OSError):
        return False

def form_swarm(swarm_id: str) -> bool:
    """Evaluate bids and assign roles based on reputation and skill match."""
    _ensure_swarm_dir()
    path = SWARM_DIR / f"{swarm_id}.json"
    if not path.exists():
        return False
        
    try:
        swarm = json.loads(path.read_text("utf-8"))
        if swarm["status"] != "forming" or not swarm["bids"]:
            return False
            
        # Sort bids: primarily by skill match, then by reputation
        sorted_bids = sorted(
            swarm["bids"], 
            key=lambda x: (x["skill_match"], x["reputation"]), 
            reverse=True
        )
        
        selected = sorted_bids[:swarm["max_size"]]
        if not selected:
            return False
            
        # Highest rep/skill becomes temporary Lead
        lead = selected[0]["session_id"]
        swarm["members"][lead] = "lead"
        
        # Others become workers/reviewers
        for i, bid in enumerate(selected[1:]):
            role = "reviewer" if i == 0 and len(selected) > 2 else "worker"
            swarm["members"][bid["session_id"]] = role
            
        swarm["status"] = "active"
        _write_json_sync(path, swarm)
        return True
    except (json.JSONDecodeError, OSError):
        return False

def propose_decision(swarm_id: str, proposal_id: str, description: str) -> bool:
    """A member proposes a decision that requires swarm consensus."""
    _ensure_swarm_dir()
    path = SWARM_DIR / f"{swarm_id}.json"
    if not path.exists():
        return False
        
    try:
        swarm = json.loads(path.read_text("utf-8"))
        if proposal_id not in swarm["consensus_votes"]:
            swarm["consensus_votes"][proposal_id] = {
                "description": description,
                "votes": {},
                "status": "pending"
            }
            _write_json_sync(path, swarm)
            return True
        return False
    except (json.JSONDecodeError, OSError):
        return False

def vote_on_decision(swarm_id: str, proposal_id: str, session_id: str, vote: bool) -> str:
    """Swarm members vote. Returns 'approved', 'rejected', or 'pending'."""
    _ensure_swarm_dir()
    path = SWARM_DIR / f"{swarm_id}.json"
    if not path.exists():
        return "error"
        
    try:
        swarm = json.loads(path.read_text("utf-8"))
        if session_id not in swarm["members"]:
            return "error"
            
        proposal = swarm["consensus_votes"].get(proposal_id)
        if not proposal or proposal["status"] != "pending":
            return "error"
            
        proposal["votes"][session_id] = vote
        
        # Check quorum (2/3 majority of active members)
        member_count = len(swarm["members"])
        votes_cast = len(proposal["votes"])
        yes_votes = sum(1 for v in proposal["votes"].values() if v)
        
        quorum_needed = max(1, int(member_count * 0.66))
        
        if yes_votes >= quorum_needed:
            proposal["status"] = "approved"
        elif (votes_cast - yes_votes) > (member_count - quorum_needed):
            # Mathematically impossible to reach quorum now
            proposal["status"] = "rejected"
            
        _write_json_sync(path, swarm)
        return proposal["status"]
    except (json.JSONDecodeError, OSError):
        return "error"

def dissolve_swarm(swarm_id: str) -> bool:
    """Disband the swarm after task completion."""
    _ensure_swarm_dir()
    path = SWARM_DIR / f"{swarm_id}.json"
    if not path.exists():
        return False
        
    try:
        swarm = json.loads(path.read_text("utf-8"))
        swarm["status"] = "dissolved"
        swarm["dissolved_at"] = datetime.now(timezone.utc).isoformat()
        _write_json_sync(path, swarm)
        return True
    except (json.JSONDecodeError, OSError):
        return False
