"""
CrewAI Registry Loader - Variant A (Profiles per Agent)
Loads team configurations from crewai_teams.yml with profile support.
"""
import os
import json
import yaml
import logging
from pathlib import Path
from typing import Dict, Any, List

logger = logging.getLogger(__name__)

CREWAI_AGENTS_PATH = os.getenv("CREWAI_AGENTS_PATH", "/app/config/crewai_agents.json")
CREWAI_TEAMS_PATH = os.getenv("CREWAI_TEAMS_PATH", "/app/config/crewai_teams.yml")
CREWAI_TEAMS_GENERATED_PATH = os.getenv("CREWAI_TEAMS_GENERATED_PATH", "/app/config/crewai_teams.generated.yml")
ROLES_BASE_PATH = os.getenv("ROLES_BASE_PATH", "/app/config/roles")

# Example: "agromatrix=agx" means refs under agromatrix/* also try agx/*
ROLE_NAMESPACE_OVERRIDES_RAW = os.getenv("ROLE_NAMESPACE_OVERRIDES", "agromatrix=agx")
ROLE_NAMESPACE_OVERRIDES = {}
for part in ROLE_NAMESPACE_OVERRIDES_RAW.split(","):
    part = part.strip()
    if not part or "=" not in part:
        continue
    src, dst = part.split("=", 1)
    src = src.strip()
    dst = dst.strip()
    if src and dst:
        ROLE_NAMESPACE_OVERRIDES[src] = dst

_teams_config = None
_agents_config = None


def _normalize_prompt_ref(prompt_ref: str) -> str:
    ref = (prompt_ref or "").strip().lstrip("/")
    while ref.startswith("roles/"):
        ref = ref[len("roles/"):]
    return ref


def _build_prompt_candidates(prompt_ref: str) -> List[Path]:
    base = Path(ROLES_BASE_PATH)
    ref = _normalize_prompt_ref(prompt_ref)
    candidates: List[Path] = []

    if ref:
        candidates.append(base / ref)

        parts = ref.split("/", 1)
        if parts and parts[0] in ROLE_NAMESPACE_OVERRIDES and len(parts) > 1:
            mapped = f"{ROLE_NAMESPACE_OVERRIDES[parts[0]]}/{parts[1]}"
            candidates.append(base / mapped)

    # Legacy fallback (in case a ref is already relative but not under roles/*)
    raw_ref = (prompt_ref or "").strip().lstrip("/")
    if raw_ref and raw_ref != ref:
        candidates.append(base / raw_ref)

    # Deduplicate while preserving order
    unique: List[Path] = []
    seen = set()
    for p in candidates:
        k = str(p)
        if k in seen:
            continue
        seen.add(k)
        unique.append(p)
    return unique


def resolve_prompt_path(prompt_ref: str) -> Path:
    for candidate in _build_prompt_candidates(prompt_ref):
        if candidate.exists():
            return candidate
    return None


def load_agents_config():
    """Load basic agent config from crewai_agents.json"""
    global _agents_config
    if _agents_config is None:
        try:
            with open(CREWAI_AGENTS_PATH, "r") as f:
                _agents_config = json.load(f)
            logger.info(f"Loaded agents config from {CREWAI_AGENTS_PATH}")
        except Exception as e:
            logger.error(f"Failed to load agents config: {e}")
            _agents_config = {}
    return _agents_config


def load_teams_config():
    """Load teams/profiles config with generated-over-legacy merge."""
    global _teams_config
    if _teams_config is None:
        try:
            with open(CREWAI_TEAMS_PATH, "r") as f:
                legacy = yaml.safe_load(f) or {}
            merged = dict(legacy)
            version = merged.get("version", "unknown")
            logger.info(f"Loaded legacy teams config v{version} from {CREWAI_TEAMS_PATH}")

            generated = {}
            gen_path = Path(CREWAI_TEAMS_GENERATED_PATH)
            if gen_path.exists():
                with open(gen_path, "r") as f:
                    generated = yaml.safe_load(f) or {}
                logger.info(f"Loaded generated teams config from {CREWAI_TEAMS_GENERATED_PATH}")

            # Merge strategy: generated overrides legacy for same (agent, profile).
            # Missing agents/profiles continue to work from legacy file.
            if generated:
                skip_keys = {"schema_version", "version", "description"}
                for key, val in generated.items():
                    if key in skip_keys:
                        continue
                    if not isinstance(val, dict):
                        merged[key] = val
                        continue

                    legacy_agent = merged.get(key, {})
                    if not isinstance(legacy_agent, dict):
                        legacy_agent = {}

                    merged_agent = dict(legacy_agent)

                    gen_profiles = val.get("profiles", {})
                    if isinstance(gen_profiles, dict):
                        legacy_profiles = legacy_agent.get("profiles", {})
                        if not isinstance(legacy_profiles, dict):
                            legacy_profiles = {}
                        combined_profiles = dict(legacy_profiles)
                        for profile_name, profile_cfg in gen_profiles.items():
                            if profile_name in combined_profiles:
                                logger.info(
                                    f"Generated teams override legacy profile: {key}.{profile_name}"
                                )
                            combined_profiles[profile_name] = profile_cfg
                        merged_agent["profiles"] = combined_profiles

                    if "default_profile" in val:
                        merged_agent["default_profile"] = val["default_profile"]
                    if "profile_hints" in val:
                        merged_agent["profile_hints"] = val["profile_hints"]

                    merged[key] = merged_agent

            _teams_config = merged
            merged_version = _teams_config.get("version", "unknown")
            logger.info(f"Effective teams config v{merged_version} loaded (legacy+generated merge)")
        except Exception as e:
            logger.error(f"Failed to load teams config: {e}")
            _teams_config = {}
    return _teams_config


def load_role_prompt(prompt_ref: str) -> str:
    """Load role prompt from .md file with normalized path resolution."""
    if not prompt_ref:
        return ""

    resolved = resolve_prompt_path(prompt_ref)
    if not resolved:
        tried = ", ".join(str(p) for p in _build_prompt_candidates(prompt_ref))
        logger.warning(f"Role prompt not found: ref={prompt_ref}; tried=[{tried}]")
        return f"# Role: {prompt_ref}\n(prompt file missing)"

    try:
        return resolved.read_text(encoding="utf-8")
    except Exception as e:
        logger.error(f"Error loading role prompt {resolved}: {e}")
        return ""


def validate_required_prompts(strict: bool = False) -> Dict[str, Any]:
    """Validate all team/synthesis prompt refs are resolvable."""
    config = load_teams_config()
    missing = []
    skip_keys = {"schema_version", "version", "description"}

    for agent_id, agent_cfg in config.items():
        if agent_id in skip_keys or not isinstance(agent_cfg, dict):
            continue
        profiles = agent_cfg.get("profiles", {})
        if not isinstance(profiles, dict):
            continue

        for profile_name, profile_cfg in profiles.items():
            if not isinstance(profile_cfg, dict):
                continue

            synthesis = profile_cfg.get("synthesis", {}) or {}
            synth_ref = synthesis.get("system_prompt_ref", "")
            if synth_ref and not resolve_prompt_path(synth_ref):
                missing.append(f"{agent_id}.{profile_name}.synthesis -> {synth_ref}")

            for member in profile_cfg.get("team", []) or []:
                ref = (member or {}).get("system_prompt_ref", "")
                if ref and not resolve_prompt_path(ref):
                    mid = (member or {}).get("id", "unknown")
                    missing.append(f"{agent_id}.{profile_name}.{mid} -> {ref}")

    if missing:
        msg = f"Missing CrewAI role prompts: {len(missing)}"
        if strict:
            sample = "; ".join(missing[:8])
            raise RuntimeError(f"{msg}. Examples: {sample}")
        logger.warning(f"{msg}. Examples: {'; '.join(missing[:8])}")

    return {
        "missing_count": len(missing),
        "missing": missing,
    }


def get_agent_profiles(agent_id: str) -> list:
    """Get list of available profiles for an agent"""
    config = load_teams_config()
    agent_cfg = config.get(agent_id, {})
    profiles = agent_cfg.get("profiles", {})
    return list(profiles.keys())


def get_default_profile(agent_id: str) -> str:
    """Get default profile name for an agent"""
    config = load_teams_config()
    agent_cfg = config.get(agent_id, {})
    return agent_cfg.get("default_profile", "default")


def get_profile_hints(agent_id: str) -> dict:
    """Get profile selection hints (keywords) for an agent"""
    config = load_teams_config()
    agent_cfg = config.get(agent_id, {})
    return agent_cfg.get("profile_hints", {})


def select_profile(agent_id: str, prompt: str) -> str:
    """Select appropriate profile based on prompt keywords"""
    hints = get_profile_hints(agent_id)
    prompt_lower = prompt.lower()

    for profile_name, keywords in hints.items():
        for kw in keywords:
            if kw.lower() in prompt_lower:
                logger.info(f"Selected profile {profile_name} for {agent_id} (matched: {kw})")
                return profile_name

    return get_default_profile(agent_id)


def get_profile_config(agent_id: str, profile: str = None) -> dict:
    """Get full profile configuration for an agent"""
    config = load_teams_config()
    agent_cfg = config.get(agent_id, {})
    profiles = agent_cfg.get("profiles", {})

    if profile is None:
        profile = get_default_profile(agent_id)

    return profiles.get(profile, {})


def get_team_members(agent_id: str, profile: str = None) -> list:
    """Get team members with resolved prompts"""
    profile_cfg = get_profile_config(agent_id, profile)
    team = profile_cfg.get("team", [])

    resolved = []
    for member in team:
        resolved_member = dict(member)
        prompt_ref = member.get("system_prompt_ref", "")
        resolved_member["system_prompt"] = load_role_prompt(prompt_ref)
        resolved.append(resolved_member)

    return resolved


def get_synthesis_config(agent_id: str, profile: str = None) -> dict:
    """Get synthesis config with resolved prompt"""
    profile_cfg = get_profile_config(agent_id, profile)
    synthesis = profile_cfg.get("synthesis", {})

    if synthesis:
        prompt_ref = synthesis.get("system_prompt_ref", "")
        synthesis = dict(synthesis)
        synthesis["system_prompt"] = load_role_prompt(prompt_ref)

    return synthesis


def get_team_settings(agent_id: str, profile: str = None) -> dict:
    """Get team execution settings"""
    profile_cfg = get_profile_config(agent_id, profile)
    return {
        "team_name": profile_cfg.get("team_name", f"{agent_id} team"),
        "parallel_roles": profile_cfg.get("parallel_roles", True),
        "max_concurrency": profile_cfg.get("max_concurrency", 3)
    }


def get_delegation_config(agent_id: str, profile: str = None) -> dict:
    """Get delegation config for an agent"""
    profile_cfg = get_profile_config(agent_id, profile)
    return profile_cfg.get("delegation", {"enabled": False})


def can_delegate_to(agent_id: str, target_agent_id: str, profile: str = None) -> bool:
    """Check if agent can delegate to target"""
    deleg = get_delegation_config(agent_id, profile)
    if not deleg.get("enabled"):
        return False
    if deleg.get("forbid_self") and target_agent_id == agent_id:
        return False
    allowed = deleg.get("allow_top_level_agents", [])
    return target_agent_id in allowed


def is_orchestrator(agent_id: str) -> bool:
    """Check if agent has orchestrator capability"""
    config = load_teams_config()
    return agent_id in config and "profiles" in config.get(agent_id, {})


def get_all_agents_summary() -> dict:
    """Get summary of all agents and their profiles"""
    config = load_teams_config()
    summary = {}

    skip_keys = ["schema_version", "version", "description"]

    for agent_id, agent_cfg in config.items():
        if agent_id in skip_keys:
            continue
        if not isinstance(agent_cfg, dict):
            continue

        profiles = agent_cfg.get("profiles", {})
        summary[agent_id] = {
            "profiles": list(profiles.keys()),
            "default_profile": agent_cfg.get("default_profile", "default"),
            "has_hints": bool(agent_cfg.get("profile_hints"))
        }

        # Add role counts per profile
        for pname, pcfg in profiles.items():
            summary[agent_id][f"{pname}_roles"] = len(pcfg.get("team", []))

    return summary
