import subprocess
import os
import sys
from pathlib import Path
from rich.console import Console
import typer
from enum import Enum

class ProjectType(str, Enum):
    APP = "app"
    LIB = "lib"

console = Console()


def initialize_project_interactive() -> tuple[str, bool, bool, bool]:
    """Interactive wizard for project initialization."""
    console.print("[dim italic]Hint: Use '.' to initialize in the current directory.[/dim italic]")
    project_name = typer.prompt("Project Name or Directory", default="my-sibi-project")
    
    # Check if directory exists
    if (Path(os.getcwd()) / project_name).exists() and project_name != ".":
        console.print(f"[yellow]Warning: Directory '{project_name}' already exists.[/yellow]")
        if not typer.confirm("Do you want to continue in this directory?"):
            raise typer.Abort()

    project_type_val = typer.prompt(
        "Project Type", 
        type=str, 
        default=ProjectType.APP.value
    )
    # Validate and convert back to Enum
    try:
        project_type = ProjectType(project_type_val)
    except ValueError:
        console.print(f"[red]Error: Invalid project type '{project_type_val}'. Valid options are: {', '.join([e.value for e in ProjectType])}[/red]")
        raise typer.Abort()
        
    lib = project_type == ProjectType.LIB
    app = project_type == ProjectType.APP

    use_osmnx = False
    if app:
        use_osmnx = typer.confirm("Do you need OSMnx (geospatial) support?", default=False)
    
    return project_name, lib, app, use_osmnx


def initialize_project(
    project_name: str | None = None,
    lib: bool = False,
    app: bool = False,
    use_osmnx: bool = False,
) -> None:
    """
    Initialize a new Sibi Flux project with the standard directory structure and configuration.

    Args:
        project_name: The name of the project directory to create.
        lib: If True, initialize as a library project via `uv init --lib`.
        app: If True, initialize as an application project via `uv init --app`.
             Note: If `lib` is False, the default behavior will be to include `--app`
             unless `app` is explicitly False. (Aligned with CLI defaults).
    """
    project_path = Path(os.getcwd()) / project_name

    # Handle "." case
    is_current_dir = project_name == "."
    if is_current_dir:
        project_path = Path(os.getcwd())

    if project_path.exists() and not is_current_dir:
        console.print(f"[red]Error: Directory '{project_name}' already exists.[/red]")
        raise typer.Exit(code=1)

    display_name = project_path.name if is_current_dir else project_name
    console.print(
        f"[bold blue]Initializing Sibi Flux project: {display_name}[/bold blue]"
    )

    # 1. Create Directory
    try:
        # Run `uv init`
        # if project_name match ".", pass it directly.
        cmd = ["uv", "init"]
        if not is_current_dir:
            cmd.append(project_name)

        if lib:
            cmd.append("--lib")
        else:
            cmd.append("--app")

        console.print(f"Running: {' '.join(cmd)}")
        subprocess.check_call(cmd)

        # 2. Add sibi-flux dependency
        console.print(f"Adding sibi-flux dependency...")
        try:
            from importlib.metadata import version

            ver = version("sibi-flux")
            pkg_spec = f"sibi-flux>={ver}"
        except Exception:
            pkg_spec = "sibi-flux@latest"

        subprocess.check_call(["uv", "add", pkg_spec], cwd=project_path)

        # 2b. Add dev dependencies
        dev_deps = [
            "black",
            "notebook",
            "pytest",
            "ruff",
            "httpx",
            "poethepoet",
            "PyYAML",
            "uvicorn",
        ]
        console.print(f"Adding dev dependencies: {', '.join(dev_deps)}...")
        subprocess.check_call(["uv", "add", "--dev"] + dev_deps, cwd=project_path)

        # 3. Create Scaffolding Folders (conf, blueprints, .flux, _gen)
        (project_path / "flux").mkdir(exist_ok=True)
        (project_path / "flux" / "metadata").mkdir(exist_ok=True)
        (project_path / "_gen").mkdir(exist_ok=True)
        (project_path / "_gen" / "assets").mkdir(exist_ok=True)
        
        # Touch gitkeeps
        (project_path / "conf" / ".gitkeep").touch() # Keep conf gitkeep
        (project_path / "blueprints" / ".gitkeep").touch()
        (project_path / "flux" / "metadata" / ".gitkeep").touch()
        (project_path / "_gen" / "assets" / ".gitkeep").touch()
        # No legacy dataobjects folder created by default in new projects
        # (project_path / "generators" / "datacubes" / ".gitkeep").touch() # Not needed if we write params

        # 4. Write YAML templates to conf/
        try:
            from importlib import resources as importlib_resources
            pass
        except ImportError:
            import importlib_resources  # type: ignore

        # A. discovery_params.yaml
        try:
            ref = importlib_resources.files("sibi_flux.init.templates").joinpath(
                "discovery_params.yaml"
            )
            template_content = ref.read_text()
        except AttributeError:
            template_content = importlib_resources.read_text(
                "sibi_flux.init.templates", "discovery_params.yaml"
            )

        template_content = template_content.replace(".env.linux", ".env.local")
        (project_path / "conf" / "discovery_params.yaml").write_text(template_content)

        # B. discovery_params.yaml (Conditional handling omitted for brevity but should be consistent)
        # B1. osmnx.yaml (Conditional)
        if use_osmnx:
            try:
                ref_osmnx = importlib_resources.files("sibi_flux.init.templates").joinpath(
                    "osmnx.yaml"
                )
                osmnx_content = ref_osmnx.read_text()
            except AttributeError:
                osmnx_content = importlib_resources.read_text(
                    "sibi_flux.init.templates", "osmnx.yaml"
                )
            (project_path / "conf" / "osmnx.yaml").write_text(osmnx_content)

        # C. main.py (Conditional)
        if app:
            try:
                ref_main = importlib_resources.files("sibi_flux.init.templates").joinpath(
                    "main.py.template"
                )
                main_content = ref_main.read_text()
            except AttributeError:
                main_content = importlib_resources.read_text(
                    "sibi_flux.init.templates", "main.py.template"
                )

            osmnx_import = ""
            osmnx_bootstrap = ""
            if use_osmnx:
                osmnx_import = "from conf.osmnx import ensure_initialized"
                osmnx_bootstrap = """    print('Bootstrapping OSMnx data (with 10s timeout)...')
    try:
        await asyncio.wait_for(asyncio.to_thread(ensure_initialized), timeout=10.0)
        print('OSMnx data ready.')
    except asyncio.TimeoutError:
        print('OSMnx bootstrap timed out - continuing in background...')
    except Exception as e:
        print(f'OSMnx bootstrap failed: {e} - continuing...')"""

            # Clean up placeholders and extra newlines
            main_content = main_content.replace("{{ OSMNX_IMPORT }}", osmnx_import)
            
            # If bootstrapping is empty, remove the line completely
            if not osmnx_bootstrap:
                main_content = main_content.replace("{{ OSMNX_BOOTSTRAP }}\n", "")
            else:
                main_content = main_content.replace("{{ OSMNX_BOOTSTRAP }}", osmnx_bootstrap)
            
            # Final collapse of redundant newlines
            while "\n\n\n" in main_content:
                main_content = main_content.replace("\n\n\n", "\n\n")

            (project_path / "main.py").write_text(main_content)
        (project_path / ".env.local").touch()

        # 6. Create/Update .gitignore
        gitignore_path = project_path / ".gitignore"
        gitignore_content = ""
        if gitignore_path.exists():
            gitignore_content = gitignore_path.read_text()

        # Ensure .env is ignored
        if ".env" not in gitignore_content:
            with open(gitignore_path, "a") as f:
                if gitignore_content and not gitignore_content.endswith("\n"):
                    f.write("\n")
                f.write("\n# Environment variables\n.env\n.env.local\n.env.*\n")

        if not gitignore_path.exists():
            gitignore_path.write_text(
                "# Sibi Flux\n.env\n.env.local\n.env.*\n__pycache__/\n*.pyc\n.DS_Store\n"
            )

        # 7. Configure poe tasks in pyproject.toml
        pyproject_path = project_path / "pyproject.toml"
        if pyproject_path.exists():
            with open(pyproject_path, "a") as f:
                f.write("\n\n[tool.poe.tasks]\n")
                f.write('dc-sync = "sibi-flux dc apply"\n')
                f.write('dc-init = "sibi-flux dc prepare"\n')
                f.write(
                    'dc-discover = "sibi-flux dc prepare"\n'
                )
                f.write('dc-scan = "sibi-flux dc prepare"\n')
                f.write('dc-match = "sibi-flux dc prepare"\n')
                f.write('dc-map = "sibi-flux dc prepare"\n')
                f.write('\n')
                f.write('readers-prepare = "sibi-flux readers prepare"\n')
                f.write('readers-apply = "sibi-flux readers apply"\n')
                f.write('readers-workflow = "sibi-flux readers workflow"\n')

        console.print(
            f"[bold green]Successfully initialized {project_name}![/bold green]"
        )
        console.print(
            f"Created directories: conf/, blueprints/, flux/, _gen/assets/"
        )
        console.print(
            f"Created files: .env.local, .gitignore, conf/discovery_params.yaml"
        )
        console.print(f"cd {project_name}")

    except subprocess.CalledProcessError as e:
        console.print(f"[red]Command failed with exit code {e.returncode}[/red]")
        raise typer.Exit(code=e.returncode)
    except FileNotFoundError:
        console.print(
            "[red]Error: 'uv' command not found. Please ensure uv is installed and in your PATH.[/red]"
        )
        raise typer.Exit(code=1)
