"""
Single source of truth for every agent the platform exposes.

Adding a new agent = add one AgentRoute entry below. That entry gives you:
  1. the dispatcher wiring          (SERVICE_REGISTRY, derived in core/registry.py)
  2. a dedicated URL route          (POST /<slug>, registered in api/agents.py)
  3. an entry in GET /api/ai/agents (service discovery)

The generic POST /api/ai/execute endpoint keeps working unchanged — it resolves
the same initiative/capability pair through core/intent.py instead of the URL.
"""
import os
from dataclasses import dataclass, field
from typing import Callable, Optional


def route_prefix() -> str:
    """Prefix the per-agent routes are mounted under. Empty string = server root."""
    prefix = os.getenv("AGENT_ROUTE_PREFIX", "").strip().rstrip("/")
    if prefix and not prefix.startswith("/"):
        prefix = f"/{prefix}"
    return prefix


@dataclass(frozen=True)
class AgentRoute:
    slug: str                      # URL segment  → POST /<slug>
    initiative_code: str           # maiinitiative.aiinitiativecode
    default_capability: str        # capability used when the caller sends none
    module: str                    # adapter module implementing handle_request()
    summary: str                   # shown in Swagger
    capabilities: tuple = ()       # extra capabilities the caller may select
    aliases: tuple = ()            # additional URL segments hitting the same agent
    accepts_file: bool = True
    accepts_text: bool = True
    requires_file: bool = False
    uses_session: bool = False     # honours session_id → shown in the Swagger form
    uses_login: bool = False       # forwards the GoodBooks Login header
    url_env: tuple = ()            # env var(s) holding this agent's service URL
    optional_url: bool = False     # adapter has a built-in default URL
    # Optional rule that picks the capability from the request itself.
    # Signature: (input_data, file, session_id) -> capability_code
    select_capability: Optional[Callable] = field(default=None, compare=False)

    @property
    def all_capabilities(self) -> tuple:
        return (self.default_capability,) + tuple(
            c for c in self.capabilities if c != self.default_capability
        )

    @property
    def all_slugs(self) -> tuple:
        return (self.slug,) + tuple(self.aliases)


def _report_capability(input_data, file, session_id):
    """An existing session means the caller is continuing a Q&A conversation."""
    return "QUESTION_ANSWER" if session_id else "ANALYZE_REPORT"


AGENT_ROUTES = (
    AgentRoute(
        slug="report_analyzer",
        initiative_code="REPORT_ANALYZER",
        default_capability="ANALYZE_REPORT",
        capabilities=("QUESTION_ANSWER",),
        module="enterprise_ai.services.adapters.report_service_adapter",
        summary="Analyze a report, or continue a report Q&A session",
        select_capability=_report_capability,
        uses_session=True,
        url_env=("REPORT_ANALYZER_URL",),
    ),
    AgentRoute(
        slug="extractor",
        initiative_code="EXTRACTOR_AGENT",
        default_capability="EXTRACT_DOCUMENT",
        module="enterprise_ai.services.adapters.extractor_service_adapter",
        summary="Extract structured data from an uploaded document",
        aliases=("extract_document",),
        accepts_text=False,
        requires_file=True,
        url_env=("EXTRACTOR_AGENT", "EXTRACTOR_AGENT_URL"),
    ),
    AgentRoute(
        slug="creator",
        initiative_code="CREATOR_AGENT",
        default_capability="GENERATE_VOUCHER",
        module="enterprise_ai.services.adapters.creator_service_adapter",
        summary="Generate a voucher from free text",
        aliases=("generate_voucher",),
        accepts_file=False,
        url_env=("CREATOR_AGENT_URL",),
    ),
    AgentRoute(
        slug="chatbot",
        initiative_code="CHATBOT_AGENT",
        default_capability="CHATBOT",
        module="enterprise_ai.services.adapters.chatbot_service_adapter",
        summary="Enterprise chatbot (send the GoodBooks login JSON in the login field)",
        accepts_file=False,
        uses_session=True,
        uses_login=True,
        url_env=("CHATBOT_AGENT_URL",),
    ),
    AgentRoute(
        slug="chatinterface",
        initiative_code="CHATINTERFACE_AGENT",
        default_capability="CHATINTERFACE",
        module="enterprise_ai.services.adapters.chatinterface_service_adapter",
        summary="Chat interface agent",
        accepts_file=False,
        uses_login=True,
        url_env=("CHATINTERFACE_AGENT_URL",),
    ),
    AgentRoute(
        slug="applicant",
        initiative_code="APPLICANT_AGENT",
        default_capability="APPLICANT_PROCESS",
        module="enterprise_ai.services.adapters.applicant_service_adapter",
        summary="Process an applicant payload or document",
        uses_login=True,
        url_env=("APPLICANT_URL",),
    ),
    AgentRoute(
        slug="document_identifier",
        initiative_code="DOCUMENT_IDENTIFIER_AGENT",
        default_capability="IDENTIFY_DOCUMENT",
        module="enterprise_ai.services.adapters.document_identifier_service_adapter",
        summary="Classify the type of an uploaded document",
        aliases=("identify_document",),
        accepts_text=False,
        requires_file=True,
        url_env=("DOCUMENT_IDENTIFIER_URL",),
        optional_url=True,
    ),
    AgentRoute(
        slug="parse_validate",
        initiative_code="PARSE_VALIDATE_AGENT",
        default_capability="PARSE_VALIDATE_DOCUMENT",
        module="enterprise_ai.services.adapters.parse_validate_service_adapter",
        summary="Parse and validate an uploaded document",
        aliases=("parser_document", "parse_document"),
        accepts_text=False,
        requires_file=True,
        url_env=("PARSE_VALIDATE_URL",),
        optional_url=True,
    ),
    AgentRoute(
        slug="tax_suggestions",
        initiative_code="TAX_ADVISORY_AGENT",
        default_capability="TAX_SUGGESTIONS",
        module="enterprise_ai.services.adapters.tax_suggestions_service_adapter",
        summary="Tax regime comparison and advisory from ERP payroll JSON",
        aliases=("ai_tax_suggestions",),
        accepts_file=False,
        url_env=("TAX_SUGGESTIONS_URL",),
        optional_url=True,
    ),
    AgentRoute(
        slug="generate_profile",
        initiative_code="generate_profile",
        default_capability="GENERATE_PROFILE",
        module="enterprise_ai.services.adapters.generate_profile_service_adapter",
        summary="Generate a job profile from 'position | department | level | industry | context'",
        accepts_file=False,
        url_env=("GENERATE_PROFILE_URL",),
    ),
    AgentRoute(
        slug="kms_discovery",
        initiative_code="KMS_DISCOVERY_AGENT",
        default_capability="KMS_EXTRACT",
        capabilities=("KMS_EMBED_ARTIFACT", "KMS_RETIRE_ARTIFACT"),
        module="enterprise_ai.services.adapters.kms_discovery_adapter",
        summary="Extract knowledge artifacts, or embed/retire a published artifact",
        aliases=("kms_extract",),
        url_env=("KMS_CORE_URL",),
    ),
)


def _build_slug_index() -> dict:
    """Fail at import time on a duplicated slug rather than at request time."""
    index: dict = {}
    for route in AGENT_ROUTES:
        for slug in route.all_slugs:
            if not slug or "/" in slug:
                raise ValueError(f"Invalid agent slug {slug!r} on {route.initiative_code}")
            if slug in index:
                raise ValueError(
                    f"Duplicate agent slug '{slug}' "
                    f"({index[slug].initiative_code} and {route.initiative_code})"
                )
            index[slug] = route
    return index


SLUG_INDEX = _build_slug_index()

INITIATIVE_INDEX = {route.initiative_code: route for route in AGENT_ROUTES}


def get_route_by_slug(slug: str) -> Optional[AgentRoute]:
    return SLUG_INDEX.get(slug)


def get_route_by_initiative(initiative_code: str) -> Optional[AgentRoute]:
    return INITIATIVE_INDEX.get(initiative_code)


def build_service_registry() -> dict:
    """initiative_code -> adapter module path (consumed by core/dispatcher.py)."""
    return {route.initiative_code: route.module for route in AGENT_ROUTES}
