import click
import subprocess
import shutil
from pathlib import Path
from .config_parser import CppProjectConfig

@click.group()
def cli():
    pass

@cli.command()
def init():
    config_file = Path.cwd() / "cpp.proj"
    if config_file.exists():
        click.echo("cpp.proj already exists")
        return

    CppProjectConfig.create_default('cpp.proj')
    click.echo("Created cpp.proj configuration")
    click.echo("Created include/ directory")
    click.echo("Created plugins/ directory")

@cli.command()
@click.option('--clean', is_flag=True, help='Force clean rebuild (ignore incremental)')
@click.option('--full', is_flag=True, help='Complete rebuild including plugin_gen.exe and all caches')
@click.option('--verbose', is_flag=True, help='Verbose output')
@click.option('--no-incremental', is_flag=True, help='Disable incremental builds')
@click.option('--parallel/--no-parallel', default=True, help='Enable/disable parallel compilation (default: enabled)')
@click.option('--jobs', '-j', type=int, default=4, help='Max parallel jobs (default: 4)')
@click.option('--modules', '-m', multiple=True, help='Specific modules to rebuild')
def rebuild(clean, full, verbose, no_incremental, parallel, jobs, modules):
    """v2.0: Rebuild C++ modules with incremental and parallel support."""
    from ..core.build_manager import BuildManager
    from ..core.error_formatter import BuildErrorFormatter

    project_root = Path.cwd()
    config = CppProjectConfig()

    compiler = detect_compiler()
    build_dir = config.get_build_dir(compiler)

    # Full rebuild: delete everything including generator
    if full and build_dir.exists():
        click.echo("Full rebuild: Deleting entire build directory including plugin_gen.exe...")
        shutil.rmtree(build_dir)
        clean = True  # Force clean mode

    # Clean if requested
    elif clean and build_dir.exists():
        click.echo("Cleaning build directory...")
        shutil.rmtree(build_dir)

    build_dir.mkdir(parents=True, exist_ok=True)
    config.update_base_dir(build_dir)

    click.echo(f"Build directory: {build_dir}")

    # v2.0: Show build configuration
    if verbose:
        click.echo(f"Incremental: {not no_incremental and not clean}")
        click.echo(f"Parallel: {parallel}")
        if parallel:
            click.echo(f"Max jobs: {jobs}")
        if modules:
            click.echo(f"Target modules: {', '.join(modules)}")

    click.echo("Building C++ modules...")

    try:
        builder = BuildManager(project_root, build_dir, config)

        # v2.0: Use new rebuild() method
        success = builder.rebuild(
            modules=list(modules) if modules else None,
            incremental=not no_incremental,
            parallel=parallel,
            clean=clean,
            verbose=verbose
        )

        if success:
            click.echo("\n✓ Build completed successfully!")
        else:
            click.echo("\n✗ Build failed", err=True)
            raise click.Abort()

    except Exception as e:
        # v2.0: Enhanced error messages
        error_msg = str(e)

        # Check for specific error types
        if "circular dependency" in error_msg.lower():
            click.echo("\n" + error_msg, err=True)
        elif "depends on unknown module" in error_msg.lower():
            click.echo("\n" + error_msg, err=True)
        else:
            click.echo(f"\n✗ Build failed: {e}", err=True)

        if verbose:
            import traceback
            traceback.print_exc()

        raise click.Abort()

@cli.command()
@click.argument('module_name')
def add(module_name):
    """Create a new module template."""
    plugins_dir = Path("plugins")
    if not plugins_dir.exists():
        click.echo("Error: plugins/ directory not found")
        return

    cp_file = plugins_dir / f"{module_name}.cp"
    if cp_file.exists():
        click.echo(f"Module {module_name} already exists")
        return

    template = f"""SOURCE(include/{module_name}.cpp) {module_name}

PUBLIC(
    {module_name} FUNC(example_function)
)
"""

    with open(cp_file, 'w') as f:
        f.write(template)

    click.echo(f"Created {cp_file}")
    click.echo(f"Now create include/{module_name}.cpp with your C++ code")

@cli.command()
def list():
    """List all available C++ modules."""
    from ..core.build_manager import BuildManager

    project_root = Path.cwd()
    config = CppProjectConfig()
    compiler = detect_compiler()
    build_dir = config.get_build_dir(compiler)

    if not build_dir.exists():
        click.echo("No build directory found. Run 'python -m includecpp rebuild' first.")
        return

    try:
        builder = BuildManager(project_root, build_dir, config)
        registry = builder._load_registry()

        modules = registry.get('modules', {})
        if not modules:
            click.echo("No modules found. Run 'python -m includecpp rebuild' to build modules.")
            return

        click.echo(f"\nAvailable modules ({len(modules)}):")
        click.echo("=" * 60)

        for module_name, module_info in sorted(modules.items()):
            # Get source count
            sources = module_info.get('sources', [])
            source_count = len(sources)

            # Get dependency count
            deps = module_info.get('dependencies', [])
            dep_count = len(deps)

            # Get struct/class/function counts
            structs = module_info.get('structs', [])
            classes = module_info.get('classes', [])
            functions = module_info.get('functions', [])

            click.echo(f"\n  {module_name}")
            click.echo(f"    Sources: {source_count} file(s)")

            if dep_count > 0:
                dep_names = [d.get('target', '?') for d in deps]
                click.echo(f"    Dependencies: {', '.join(dep_names)}")

            if structs:
                click.echo(f"    Structs: {len(structs)}")
            if classes:
                click.echo(f"    Classes: {len(classes)}")
            if functions:
                click.echo(f"    Functions: {len(functions)}")

        click.echo("\n" + "=" * 60)
        click.echo(f"Total: {len(modules)} module(s)\n")

    except Exception as e:
        click.echo(f"Error: {e}", err=True)

@cli.command()
@click.argument('module_name')
def info(module_name):
    """Show detailed information about a module."""
    from ..core.build_manager import BuildManager

    project_root = Path.cwd()
    config = CppProjectConfig()
    compiler = detect_compiler()
    build_dir = config.get_build_dir(compiler)

    if not build_dir.exists():
        click.echo("No build directory found. Run 'python -m includecpp rebuild' first.")
        return

    try:
        builder = BuildManager(project_root, build_dir, config)
        registry = builder._load_registry()

        modules = registry.get('modules', {})
        if module_name not in modules:
            click.echo(f"Module '{module_name}' not found.")
            click.echo(f"Available modules: {', '.join(sorted(modules.keys()))}")
            return

        module_info = modules[module_name]

        click.echo(f"\nModule: {module_name}")
        click.echo("=" * 60)

        # Sources
        sources = module_info.get('sources', [])
        click.echo(f"\nSources ({len(sources)}):")
        for src in sources:
            click.echo(f"  • {src}")

        # Dependencies
        deps = module_info.get('dependencies', [])
        if deps:
            click.echo(f"\nDependencies ({len(deps)}):")
            for dep in deps:
                target = dep.get('target', '?')
                types = dep.get('types', [])
                if types:
                    click.echo(f"  • {target} (types: {', '.join(types)})")
                else:
                    click.echo(f"  • {target}")

        # Structs
        structs = module_info.get('structs', [])
        if structs:
            click.echo(f"\nStructs ({len(structs)}):")
            for struct in structs:
                name = struct.get('name', '?')
                is_template = struct.get('is_template', False)
                if is_template:
                    template_types = struct.get('template_types', [])
                    click.echo(f"  • {name}<{', '.join(template_types)}>")
                else:
                    click.echo(f"  • {name}")

                fields = struct.get('fields', [])
                if fields:
                    for field in fields:
                        field_type = field.get('type', '?')
                        field_name = field.get('name', '?')
                        click.echo(f"      - {field_type} {field_name}")

        # Classes
        classes = module_info.get('classes', [])
        if classes:
            click.echo(f"\nClasses ({len(classes)}):")
            for cls in classes:
                name = cls.get('name', '?')
                methods = cls.get('methods', [])
                click.echo(f"  • {name}")
                if methods:
                    click.echo(f"      Methods: {', '.join(methods)}")

        # Functions
        functions = module_info.get('functions', [])
        if functions:
            click.echo(f"\nFunctions ({len(functions)}):")
            for func in functions:
                name = func.get('name', '?')
                click.echo(f"  • {name}()")

        # Build info
        last_built = module_info.get('last_built', 'Never')
        build_time = module_info.get('build_time_ms', 0)
        click.echo(f"\nLast Built: {last_built}")
        if build_time > 0:
            click.echo(f"Build Time: {build_time}ms")

        click.echo("=" * 60 + "\n")

    except Exception as e:
        click.echo(f"Error: {e}", err=True)

@cli.command()
@click.argument('module_name')
def install(module_name):
    """Install a module from GitHub: https://github.com/liliassg/IncludeCPP/minstall/{module}"""
    import tempfile
    import urllib.request
    import urllib.error

    # Check if include and plugins directories exist
    include_dir = Path.cwd() / "include"
    plugins_dir = Path.cwd() / "plugins"

    if not include_dir.exists() or not plugins_dir.exists():
        click.secho("Error: Not in a valid IncludeCPP project directory.", fg='red', err=True)
        click.echo("Run 'python -m includecpp init' first.")
        return

    click.echo("=" * 60)
    click.secho(f"Collecting {module_name}", fg='cyan', bold=True)
    click.echo("=" * 60)
    click.echo(f"Source: https://github.com/liliassg/IncludeCPP/tree/main/minstall/{module_name}")
    click.echo()

    # GitHub raw content base URL
    base_url = f"https://raw.githubusercontent.com/liliassg/IncludeCPP/main/minstall/{module_name}/"

    # Try to download .cp, .cpp, and .h files
    files_to_download = [
        (f"{module_name}.cp", plugins_dir, "Plugin definition"),
        (f"{module_name}.cpp", include_dir, "C++ implementation"),
        (f"{module_name}.h", include_dir, "C++ header")
    ]

    downloaded = []

    for filename, target_dir, description in files_to_download:
        url = base_url + filename
        target_path = target_dir / filename

        try:
            click.echo(f"  Collecting {filename:20} ({description})...", nl=False)
            with urllib.request.urlopen(url) as response:
                content = response.read()

                # Write to target directory
                with open(target_path, 'wb') as f:
                    f.write(content)

                downloaded.append(filename)
                file_size_kb = len(content) / 1024
                click.secho(f" OK ({file_size_kb:.1f} KB)", fg='green')

        except urllib.error.HTTPError as e:
            if e.code == 404:
                # File doesn't exist, skip (optional file like .h)
                click.secho(" SKIP (optional)", fg='yellow')
            else:
                click.secho(f" FAILED ({e})", fg='red', err=True)
        except Exception as e:
            click.secho(f" FAILED ({e})", fg='red', err=True)

    click.echo()

    if not downloaded:
        click.secho(f"Error: Module '{module_name}' not found or no files available.", fg='red', err=True)
        click.echo(f"Check https://github.com/liliassg/IncludeCPP/tree/main/minstall for available modules.")
        return

    click.secho(f"Successfully installed {len(downloaded)} file(s):", fg='green', bold=True)
    for f in downloaded:
        click.echo(f"  - {f}")

    click.echo()
    click.secho(f"Module '{module_name}' installed successfully!", fg='green', bold=True)
    click.echo("Run 'python -m includecpp rebuild' to build the module.")
    click.echo("=" * 60)

def detect_compiler():
    for compiler in ['g++', 'clang++', 'cl']:
        if shutil.which(compiler):
            return compiler.replace('++', '').replace('cl', 'msvc')
    return 'gcc'

if __name__ == '__main__':
    cli()
