#app/aroflo_connector_app/cli.py
from __future__ import annotations

import importlib
import json
import logging
import os
from typing import Dict, Any, List, Tuple

import click
from dotenv import load_dotenv
from urllib.parse import urlencode

from .client import AroFloClient, AroFloError
from .config import AroFloConfigError, AroFloSettings
from .services.capabilities import build_capabilities_manifest
from .zones.registry import build_registry
from .services.users_v1 import UsersContractError, query_users
from bridge_platform.secrets.secrets_manager import SecretsManager

# Cargar variables desde .env automáticamente cuando uses la CLI
# (se asume que ejecutas la CLI desde /var/www/html/flask_server)
load_dotenv(os.path.join(os.getcwd(), ".env"))

logging.basicConfig(
    level=logging.INFO,
    format="%(levelname)s:%(name)s:%(message)s",
)
log = logging.getLogger("aroflo_cli")


def make_client() -> AroFloClient:
    """
    Helper central para crear el cliente AroFlo para comandos generales.
    """
    return AroFloClient()


@click.group()
@click.option("--debug", is_flag=True, help="Activa logs en modo DEBUG.")
def cli(debug: bool) -> None:
    """CLI para la AroFlo Connector App."""
    if debug:
        logging.getLogger().setLevel(logging.DEBUG)
        log.debug("Modo DEBUG activado.")


@cli.command("contract-users")
@click.option("--tenant-id", required=True, help="Trusted platform tenant ID.")
@click.option("--operation", type=click.Choice(["list", "search", "get"]), default="search")
@click.option("--record-id", default="")
@click.option("--filter", "filter_specs", multiple=True, help="Canonical field:operator:value filter.")
@click.option("--page", default=1, type=click.IntRange(1, 10000))
@click.option("--page-size", default=25, type=click.IntRange(1, 100))
def contract_users(tenant_id: str, operation: str, record_id: str,
                   filter_specs: tuple[str, ...], page: int, page_size: int) -> None:
    """Run the normalized users contract through tenant vault credentials."""
    filters = []
    for spec in filter_specs:
        parts = spec.split(":", 2)
        if len(parts) != 3:
            raise click.ClickException("Each --filter must use field:operator:value.")
        value: Any = parts[2]
        if value.lower() in {"true", "false"}:
            value = value.lower() == "true"
        filters.append({"field": parts[0], "operator": parts[1], "value": value})
    manager = SecretsManager()
    names = ("aroflo_base_url", "aroflo_u_encoded", "aroflo_p_encoded",
             "aroflo_api_secret", "aroflo_org_encoded", "aroflo_host_ip")
    credentials = {name: value for name in names if (value := manager.get_secret(
        tenant_id=tenant_id, app_id="aroflo_connector_app", secret_name=name)) is not None}
    try:
        result = query_users(AroFloClient(settings=AroFloSettings.from_credentials(credentials)), {
            "operation": operation, "record_id": record_id, "filters": filters,
            "pagination": {"page": page, "page_size": page_size}, "transport": "auto",
        })
    except (AroFloConfigError, AroFloError, UsersContractError) as exc:
        raise click.ClickException(str(exc)) from exc
    click.echo(json.dumps(result, indent=2, ensure_ascii=False))


# --------------------------------------------------------------------
#  Comando: zones (debug rápido)
# --------------------------------------------------------------------
@cli.command("zones")
def zones_cmd() -> None:
    """Lista las zonas registradas (debug rápido)."""
    try:
        client = make_client()
    except AroFloConfigError as exc:
        click.echo("❌ Error de configuración:")
        click.echo(str(exc))
        raise SystemExit(1)

    reg = build_registry(client)
    click.echo(json.dumps(list(reg.keys()), indent=2, ensure_ascii=False))


# --------------------------------------------------------------------
#  Comando: envcheck
# --------------------------------------------------------------------
@cli.command()
def envcheck() -> None:
    """Verifica que las variables de entorno necesarias estén presentes."""
    try:
        settings = AroFloSettings.from_env()
    except AroFloConfigError as exc:
        click.echo("❌ Configuración incompleta:")
        click.echo(str(exc))
        raise SystemExit(1)

    click.echo("✅ Configuración OK. Valores leídos:")
    masked = {
        "base_url": settings.base_url,
        "u_encoded": settings.u_encoded[:4] + "...",
        "p_encoded": settings.p_encoded[:4] + "...",
        "api_secret": settings.api_secret[:4] + "...",
        "org_encoded": settings.org_encoded[:4] + "...",
        "accept": settings.accept,
        "host_ip": settings.host_ip,
        "timeout": settings.timeout,
    }
    click.echo(json.dumps(masked, indent=2, ensure_ascii=False))


# --------------------------------------------------------------------
#  Helpers para ping / users-raw (legacy)
# --------------------------------------------------------------------
def build_users_query() -> Tuple[Dict[str, Any], str]:
    """
    Replica el urlVarString del script de Postman para zone=users.
    """
    params_list: List[Tuple[str, Any]] = [
        ("zone", "users"),
        ("where", "and|createdutc|>|2001-01-01"),
        ("page", "1"),
    ]
    var_string = urlencode(params_list)  # zone=users&where=...&page=1
    params = dict(params_list)
    return params, var_string


@cli.command()
def ping() -> None:
    """
    Ping simple a AroFlo usando base_url + ?zone=users...
    """
    try:
        client = make_client()
    except AroFloConfigError as exc:
        click.echo("❌ Error de configuración:")
        click.echo(str(exc))
        raise SystemExit(1)

    params, var_string = build_users_query()

    try:
        data = client.request(
            "",
            method="GET",
            params=params,
            var_string=var_string,
        )
    except AroFloError as exc:
        click.echo("❌ Error al conectar con AroFlo:")
        click.echo(str(exc))
        raise SystemExit(1)

    click.echo("✅ Conexión OK a zone=users. Respuesta resumida:")
    click.echo(json.dumps(data, indent=2, ensure_ascii=False))


@cli.command("users-raw")
@click.option("--include-raw", is_flag=True, help="Imprime la respuesta completa en JSON.")
def users_raw(include_raw: bool) -> None:
    """
    Lista usuarios desde AroFlo usando zone=users en base_url (versión antigua).
    """
    try:
        client = make_client()
    except AroFloConfigError as exc:
        click.echo("❌ Error de configuración:")
        click.echo(str(exc))
        raise SystemExit(1)

    params, var_string = build_users_query()

    try:
        data = client.request(
            "",
            method="GET",
            params=params,
            var_string=var_string,
        )
    except AroFloError as exc:
        click.echo("❌ Error al obtener usuarios:")
        click.echo(str(exc))
        raise SystemExit(1)

    if include_raw:
        click.echo(json.dumps(data, indent=2, ensure_ascii=False))
        return

    zone = data.get("zoneresponse", {})
    records = zone.get("users") or zone.get("items") or zone.get("records") or []

    click.echo(f"Usuarios recibidos: {len(records)}")
    for u in records:
        uid = u.get("userid") or u.get("UserID") or u.get("id")
        name = (
            u.get("name")
            or u.get("UserName")
            or u.get("username")
            or f"{u.get('givennames', '')} {u.get('surname', '')}".strip()
        )
        click.echo(f"- {uid}: {name}")


@cli.command("caps-dump")
@click.option(
    "--pretty/--no-pretty",
    default=True,
    show_default=True,
    help="Imprime el JSON con indentación legible o en una sola línea.",
)
def caps_dump(pretty: bool) -> None:
    """
    Muestra el manifest de capacidades de la AroFlo Connector App.
    """
    try:
        client = make_client()
    except AroFloConfigError as exc:
        click.echo("❌ Error de configuración:")
        click.echo(str(exc))
        raise SystemExit(1)

    manifest = build_capabilities_manifest(client)

    if pretty:
        click.echo(json.dumps(manifest, indent=2, ensure_ascii=False))
    else:
        click.echo(json.dumps(manifest, separators=(",", ":"), ensure_ascii=False))


def register_zone_clis() -> None:
    """
    Registra dinámicamente las CLIs de todas las zonas registradas.

    Estándar:
    - cada zona puede tener zones/<zone>/cli.py con register_cli(root, zone)
    - el CLI general no debe duplicar lógica por zona
    """
    try:
        client = make_client()
    except AroFloConfigError as exc:
        # No tumbamos el CLI completo: envcheck seguirá mostrando el error.
        log.debug("No se pudo construir AroFloClient para registrar zonas: %s", exc)
        return

    registry = build_registry(client)

    for code, zone in registry.items():
        module_path = f"{__package__}.zones.{code}.cli"
        try:
            mod = importlib.import_module(module_path)
        except ModuleNotFoundError:
            log.debug("Zona '%s' no tiene cli.py (saltando).", code)
            continue

        register_fn = getattr(mod, "register_cli", None)
        if callable(register_fn):
            register_fn(cli, zone)
            log.debug("Registrada CLI para zona '%s'.", code)
        else:
            log.debug("Zona '%s' tiene cli.py pero sin register_cli (saltando).", code)


# Registrar todas las zonas al importar el módulo
register_zone_clis()


if __name__ == "__main__":
    cli()
