"""Emergency location management commands for Teams Phone CLI.

This module provides CLI commands for listing, showing, and searching
emergency locations from the CSV cache.
"""

from __future__ import annotations

from enum import Enum
from typing import TYPE_CHECKING, Any, Optional

import typer

from teams_phone.cli.context import get_context
from teams_phone.exceptions import TeamsPhoneError
from teams_phone.infrastructure import CacheManager
from teams_phone.models import EmergencyLocation
from teams_phone.services import LocationService

if TYPE_CHECKING:
    from teams_phone.infrastructure import OutputFormatter

locations_app = typer.Typer(
    name="locations",
    help="Manage emergency locations.",
    no_args_is_help=True,
)


class SearchField(str, Enum):
    """Search field options for location search."""

    NAME = "name"
    ADDRESS = "address"
    CITY = "city"
    COMPANY = "company"


def _format_default(is_default: bool) -> str:
    """Format default status for display."""
    return "Yes" if is_default else "No"


def _location_to_list_dict(location: EmergencyLocation) -> dict[str, Any]:
    """Convert EmergencyLocation to dict for list/search table display."""
    return {
        "description": location.description or "—",
        "address": location.address or "—",
        "city": location.city or "—",
        "state": location.state_or_province or "—",
        "default": _format_default(location.is_default),
    }


def _location_to_json_dict(location: EmergencyLocation) -> dict[str, Any]:
    """Convert EmergencyLocation to dict for JSON output."""
    return {
        "location_id": location.location_id,
        "civic_address_id": location.civic_address_id,
        "description": location.description,
        "company_name": location.company_name,
        "address": location.address,
        "city": location.city,
        "state_or_province": location.state_or_province,
        "postal_code": location.postal_code,
        "country_or_region": location.country_or_region,
        "is_default": location.is_default,
    }


def _get_service() -> LocationService:
    """Create a LocationService instance."""
    cache_manager = CacheManager()
    return LocationService(cache_manager)


def _check_staleness(service: LocationService, formatter: OutputFormatter) -> None:
    """Check for staleness warning and display if needed."""
    staleness_warning = service.get_staleness_warning()
    if staleness_warning:
        formatter.warning(staleness_warning)


@locations_app.command("list")
def list_locations(
    ctx: typer.Context,
    city: Optional[str] = typer.Option(
        None,
        "--city",
        "-c",
        help="Filter by city.",
    ),
    limit: Optional[int] = typer.Option(
        None,
        "--limit",
        "-l",
        help="Limit results to n locations.",
    ),
    all_locations: bool = typer.Option(
        False,
        "--all",
        help="Fetch all locations (default behavior when no limit specified).",
    ),
) -> None:
    """List emergency locations from CSV cache."""
    cli_ctx = get_context(ctx)
    formatter = cli_ctx.get_output_formatter()

    # Validate mutually exclusive options
    if limit is not None and all_locations:
        formatter.error(
            "Cannot specify both --limit and --all",
            remediation="Use either --limit <n> to limit results or --all to fetch all locations.",
        )
        raise typer.Exit(5)  # ValidationError exit code

    try:
        service = _get_service()

        # Check for staleness before output
        _check_staleness(service, formatter)

        locations = service.list_locations()

        # Filter by city if specified
        if city:
            city_lower = city.lower()
            locations = [
                loc for loc in locations if (loc.city or "").lower() == city_lower
            ]

        # Apply limit if specified
        if limit is not None:
            locations = locations[:limit]

        if cli_ctx.json_output:
            formatter.json([_location_to_json_dict(loc) for loc in locations])
        else:
            if not locations:
                formatter.info("No locations found matching the specified criteria")
            else:
                formatter.table(
                    data=[_location_to_list_dict(loc) for loc in locations],
                    columns=[
                        "description",
                        "address",
                        "city",
                        "state",
                        "default",
                    ],
                    title="Emergency Locations",
                )
    except TeamsPhoneError as e:
        formatter.error(e.message, remediation=e.remediation)
        raise typer.Exit(e.exit_code)


@locations_app.command("show")
def show_location(
    ctx: typer.Context,
    location: str = typer.Argument(
        ...,
        help="Location identifier: ID (GUID) or description.",
    ),
) -> None:
    """Show detailed information for a specific emergency location."""
    cli_ctx = get_context(ctx)
    formatter = cli_ctx.get_output_formatter()

    try:
        service = _get_service()

        # Check for staleness before output
        _check_staleness(service, formatter)

        loc = service.validate_location(location)

        if cli_ctx.json_output:
            formatter.json(_location_to_json_dict(loc))
        else:
            # Identity section
            formatter.info(f"Description: {loc.description}")
            formatter.info(f"Location ID: {loc.location_id}")
            formatter.info(f"Civic Address ID: {loc.civic_address_id}")

            # Company section
            formatter.info("")
            formatter.info("Company:")
            formatter.info(f"  Name: {loc.company_name or '—'}")

            # Address section
            formatter.info("")
            formatter.info("Address:")
            formatter.info(f"  Street: {loc.address or '—'}")
            formatter.info(f"  City: {loc.city or '—'}")
            formatter.info(f"  State/Province: {loc.state_or_province or '—'}")
            formatter.info(f"  Postal Code: {loc.postal_code or '—'}")
            formatter.info(f"  Country: {loc.country_or_region or '—'}")

            # Default status
            formatter.info("")
            formatter.info(f"Default Location: {_format_default(loc.is_default)}")

    except TeamsPhoneError as e:
        formatter.error(e.message, remediation=e.remediation)
        raise typer.Exit(e.exit_code)


@locations_app.command("search")
def search_locations(
    ctx: typer.Context,
    query: str = typer.Argument(
        ...,
        help="Search term (partial match supported, case-insensitive).",
    ),
    field: Optional[SearchField] = typer.Option(
        None,
        "--field",
        "-f",
        help="Limit search to: name, address, city, company.",
    ),
    limit: int = typer.Option(
        25,
        "--limit",
        "-l",
        help="Maximum results (default: 25).",
    ),
) -> None:
    """Search emergency locations by name, address, or city."""
    cli_ctx = get_context(ctx)
    formatter = cli_ctx.get_output_formatter()

    try:
        service = _get_service()

        # Check for staleness before output
        _check_staleness(service, formatter)

        # Get all locations for filtered search
        all_locs = service.list_locations()
        query_lower = query.lower()
        results: list[EmergencyLocation] = []

        for loc in all_locs:
            matched = False

            if field is None:
                # Search across all fields
                if query_lower in (loc.description or "").lower():
                    matched = True
                elif query_lower in (loc.address or "").lower():
                    matched = True
                elif query_lower in (loc.city or "").lower():
                    matched = True
                elif query_lower in (loc.company_name or "").lower():
                    matched = True
            elif field == SearchField.NAME:
                if query_lower in (loc.description or "").lower():
                    matched = True
            elif field == SearchField.ADDRESS:
                if query_lower in (loc.address or "").lower():
                    matched = True
            elif field == SearchField.CITY:
                if query_lower in (loc.city or "").lower():
                    matched = True
            elif field == SearchField.COMPANY:
                if query_lower in (loc.company_name or "").lower():
                    matched = True

            if matched:
                results.append(loc)

        # Apply limit
        results = results[:limit]

        if cli_ctx.json_output:
            formatter.json([_location_to_json_dict(loc) for loc in results])
        else:
            if not results:
                formatter.info(f"No locations found matching '{query}'")
            else:
                formatter.table(
                    data=[_location_to_list_dict(loc) for loc in results],
                    columns=[
                        "description",
                        "address",
                        "city",
                        "state",
                        "default",
                    ],
                    title=f"Search Results for '{query}'",
                )
    except TeamsPhoneError as e:
        formatter.error(e.message, remediation=e.remediation)
        raise typer.Exit(e.exit_code)
