"""Family coordinator — orchestrates generation of the 5-draft ecosystem."""

from __future__ import annotations

import json

from rich.console import Console

from ..config import Config
from ..db import Database
from .generator import PipelineGenerator
from .quality import QualityGates

console = Console()

FAMILY_DRAFTS = [
    {
        "role": "AEM",
        "topic": "Agent Ecosystem Model",
        "description": (
            "Core architecture and terminology for the agent ecosystem. "
            "Defines shared concepts: DAG execution model, HITL points, "
            "assurance levels, protocol agnosticism. Foundation for all "
            "companion drafts."
        ),
    },
    {
        "role": "ATD",
        "topic": "Agent Task DAG",
        "description": (
            "Execution model using DAG structure with checkpoints and rollback. "
            "Defines node semantics (pending/running/done/failed/rolled-back), "
            "resource hints, circuit breakers, and rollback protocol. "
            "Uses ECT as token and DAG format."
        ),
    },
    {
        "role": "HITL",
        "topic": "Human-in-the-Loop",
        "description": (
            "Human oversight as first-class primitive. Approval gates, "
            "escalation paths, emergency override (PAUSE/CONSTRAIN/STOP/TAKEOVER), "
            "and explainability hooks. Integrates with DAG as HITL nodes."
        ),
    },
    {
        "role": "AEPB",
        "topic": "Agent Ecosystem Protocol Bindings",
        "description": (
            "Cross-protocol interoperability layer. Capability advertisement, "
            "protocol binding requirements, translation gateways, negotiation. "
            "Makes ecosystem semantics available over any A2A protocol."
        ),
    },
    {
        "role": "APAE",
        "topic": "Agent Provenance Assurance Ecosystem",
        "description": (
            "Trust, verification, and provenance for dual-regime operation. "
            "Assurance profiles (relaxed/standard/regulated), behavior verification, "
            "dynamic trust scoring (AIMD model), provenance chains. "
            "Same stack from K8s to fully proven."
        ),
    },
]


class FamilyCoordinator:
    def __init__(self, config: Config, db: Database, analyzer):
        self.config = config
        self.db = db
        self.analyzer = analyzer
        self.generator = PipelineGenerator(config, db, analyzer)
        self.quality = QualityGates(config, db, analyzer)

    def generate_family(self, family_name: str = "agent-ecosystem", cheap: bool = False) -> list[dict]:
        """Generate all 5 drafts in order. AEM first, then B-E with sibling context."""
        console.print(f"\n[bold cyan]Generating draft family: {family_name}[/]")
        console.print(f"Drafts: {len(FAMILY_DRAFTS)}, cheap={cheap}")

        # Log the generation run
        run_id = self.db.log_generation_run({
            "family_name": family_name,
            "gap_ids": [d["role"] for d in FAMILY_DRAFTS],
            "model_used": self.config.claude_model_cheap if cheap else self.config.claude_model,
            "status": "running",
        })

        results = []
        total_in = 0
        total_out = 0

        for i, draft_spec in enumerate(FAMILY_DRAFTS):
            console.print(
                f"\n[bold]{'='*60}[/]"
                f"\n[bold]Draft {i+1}/{len(FAMILY_DRAFTS)}: "
                f"[cyan]{draft_spec['role']}[/] — {draft_spec['topic']}[/]"
                f"\n[bold]{'='*60}[/]"
            )

            try:
                result = self.generator.generate_full(
                    gap_topic=draft_spec["topic"],
                    cheap=cheap,
                    family_name=family_name,
                    family_role=draft_spec["role"],
                )
                results.append(result)

                # Run quality gates
                draft_id = result.get("id")
                if draft_id:
                    console.print(f"\n[dim]Running quality gates for {draft_spec['role']}...[/]")
                    qr = self.quality.run_all(draft_id)
                    result["quality_results"] = qr

            except Exception as e:
                console.print(f"[red]Failed to generate {draft_spec['role']}: {e}[/]")
                results.append({
                    "role": draft_spec["role"],
                    "topic": draft_spec["topic"],
                    "error": str(e),
                })

        # Update run
        self.db.update_generation_run(
            run_id,
            status="completed",
            completed_at=_now_iso(),
        )

        # Summary
        console.print(f"\n[bold cyan]{'='*60}[/]")
        console.print(f"[bold]Family generation complete: {family_name}[/]")
        successful = [r for r in results if "error" not in r]
        console.print(f"  Generated: {len(successful)}/{len(FAMILY_DRAFTS)} drafts")
        for r in results:
            if "error" in r:
                console.print(f"  [red]FAIL[/] {r['role']}: {r['error']}")
            else:
                console.print(f"  [green]OK[/] {r.get('family_role', '?')}: {r.get('title', '?')}")

        return results

    def check_consistency(self, family_name: str) -> dict:
        """Check terminology consistency across family drafts."""
        drafts = self.db.get_family_drafts(family_name)
        if not drafts:
            return {"consistent": False, "details": "No drafts found for family"}

        # Collect terminology from all outlines
        all_terms: dict[str, dict[str, str]] = {}  # term -> {role: definition}
        for gd in drafts:
            role = gd.get("family_role", "?")
            outline_raw = gd.get("outline_json", "{}")
            try:
                outline = json.loads(outline_raw) if isinstance(outline_raw, str) else outline_raw
            except (json.JSONDecodeError, TypeError):
                continue

            terms = outline.get("terminology", {})
            if not isinstance(terms, dict):
                continue
            for term, defn in terms.items():
                term_lower = term.lower()
                if term_lower not in all_terms:
                    all_terms[term_lower] = {}
                all_terms[term_lower][role] = defn

        # Find terms used in multiple drafts
        shared_terms = {t: roles for t, roles in all_terms.items() if len(roles) > 1}
        if not shared_terms:
            return {
                "consistent": True,
                "shared_terms": 0,
                "details": "No shared terminology found across drafts",
            }

        # Check for inconsistencies (simple: different definitions for same term)
        inconsistencies = []
        for term, roles in shared_terms.items():
            definitions = list(roles.values())
            # Rough check: if definitions differ significantly
            unique_defs = set(d.lower().strip().rstrip(".") for d in definitions)
            if len(unique_defs) > 1:
                inconsistencies.append({
                    "term": term,
                    "definitions": roles,
                })

        consistent = len(inconsistencies) == 0
        details_parts = [f"{len(shared_terms)} shared terms across drafts"]
        if inconsistencies:
            details_parts.append(f"{len(inconsistencies)} inconsistencies found:")
            for inc in inconsistencies:
                details_parts.append(f"  '{inc['term']}': {inc['definitions']}")

        console.print(f"\n[bold]Consistency check: {family_name}[/]")
        console.print(f"  Shared terms: {len(shared_terms)}")
        console.print(f"  Inconsistencies: {len(inconsistencies)}")
        if consistent:
            console.print("  [green]All terminology consistent[/]")
        else:
            for inc in inconsistencies:
                console.print(f"  [yellow]Inconsistent: '{inc['term']}'[/]")
                for role, defn in inc["definitions"].items():
                    console.print(f"    {role}: {defn[:80]}")

        return {
            "consistent": consistent,
            "shared_terms": len(shared_terms),
            "inconsistencies": inconsistencies,
            "details": "; ".join(details_parts),
        }


def _now_iso() -> str:
    from datetime import datetime, timezone
    return datetime.now(timezone.utc).isoformat()
