"""Initialize gjalla in a repository."""

import json
import os
from pathlib import Path

import click
import httpx
from rich.prompt import Prompt, Confirm
from rich.table import Table

from gjalla_precommit.display.output import console, success, error, warning, info, panel
from gjalla_precommit.tools.git_tools import is_git_repo, get_remote_url


# Global config paths
GJALLA_DIR = Path.home() / ".gjalla"
CONFIG_PATH = GJALLA_DIR / "config.yaml"

# API timeout in seconds
API_TIMEOUT = 30.0


def load_global_config() -> dict:
    """Load global gjalla config from ~/.gjalla/config.yaml."""
    import yaml

    if not CONFIG_PATH.exists():
        return {
            "api_key": None,
            "api_url": "https://gjalla.io",
            "projects": {},
        }

    with open(CONFIG_PATH) as f:
        return yaml.safe_load(f) or {}


def save_global_config(config: dict) -> None:
    """Save global gjalla config to ~/.gjalla/config.yaml."""
    import yaml

    GJALLA_DIR.mkdir(parents=True, exist_ok=True)
    with open(CONFIG_PATH, "w") as f:
        yaml.dump(config, f, default_flow_style=False)


def _make_api_headers(api_key: str) -> dict:
    """Create headers for API requests.

    Args:
        api_key: The API key for authentication.

    Returns:
        Headers dict with authorization.
    """
    return {
        "x-api-key": api_key,
        "Content-Type": "application/json",
        "Accept": "application/json",
    }


def verify_api_key(api_key: str, api_url: str) -> bool:
    """Verify API key with the cloud service by listing projects.

    Args:
        api_key: The API key to verify.
        api_url: The API base URL.

    Returns:
        True if API key is valid, False otherwise.
    """
    try:
        # Use the projects endpoint to verify the key works
        response = httpx.get(
            f"{api_url}/api/agent/projects",
            headers=_make_api_headers(api_key),
            timeout=API_TIMEOUT,
        )
        return response.status_code == 200
    except httpx.RequestError:
        return False


def list_projects(api_key: str, api_url: str) -> list[dict] | None:
    """List all accessible projects.

    Args:
        api_key: The API key for authentication.
        api_url: The API base URL.

    Returns:
        List of project dicts with 'id', 'title', 'description', or None on error.
    """
    try:
        response = httpx.get(
            f"{api_url}/api/agent/projects",
            headers=_make_api_headers(api_key),
            timeout=API_TIMEOUT,
        )
        if response.status_code != 200:
            return None

        data = response.json()
        return data.get("projects", [])
    except (httpx.RequestError, json.JSONDecodeError):
        return None


def discover_project(remote_url: str, api_key: str, api_url: str) -> dict | None:
    """Discover project from git remote URL by matching against accessible projects.

    Args:
        remote_url: The git remote URL.
        api_key: The API key for authentication.
        api_url: The API base URL.

    Returns:
        Project info dict with 'id' and 'name', or None if not found.
    """
    # Extract repo name from URL for matching
    repo_name = None

    if remote_url.startswith("git@"):
        # git@github.com:org/repo.git
        parts = remote_url.split(":")
        if len(parts) == 2:
            repo_name = parts[1].replace(".git", "").split("/")[-1]
    elif remote_url.startswith("https://"):
        # https://github.com/org/repo.git
        repo_name = remote_url.replace(".git", "").split("/")[-1]

    if not repo_name:
        return None

    # Fetch accessible projects and try to match by name
    projects = list_projects(api_key, api_url)
    if not projects:
        return None

    # Try exact match first
    for project in projects:
        if project.get("title", "").lower() == repo_name.lower():
            return {
                "id": project["id"],
                "name": project["title"],
            }

    # Try partial match
    for project in projects:
        if repo_name.lower() in project.get("title", "").lower():
            return {
                "id": project["id"],
                "name": project["title"],
            }

    return None


def select_project_interactively(api_key: str, api_url: str) -> dict | None:
    """Show list of projects and let user select one.

    Args:
        api_key: The API key for authentication.
        api_url: The API base URL.

    Returns:
        Selected project info dict with 'id' and 'name', or None if cancelled.
    """
    projects = list_projects(api_key, api_url)
    if projects is None:
        error("Could not fetch projects from server. Check your API key and network connection.")
        return None

    # If no projects exist, direct user to dashboard
    if not projects:
        console.print()
        warning("No projects found in your Gjalla account.")
        console.print()
        info("To add a project:")
        info("  1. Go to your Gjalla dashboard at https://gjalla.io")
        info("  2. Click 'New Project' to create a project")
        info("  3. Run 'gjalla init' again to connect this repository")
        console.print()
        return None

    # Display projects in a table
    console.print()
    table = Table(title="Available Projects")
    table.add_column("#", justify="right", style="cyan")
    table.add_column("Name", style="bold")
    table.add_column("Description")

    for i, project in enumerate(projects, 1):
        desc = project.get("description", "")
        if len(desc) > 50:
            desc = desc[:47] + "..."
        table.add_row(str(i), project.get("title", ""), desc)

    console.print(table)
    console.print()

    # Let user select
    choice = Prompt.ask(
        "[bold]Select project number[/bold]",
        default="1",
    )

    try:
        index = int(choice) - 1
        if 0 <= index < len(projects):
            selected = projects[index]
            return {
                "id": selected["id"],
                "name": selected["title"],
            }
    except ValueError:
        pass

    error("Invalid selection")
    return None


@click.command()
@click.option(
    "--api-key",
    envvar="GJALLA_API_KEY",
    help="API key for gjalla cloud. Can also be set via GJALLA_API_KEY env var.",
)
@click.option(
    "--project-id",
    type=int,
    help="Project ID. If not provided, will be auto-discovered from git remote.",
)
def init(
    api_key: str | None,
    project_id: int | None,
) -> None:
    """Initialize gjalla in the current repository.

    This command sets up gjalla for your repository by:

    \b
    1. Checking git repository
    2. Configuring your API key
    3. Verifying API key
    4. Selecting your project from Gjalla
    5. Creating .gjalla project config
    6. Updating .gitignore
    7. Offering guardrails setup

    If you don't have a project yet, create one at https://gjalla.io first.
    """
    repo_path = Path.cwd()

    panel("gjalla Initialization", title="gjalla init")
    console.print()

    # Step 1: Verify we're in a git repo
    info("Step 1/7: Checking git repository...")
    if not is_git_repo(repo_path):
        error("Not a git repository. Please run 'gjalla init' from a git repository.")
        raise SystemExit(1)
    success("Git repository detected")
    console.print()

    # Load existing config
    config = load_global_config()
    # Environment variable takes precedence over config file
    api_url = os.environ.get("GJALLA_API_URL") or config.get("api_url", "https://gjalla.io")

    # Step 2: Get API key
    info("Step 2/7: Configuring API key...")
    if api_key:
        info("Using API key from command line or environment")
    elif config.get("api_key"):
        api_key = config["api_key"]
        info("Using existing API key from config")
    else:
        api_key = Prompt.ask(
            "[bold]Enter your gjalla API key[/bold]",
            password=True,
        )

    if not api_key:
        error("API key is required. Get one at https://gjalla.io/settings/api")
        raise SystemExit(1)
    success("API key configured")
    console.print()

    # Step 3: Verify API key
    info("Step 3/7: Verifying API key...")
    if not verify_api_key(api_key, api_url):
        error("Invalid API key. Please check your key and try again.")
        raise SystemExit(1)
    success("API key verified")
    console.print()

    # Step 4: Discover/select project
    info("Step 4/7: Selecting project...")
    project_info = None

    if project_id:
        info(f"Using provided project ID: {project_id}")
        project_info = {"id": project_id, "name": "unknown"}
    else:
        remote_url = get_remote_url(repo_path)
        if remote_url and not remote_url.startswith("Error:"):
            info(f"Found git remote: {remote_url}")
            discovered = discover_project(remote_url, api_key, api_url)

            if discovered:
                info(f"Discovered project: {discovered['name']}")
                if Confirm.ask("Is this the correct project?", default=True):
                    project_info = {"id": discovered["id"], "name": discovered["name"]}

        if not project_info:
            info("Select your project from the available options:")
            project_info = select_project_interactively(api_key, api_url)
            if not project_info:
                raise SystemExit(1)

    success(f"Selected project: {project_info.get('name', project_info['id'])}")
    console.print()

    # Save config
    config["api_key"] = api_key
    config["api_url"] = api_url
    if "projects" not in config:
        config["projects"] = {}
    config["projects"][str(repo_path)] = str(project_info["id"])
    save_global_config(config)

    # Step 5: Create .gjalla project config
    info("Step 5/7: Creating .gjalla project config...")
    gjalla_dir = repo_path / ".gjalla"

    # Migrate: if .gjalla is an old-format file, move it into the directory
    if gjalla_dir.is_file():
        old_content = gjalla_dir.read_text()
        gjalla_dir.unlink()
        gjalla_dir.mkdir(parents=True, exist_ok=True)
        (gjalla_dir / "config.json").write_text(old_content)
        info("Migrated .gjalla file → .gjalla/config.json")
    else:
        gjalla_dir.mkdir(parents=True, exist_ok=True)

    gjalla_config_data = {
        "project_id": project_info["id"],
        "api_url": api_url,
        "api_key_env": "GJALLA_API_KEY",
    }
    (gjalla_dir / "config.json").write_text(json.dumps(gjalla_config_data, indent=2) + "\n")
    success(f"Created .gjalla/config.json (project {project_info['id']})")
    console.print()

    # Step 6: Update .gitignore
    info("Step 6/7: Updating .gitignore...")
    gitignore_path = repo_path / ".gitignore"
    gitignore_content = gitignore_path.read_text() if gitignore_path.exists() else ""
    gitignore_lines = gitignore_content.splitlines()
    entries_to_add = [".commit-attestation.md", ".gjallalog"]
    added = []
    for entry in entries_to_add:
        if entry not in gitignore_lines:
            gitignore_lines.append(entry)
            added.append(entry)
    if added:
        gitignore_path.write_text("\n".join(gitignore_lines) + "\n")
        success(f"Added to .gitignore: {', '.join(added)}")
    else:
        success(".gitignore already up to date")
    console.print()

    # Step 7: Offer guardrails setup
    info("Step 7/7: Commit attestation guardrails...")
    info("This will add precommit hooks to your repository so agents must attest that their changes are compliant with your rules and architecture before committing.")
    if Confirm.ask("Enable?", default=True):
        from .setup import _ensure_gitignore
        from ..hooks.scripts import attestation_pre_commit_script, post_commit_upload_script, example_attestation
        from ..hooks.installer import install_hooks
        import stat

        # Write example attestation
        example_path = gjalla_dir / "example-attestation.md"
        example_path.write_text(example_attestation())
        success("Created .gjalla/example-attestation.md")

        scripts_dir = repo_path / "scripts"
        scripts_dir.mkdir(exist_ok=True)

        pre_path = scripts_dir / "gjalla-attestation-check.sh"
        pre_path.write_text(attestation_pre_commit_script())
        pre_path.chmod(pre_path.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)

        post_path = scripts_dir / "gjalla-post-commit-upload.sh"
        post_path.write_text(post_commit_upload_script())
        post_path.chmod(post_path.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)

        result = install_hooks(
            repo_path,
            pre_commit_command="bash scripts/gjalla-attestation-check.sh",
            post_commit_command="bash scripts/gjalla-post-commit-upload.sh",
        )
        success(f"Pre-commit: {result.pre_commit.message}")
        success(f"Post-commit: {result.post_commit.message}")
    else:
        info("Skipped. Run 'gjalla setup' later to enable guardrails.")
    console.print()

    # Build completion message
    completion_msg = "[green]Success! Agents will now have to attest that their changes are compliant with your rules and architecture before committing ✨[/green]\n\n"
    completion_msg += "Run 'gjalla sync' to refresh project context.\n"
    completion_msg += "Run 'gjalla setup' to reconfigure guardrails.\n"
    panel(completion_msg, title="gjalla initialized successfully!")
