"""complyform init — interactive wizard + flag-only CI mode for greenfield projects."""

from __future__ import annotations

import json
import os
import re
import sys
from datetime import UTC, datetime
from pathlib import Path
from typing import Annotated

import typer
from rich.panel import Panel
from rich.prompt import Confirm, Prompt
from rich.table import Table

from complyform.cli.exit_codes import EXIT_SUCCESS
from complyform.cli.main import AppContext, app
from complyform.core.errors import (
    InitMissingFlagsError,
    InvalidCloudError,
    InvalidOrgIdError,
    InvalidProjectIdError,
    UnknownFrameworkError,
    UnknownServiceError,
)
from complyform.core.greenfield.config import InitConfig, ServiceEntry, save_init_config
from complyform.core.greenfield.service_catalog import load_service_catalog
from complyform.core.profiles.registry import _load_registry

_GF = "Greenfield (New Infrastructure)"

_CLOUDS = ("gcp", "aws", "azure")

# Validation regexes
_GCP_PROJECT_RE = re.compile(r"^[a-z][a-z0-9-]{4,28}[a-z0-9]$")
_AWS_ACCOUNT_RE = re.compile(r"^\d{12}$")
_AZURE_SUB_RE = re.compile(
    r"^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$"
)


def _validate_project_id(project: str, cloud: str) -> None:
    """Validate project ID format for the given cloud."""
    if cloud == "gcp" and not _GCP_PROJECT_RE.match(project):
        raise InvalidProjectIdError(project, cloud)
    if cloud == "aws" and not _AWS_ACCOUNT_RE.match(project):
        raise InvalidProjectIdError(project, cloud)
    if cloud == "azure" and not _AZURE_SUB_RE.match(project):
        raise InvalidProjectIdError(project, cloud)


def _validate_org_id(org: str | None) -> None:
    """Validate org ID is numeric or empty."""
    if org is not None and org != "" and not org.isdigit():
        raise InvalidOrgIdError(org)


def _validate_frameworks(framework_ids: list[str]) -> None:
    """Validate all framework IDs exist in registry."""
    registry = _load_registry()
    valid_ids = [fw.id for fw in registry.frameworks]
    unknown = [fid for fid in framework_ids if fid not in valid_ids]
    if unknown:
        raise UnknownFrameworkError(unknown, valid_ids)


def _validate_services(service_ids: list[str], cloud: str) -> None:
    """Validate all service IDs exist in the cloud's catalog."""
    catalog = load_service_catalog(cloud)
    valid_ids = catalog.list_service_ids()
    unknown = [sid for sid in service_ids if sid not in valid_ids]
    if unknown:
        raise UnknownServiceError(unknown, cloud)


def _is_interactive() -> bool:
    """Check if we should run in interactive mode."""
    if os.environ.get("CI", "").lower() in ("true", "1", "yes"):
        return False
    return sys.stdin.isatty()


@app.command(name="init", rich_help_panel=_GF)
def init_cmd(
    ctx: typer.Context,
    cloud: Annotated[
        str | None,
        typer.Option("--cloud", help="Cloud provider: gcp, aws, azure"),
    ] = None,
    project: Annotated[
        str | None,
        typer.Option("--project", help="Project/account/subscription ID"),
    ] = None,
    org: Annotated[
        str | None,
        typer.Option("--org", help="Organization ID (numeric, optional)"),
    ] = None,
    frameworks: Annotated[
        str | None,
        typer.Option("--frameworks", help="Comma-separated framework IDs"),
    ] = None,
    services: Annotated[
        str | None,
        typer.Option("--services", help="Comma-separated service IDs"),
    ] = None,
    region: Annotated[
        str | None,
        typer.Option("--region", help="Cloud region"),
    ] = None,
    output: Annotated[
        Path,
        typer.Option("--output", help="Output directory"),
    ] = Path("./infra"),
    format: Annotated[
        str,
        typer.Option("--format", help="Output format: terminal, json"),
    ] = "terminal",
) -> None:
    """Initialize a new compliant infrastructure project."""
    app_ctx: AppContext = ctx.obj

    # Determine mode
    all_flags = all(
        v is not None for v in (cloud, project, frameworks, services, region)
    )
    interactive = _is_interactive() and not all_flags

    if not interactive and not all_flags:
        missing: list[str] = []
        if cloud is None:
            missing.append("--cloud")
        if project is None:
            missing.append("--project")
        if frameworks is None:
            missing.append("--frameworks")
        if services is None:
            missing.append("--services")
        if region is None:
            missing.append("--region")
        raise InitMissingFlagsError(missing)

    if interactive:
        (
            cloud,
            project,
            org,
            frameworks_list,
            services_list,
            region,
            output,
        ) = _run_wizard(
            app_ctx,
            cloud,
            project,
            org,
            frameworks,
            services,
            region,
            output,
        )
    else:
        assert cloud is not None
        assert project is not None
        assert frameworks is not None
        assert services is not None
        assert region is not None

        if cloud not in _CLOUDS:
            raise InvalidCloudError(cloud)
        _validate_project_id(project, cloud)
        _validate_org_id(org)
        frameworks_list = [f.strip() for f in frameworks.split(",") if f.strip()]
        _validate_frameworks(frameworks_list)
        services_list = [s.strip() for s in services.split(",") if s.strip()]
        _validate_services(services_list, cloud)

    # Build service entries from catalog
    catalog = load_service_catalog(cloud)
    service_entries = []
    for sid in services_list:
        svc = catalog.get_service(sid)
        if svc is not None:
            service_entries.append(
                ServiceEntry(
                    id=svc.id,
                    display_name=svc.display_name,
                    category=svc.category,
                )
            )

    from complyform import __version__

    config = InitConfig(
        cloud=cloud,  # type: ignore[arg-type]
        project_id=project,
        org_id=org if org else None,
        region=region,
        frameworks=frameworks_list,
        services=service_entries,
        output_directory=str(output),
        generated_at=datetime.now(tz=UTC).isoformat(),
        cli_version=__version__,
        tier=app_ctx.config.tier,
    )

    config_path = output / "complyform.init.yaml"
    save_init_config(config, config_path)

    if format == "json":
        envelope = {
            "schema_version": 1,
            "command": "init",
            "cloud": cloud,
            "frameworks": frameworks_list,
            "services": services_list,
            "config_path": str(config_path),
        }
        sys.stdout.write(json.dumps(envelope, indent=2) + "\n")
    else:
        console = app_ctx.console
        console.print(
            Panel(
                f"Config written to: {config_path}\n"
                f"Cloud: {cloud}\n"
                f"Frameworks: {', '.join(frameworks_list)}\n"
                f"Services: {', '.join(services_list)}\n"
                f"\nNext: [bold]complyform generate --config={config_path}[/bold]",
                title="Init Complete",
                style="green",
            )
        )

    raise typer.Exit(EXIT_SUCCESS)


def _run_wizard(
    app_ctx: AppContext,
    cloud: str | None,
    project: str | None,
    org: str | None,
    frameworks: str | None,
    services: str | None,
    region: str | None,
    output: Path,
) -> tuple[str, str, str | None, list[str], list[str], str, Path]:
    """Run the 7-step interactive wizard."""
    console = app_ctx.console

    console.print(Panel("ComplyForm Init Wizard", style="blue"))

    # Step 1: Cloud
    if cloud is None or cloud not in _CLOUDS:
        cloud = Prompt.ask(
            "Cloud provider",
            choices=list(_CLOUDS),
            console=console,
        )
    if cloud not in _CLOUDS:
        raise InvalidCloudError(cloud)

    # Step 2: Project ID
    if project is None:
        project = Prompt.ask("Project ID", console=console)
    _validate_project_id(project, cloud)

    # Step 3: Org ID
    if org is None:
        org_input = Prompt.ask(
            "Organization ID (optional, press Enter to skip)",
            default="",
            console=console,
        )
        org = org_input if org_input else None
    _validate_org_id(org)

    # Step 4: Region
    if region is None:
        region = Prompt.ask("Region", console=console)

    # Step 5: Frameworks
    if frameworks is None:
        registry = _load_registry()
        table = Table(title="Available Frameworks")
        table.add_column("ID")
        table.add_column("Name")
        table.add_column("Tier")
        for fw in registry.frameworks:
            table.add_row(fw.id, fw.name, fw.tier)
        console.print(table)
        fw_input = Prompt.ask(
            "Frameworks (comma-separated IDs, min 1)",
            console=console,
        )
        frameworks_list = [f.strip() for f in fw_input.split(",") if f.strip()]
    else:
        frameworks_list = [f.strip() for f in frameworks.split(",") if f.strip()]
    _validate_frameworks(frameworks_list)

    # Step 6: Services
    if services is None:
        catalog = load_service_catalog(cloud)
        table = Table(title=f"Available Services ({cloud})")
        table.add_column("ID")
        table.add_column("Name")
        table.add_column("Category")
        for svc in catalog.services:
            table.add_row(svc.id, svc.display_name, svc.category)
        console.print(table)
        svc_input = Prompt.ask(
            "Services (comma-separated IDs, min 1)",
            console=console,
        )
        services_list = [s.strip() for s in svc_input.split(",") if s.strip()]
    else:
        services_list = [s.strip() for s in services.split(",") if s.strip()]
    _validate_services(services_list, cloud)

    # Step 7: Output directory
    output_input = Prompt.ask(
        "Output directory",
        default=str(output),
        console=console,
    )
    output = Path(output_input)

    # Confirmation
    console.print(
        Panel(
            f"Cloud: {cloud}\n"
            f"Project: {project}\n"
            f"Org: {org or '(none)'}\n"
            f"Region: {region}\n"
            f"Frameworks: {', '.join(frameworks_list)}\n"
            f"Services: {', '.join(services_list)}\n"
            f"Output: {output}",
            title="Summary",
            style="blue",
        )
    )
    if not Confirm.ask("Proceed?", console=console):
        raise typer.Abort()

    return cloud, project, org, frameworks_list, services_list, region, output
