"""Project commands."""

from __future__ import annotations

import re
import sys

import typer
from rich import box
from rich.prompt import Prompt
from rich.table import Table

try:
    from shared import ConflictError, NotFoundError, PreconditionFailedError
except ImportError:
    from cli._vendor.shared import ConflictError, NotFoundError, PreconditionFailedError

from ..exceptions import CLIError
from ..options import json_option
from ..output import OutputFormatter
from ..styles import PRIMARY_TEXT_STYLE, SECONDARY_TEXT_STYLE
from ._auth import authenticated_client
from ._context import require_cli_context

PROJECT_KEY_REGEX = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")


def _is_tty() -> bool:
    return sys.stdin.isatty()


def _slugify(name: str) -> str:
    """Convert a project name to a valid project key."""
    slug = name.lower().strip()
    slug = re.sub(r"[\s_]+", "-", slug)  # spaces/underscores → hyphens
    slug = re.sub(r"[^a-z0-9-]", "", slug)  # remove invalid chars
    slug = re.sub(r"-+", "-", slug)  # collapse multiple hyphens
    slug = slug.strip("-")  # trim leading/trailing hyphens
    return slug


def _validate_key(key: str) -> None:
    """Validate that a project key matches the required format."""
    if len(key) < 3 or len(key) > 64:
        raise CLIError("Project key must be between 3 and 64 characters.")
    if not PROJECT_KEY_REGEX.match(key):
        raise CLIError(
            "Invalid project key. Must contain only lowercase letters, numbers, "
            "and hyphens (e.g., 'my-project')."
        )


def register(app: typer.Typer) -> None:
    projects_app = typer.Typer(help="Manage projects.")
    app.add_typer(projects_app, name="projects")

    @projects_app.command("list", help="List available projects.")
    @projects_app.command("ls", hidden=True)
    def list_projects(
        ctx: typer.Context,
        json_output: bool = json_option(),
    ) -> None:
        cli_ctx = require_cli_context(ctx)
        formatter = OutputFormatter(console=cli_ctx.console, json_mode=json_output)
        config = cli_ctx.load_config()
        with authenticated_client(cli_ctx, config) as client:
            projects = client.list_projects()

        if not projects:
            if json_output:
                formatter.print_json({"projects": []})
            else:
                cli_ctx.console.print("[yellow]No projects found.[/yellow]")
            return

        if json_output:
            json_projects = [
                {
                    "key": project.key,
                    "name": project.name,
                    "description": project.description,
                }
                for project in projects
            ]
            formatter.print_json({"projects": json_projects})
            return

        table = Table(
            title="Projects",
            box=box.SIMPLE_HEAVY,
            border_style=SECONDARY_TEXT_STYLE,
            header_style=SECONDARY_TEXT_STYLE,
        )
        table.add_column("Key", style=PRIMARY_TEXT_STYLE)
        table.add_column("Name", style=PRIMARY_TEXT_STYLE)
        table.add_column("Description", style=PRIMARY_TEXT_STYLE)
        for project in projects:
            table.add_row(project.key, project.name, project.description or "-")
        cli_ctx.console.print(table)

    @projects_app.command("current", help="Show the current default project.")
    def current(ctx: typer.Context) -> None:
        cli_ctx = require_cli_context(ctx)
        config = cli_ctx.load_config()
        if config.default_project:
            cli_ctx.console.print(
                f"[green]Default project:[/green] [bold]{config.default_project}[/bold]."
            )
        else:
            cli_ctx.console.print("[yellow]No default project set.[/yellow]")

    @projects_app.command("create", help="Create a new project.")
    def create_project(
        ctx: typer.Context,
        name: str = typer.Argument(..., help="Human-readable project name."),
        key: str = typer.Option(
            None,
            "--key",
            "-k",
            help="Explicit project key (default: slugified name).",
        ),
        description: str = typer.Option(
            None,
            "--description",
            "-d",
            help="Optional project description.",
        ),
    ) -> None:
        cli_ctx = require_cli_context(ctx)
        config = cli_ctx.load_config()

        # Determine project key
        project_key = key if key else _slugify(name)
        if not project_key:
            raise CLIError(
                "Could not generate a valid key from the project name. "
                "Please provide an explicit key with --key."
            )
        _validate_key(project_key)

        try:
            with authenticated_client(cli_ctx, config) as client:
                project = client.create_project(project_key, name, description)
        except ConflictError:
            raise CLIError(f"Project key '{project_key}' already exists.")

        cli_ctx.console.print(
            f"[green]Created project[/green] [bold]{project.key}[/bold] ({project.name})."
        )

        # Prompt to set as default
        if _is_tty():
            choice = Prompt.ask(
                "Set as default project?",
                choices=["y", "n"],
                default="y",
                case_sensitive=False,
            )
            if choice == "y":
                config = config.model_copy(update={"default_project": project.key})
                cli_ctx.save_config(config)
                cli_ctx.console.print(
                    f"[green]Default project set to[/green] [bold]{project.key}[/bold]."
                )

    @projects_app.command("delete", help="Delete a project.")
    def delete_project(
        ctx: typer.Context,
        key: str = typer.Argument(..., help="Project key to delete."),
        yes: bool = typer.Option(
            False,
            "--yes",
            "-y",
            help="Skip confirmation prompt for default project deletion.",
        ),
    ) -> None:
        cli_ctx = require_cli_context(ctx)
        config = cli_ctx.load_config()

        is_default = config.default_project == key

        # Confirm deletion of default project
        if is_default and not yes:
            if not _is_tty():
                raise CLIError(f"'{key}' is the default project. Use --yes to confirm deletion.")
            choice = Prompt.ask(
                f"'{key}' is your default project. Delete it?",
                choices=["y", "n"],
                default="n",
                case_sensitive=False,
            )
            if choice != "y":
                raise typer.Exit(code=1)

        try:
            with authenticated_client(cli_ctx, config) as client:
                # Fetch project to get ETag
                _, etag = client.get_project(key)
                # Delete with ETag
                client.delete_project(key, etag)
        except NotFoundError:
            raise CLIError(f"Project '{key}' not found.")
        except ConflictError:
            raise CLIError(
                f"Cannot delete project '{key}' with existing items or files. "
                "Remove all items first."
            )
        except PreconditionFailedError:
            raise CLIError("Project was modified. Please try again.")

        cli_ctx.console.print(f"[yellow]Deleted project[/yellow] [bold]{key}[/bold].")

        # Handle default project cleanup
        if is_default:
            if _is_tty():
                with authenticated_client(cli_ctx, config) as client:
                    projects = client.list_projects()
                if projects:
                    cli_ctx.console.print("\n[yellow]Your default project was deleted.[/yellow]")
                    choice = Prompt.ask(
                        "Set a new default project?",
                        choices=["y", "n"],
                        default="y",
                        case_sensitive=False,
                    )
                    if choice == "y":
                        cli_ctx.console.print("\nAvailable projects:")
                        for i, proj in enumerate(projects, 1):
                            cli_ctx.console.print(f"  {i}. {proj.key} ({proj.name})")
                        selection = Prompt.ask(
                            "Enter project number or key",
                        )
                        # Try to match by number or key
                        new_default = None
                        if selection.isdigit():
                            idx = int(selection) - 1
                            if 0 <= idx < len(projects):
                                new_default = projects[idx].key
                        else:
                            for proj in projects:
                                if proj.key == selection:
                                    new_default = proj.key
                                    break
                        if new_default:
                            config = config.model_copy(update={"default_project": new_default})
                            cli_ctx.save_config(config)
                            cli_ctx.console.print(
                                f"[green]Default project set to[/green] [bold]{new_default}[/bold]."
                            )
                        else:
                            # Clear default since project was deleted
                            config = config.model_copy(update={"default_project": None})
                            cli_ctx.save_config(config)
                            cli_ctx.console.print(
                                "[yellow]Invalid selection. Default project cleared.[/yellow]"
                            )
                    else:
                        # Clear default
                        config = config.model_copy(update={"default_project": None})
                        cli_ctx.save_config(config)
                else:
                    # No projects left, clear default
                    config = config.model_copy(update={"default_project": None})
                    cli_ctx.save_config(config)
            else:
                # Non-TTY: just clear the default
                config = config.model_copy(update={"default_project": None})
                cli_ctx.save_config(config)
