"""MCP tools for DS system operations.

Provides health check, authentication, business entity listing,
and URL parsing for investigation workflows.
"""

from __future__ import annotations

import re
from typing import Any
from urllib.parse import urlparse

from fastmcp.exceptions import ToolError
from mcp.types import ToolAnnotations

from mcp_eregistrations_common.auth import (
    AuthenticationError,
    keycloak_password_grant,
    store_shared_tokens,
)
from mcp_eregistrations_common.config import get_instance_config
from mcp_eregistrations_ds.ds_client import (
    DSClient,
    DSClientError,
    store_ds_token,
    translate_error,
)
from mcp_eregistrations_ds.server import mcp


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
async def ds_health(
    instance: str | None = None,
) -> dict[str, Any]:
    """Check DS instance health and component status.

    No authentication required.

    Args:
        instance: Instance profile name.

    Returns:
        dict with version, branch, build_type, redis,
        database, minio.
    """
    try:
        async with DSClient.for_instance(instance) as client:
            data = await client.get("/status")
    except DSClientError as e:
        raise translate_error(e, resource_type="health") from e

    return {
        "version": data.get("version"),
        "branch": data.get("branch"),
        "build_type": data.get("build_type"),
        "redis": data.get("redis"),
        "database": data.get("database"),
        "minio": data.get("minio"),
    }


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=False, destructiveHint=False))
async def ds_auth_login(
    username: str,
    password: str,
    instance: str | None = None,
) -> dict[str, Any]:
    """Authenticate with DS via Keycloak password grant.

    Stores token for subsequent DS API calls. Token is shared
    across all eRegistrations MCP servers via OS keyring.

    Args:
        username: Keycloak username (email).
        password: Keycloak password.
        instance: Instance profile name.

    Returns:
        dict with authenticated, instance, username.
    """
    if instance is None:
        raise ToolError(
            "No instance specified. Pass instance='<name>'. "
            "Use instance_list to see available instances."
        )

    config = get_instance_config(instance)

    keycloak_url = config.get("keycloak_url")
    keycloak_realm = config.get("keycloak_realm")
    keycloak_client_id = config.get("keycloak_client_id", "mcp-eregistrations-bpa")

    if not keycloak_url or not keycloak_realm:
        raise ToolError(
            "Keycloak not configured. Set KEYCLOAK_URL and "
            "KEYCLOAK_REALM env vars or register the instance "
            "with instance_add."
        )

    try:
        result = await keycloak_password_grant(
            keycloak_url=keycloak_url,
            realm=keycloak_realm,
            client_id=keycloak_client_id,
            username=username,
            password=password,
        )
    except AuthenticationError as e:
        raise ToolError(str(e)) from e

    access_token = result.get("access_token")
    if not access_token:
        raise ToolError("No access_token in Keycloak response.")

    # Discover token endpoint for refresh token storage
    token_endpoint = (
        f"{keycloak_url.rstrip('/')}/realms/{keycloak_realm}"
        f"/protocol/openid-connect/token"
    )

    # Store in both DS local cache and shared keyring
    store_ds_token(instance, access_token)
    store_shared_tokens(
        instance,
        access_token=access_token,
        refresh_token=result.get("refresh_token"),
        token_endpoint=token_endpoint,
        client_id=keycloak_client_id,
    )

    return {
        "authenticated": True,
        "instance": instance,
        "username": username,
    }


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
async def business_entity_list(
    page: int = 1,
    instance: str | None = None,
) -> dict[str, Any]:
    """List business entities. Requires authentication.

    Args:
        page: Page number (default: 1).
        instance: Instance profile name.

    Returns:
        dict with items, count, page, page_size, has_next.
    """
    try:
        async with DSClient.for_instance(instance) as client:
            data = await client.get_paginated(
                "/backend/businessentity",
                params={"page": page},
            )
    except DSClientError as e:
        raise translate_error(e, resource_type="business_entity") from e

    data["items"] = [
        {
            "business_entity_id": item.get("business_entity_id"),
            "name": item.get("name"),
            "created_at": item.get("created_at"),
            "updated_at": item.get("updated_at"),
        }
        for item in data.get("items", [])
    ]
    return data


_UUID = r"[0-9a-fA-F-]{36}"
_DS_URL_PATTERNS: list[tuple[re.Pattern[str], list[str], str]] = [
    (
        re.compile(rf"/part-b/({_UUID})/([^/]+)/({_UUID})"),
        ["service_id", "role", "process_id"],
        "Officer/reviewer view of an application file",
    ),
    (
        re.compile(rf"/inspector/({_UUID})/([^/]+)/({_UUID})"),
        ["service_id", "role", "process_id"],
        "Inspector view of an application file",
    ),
    (
        re.compile(rf"/services/({_UUID})"),
        ["service_id"],
        "New application form for a service",
    ),
    (
        re.compile(rf"/manage-business-entity/({_UUID})"),
        ["business_entity_id"],
        "Business entity management page",
    ),
]


@mcp.tool(annotations=ToolAnnotations(readOnlyHint=True))
async def ds_url_parse(
    url: str,
) -> dict[str, Any]:
    """Parse a DS frontend URL into resource IDs for investigation.

    Accepts any Display System URL (from the browser address bar) and
    extracts service_id, process_id, role, etc. Use this FIRST when
    a user shares a DS URL — then call the appropriate tools with
    the extracted IDs.

    Args:
        url: Full DS URL from the browser address bar.

    Returns:
        dict with page_type, extracted IDs, instance (from hostname),
        and suggested next_steps (tools to call).
    """
    parsed = urlparse(url)
    path = parsed.path
    # Strip language prefix like /en/ or /es/
    path = re.sub(r"^/[a-z]{2}/", "/", path)

    for pattern, keys, description in _DS_URL_PATTERNS:
        match = pattern.search(path)
        if match:
            ids = dict(zip(keys, match.groups()))
            result: dict[str, Any] = {
                "page_type": description,
                "instance_hostname": parsed.hostname,
                **ids,
            }
            # Suggest next steps based on what was extracted
            steps: list[str] = []
            if "service_id" in ids:
                steps.append(f"service_get(service_id='{ids['service_id']}')")
            if "process_id" in ids:
                steps.append(f"process_get(process_id='{ids['process_id']}')")
                steps.append(f"file_process_state(process_id='{ids['process_id']}')")
            if "process_id" in ids and "role" in ids:
                steps.append(
                    f"process_task_variables(process_id='{ids['process_id']}'"
                    f", role='{ids['role']}', task_id=<from process_get>)"
                )
            if "business_entity_id" in ids:
                steps.append("business_entity_list()")
            result["next_steps"] = steps
            return result

    raise ToolError(
        f"Could not parse DS URL: {url}. "
        "Expected patterns: /part-b/<service>/<role>/<process>, "
        "/inspector/<service>/<role>/<process>, /services/<service>"
    )
