import typer
import subprocess
import asyncio
import json
import os
import sys
import yaml
from pathlib import Path
from rich.console import Console
from rich.prompt import Prompt, Confirm
from warden.cli.utils import get_installed_version
from warden.analysis.application.project_structure_analyzer import ProjectStructureAnalyzer
from warden.cli.commands.install import install as run_install
from warden.cli.commands.init_helpers import configure_llm, configure_vector_db


console = Console()


def _generate_ignore_file(root: Path, meta):
    """Generate .wardenignore based on project type."""
    ignore_path = root / ".wardenignore"
    if ignore_path.exists():
        console.print("[dim].wardenignore exists, skipping.[/dim]")
        return

    content = [
        "# Warden Ignore File",
        "# Auto-generated by Smart Init",
        "",
        "# Version Control",
        ".git/",
        ".svn/",
        "",
        "# Warden",
        ".warden/reports/",
        ".warden/memory/",
        ".warden/embeddings/",
        "",
        "# Dependencies",
        "node_modules/",
        "venv/",
        ".venv/",
        "env/",
        "target/", # Rust
        "vendor/", # Go/PHP
        "dist/",
        "build/",
        "",
        "# IDEs",
        ".idea/",
        ".vscode/",
        "",
        "# Logs",
        "*.log",
        "",
        "# Language Specific"
    ]
    
    if meta.language == "python":
        content.extend(["__pycache__/", "*.pyc", "*.pyo", ".pytest_cache/", ".mypy_cache/", "htmlcov/"])
    elif meta.language in ["javascript", "typescript"]:
        content.extend([".next/", ".nuxt/", "coverage/", ".turbo/"])
    
    with open(ignore_path, "w") as f:
        f.write("\n".join(content))
    console.print("[green]Created .wardenignore (Smart Defaults)[/green]")

def _setup_semantic_search(config_path: Path):
    """
    Initialize semantic search:
    1. Check availability / Auto-install dependencies
    2. Index codebase
    Handles failures gracefully (Soft Failure).
    """
    console.print("\n[bold cyan]📚 Initializing Semantic Index...[/bold cyan]")
    try:
         # Load config
         with open(config_path) as f:
             final_config = yaml.safe_load(f)
             
         ss_config = final_config.get('semantic_search', {})
         if not ss_config.get('enabled'):
             return

         from warden.shared.services.semantic_search_service import SemanticSearchService
         
         # Reset singleton
         SemanticSearchService._instance = None
         service = SemanticSearchService(ss_config)
         
         # Inner function to perform indexing to avoid DRY violation
         async def run_indexing_if_files_exist():
             code_extensions = {'.py', '.js', '.ts', '.jsx', '.tsx', '.java', '.go', '.rs', '.cpp', '.c', '.h'}
             files = [f for f in Path.cwd().rglob("*") if f.is_file() and f.suffix in code_extensions and "node_modules" not in str(f) and ".venv" not in str(f) and ".git" not in str(f)]
             
             if not files:
                 console.print("[yellow]No code files found to index.[/yellow]")
                 return

             try:
                 with console.status(f"[bold green]Indexing {len(files)} files... (Ctrl+C to skip)[/bold green]") as spinner:
                    await service.index_project(Path.cwd(), files)
                 console.print(f"[green]✓ Semantic Index Ready ({len(files)} files)[/green]")
             except KeyboardInterrupt:
                 console.print("\n[yellow]⚠️  Indexing skipped by user.[/yellow]")
                 console.print("[dim]Run 'warden index' later to complete setup.[/dim]")

         if service.is_available():
             asyncio.run(run_indexing_if_files_exist())
         else:
              console.print("[yellow]Semantic service dependencies missing.[/yellow]")
              console.print("[dim]Auto-installing required dependencies (Mandatory for AI features)...[/dim]")
              try:
                  with console.status("[bold green]Installing dependencies...[/bold green]"):
                      subprocess.check_call([sys.executable, "-m", "pip", "install", "chromadb", "sentence-transformers"])
                  console.print("[green]Dependencies installed successfully. Retrying...[/green]")
                  
                  # Retry setup
                  service = SemanticSearchService(ss_config)
                  if service.is_available():
                      asyncio.run(run_indexing_if_files_exist())
                  else:
                      console.print("[red]Service unavailable even after install.[/red]")
                      
              except subprocess.CalledProcessError as e:
                   console.print(f"[red]Dependency installation failed.[/red]")
                   console.print("[dim]Please run manually: pip install 'warden-core[semantic]'[/dim]")
              except KeyboardInterrupt:
                   console.print("\n[yellow]⚠️  Installation skipped by user.[/yellow]")
              except Exception as e:
                  console.print(f"[red]Installation error: {e}[/red]")

    except Exception as e:
        console.print(f"[red]Warning: Failed to initialize index (Soft Failure): {e}[/red]")
        console.print("[dim]The project is initialized, but semantic features may be limited.[/dim]")

async def _create_baseline(root: Path, config_path: Path):
    """Run initial scan and save as baseline."""
    console.print("\n[bold blue]📉 Creating Baseline...[/bold blue]")
    console.print("[dim]Running initial scan to identify existing technical debt...[/dim]")
    
    try:
        from warden.cli_bridge.bridge import WardenBridge
        bridge = WardenBridge(project_root=root, config_path=str(config_path))
        
        # Run scan on current directory
        # We assume '.' finds all files via scan logic
        # Since execute_pipeline takes a file/dir path, we pass root string.
        result = await bridge.execute_pipeline(str(root))
        
        # Save baseline
        baseline_path = root / ".warden" / "baseline.json"
        
        # Extract issues (findings) from result
        # Result structure depends on bridge output. Usually {'success': ..., 'results': ...}
        # We simply save the whole result or a subset as baseline.
        # For now, save entire result.
        
        with open(baseline_path, "w") as f:
            json.dump(result, f, indent=2)
            
        issue_count = result.get("summary", {}).get("total_issues", 0)
        console.print(f"[green]Baseline created with {issue_count} existing issues.[/green]")
        console.print("[dim]Future scans will prioritize NEW issues.[/dim]")
        
    except Exception as e:
        console.print(f"[red]Failed to create baseline: {e}[/red]")

def init_command(
    ctx: typer.Context,
    force: bool = typer.Option(False, "--force", "-f", help="Force initialization even if config exists"),
    mode: str = typer.Option("normal", "--mode", "-m", help="Initialization mode (vibe, normal, strict)"),
    ci: bool = typer.Option(False, "--ci", help="Generate GitHub Actions CI workflow"),
    agent: bool = typer.Option(False, "--agent", help="Configure for AI Agents (Cursor/Claude)"),
) -> None:
    """
    Initialize Warden in the current directory with Smart Detection.
    """
    console.print("[bold blue]🛡️  Initializing Warden (Smart Mode)...[/bold blue]")

    warden_dir = Path(".warden")
    warden_dir.mkdir(parents=True, exist_ok=True)
    
    # --- Step 1: Detect Project ---
    console.print("[bold blue]🔍 Detecting project environment...[/bold blue]")
    
    # Use Core Analyzer
    analyzer = ProjectStructureAnalyzer(Path.cwd())
    # Run async analysis synchronously
    context = asyncio.run(analyzer.analyze_async())
    
    # Map ProjectContext to metadata expected by init
    # We create a simple object to hold the data compatible with previous logic
    class MetaAdapter:
        pass
    meta = MetaAdapter()
    meta.language = context.primary_language
    meta.frameworks = [context.framework.value] if context.framework.value != "none" else []
    meta.project_type = context.project_type.value
    meta.ci_providers = [] # Core analyzer detects this but stores in config_files/special_dirs differently?
    # PSA stores build tools in context.build_tools
    meta.build_tools = [bt.value for bt in context.build_tools]
    
    # Suggest frames logic (can be moved to helper or kept here for now)
    meta.suggested_frames = ["security", "architecturalconsistency", "config", "orphan"]
    if meta.project_type in ["api", "backend", "microservice"]:
         meta.suggested_frames.extend(["stress", "resilience"])
    # Helper to check for CI config files from context.config_files
    ci_files = {".github/workflows": "github-actions", ".gitlab-ci.yml": "gitlab-ci", "Jenkinsfile": "jenkins"}
    for f, p in ci_files.items():
        if f in context.config_files: meta.ci_providers.append(p)
    if meta.ci_providers: meta.suggested_frames.append("env-security")

    console.print(f"   [green]✓[/green] Language: [cyan]{meta.language}[/cyan]")
    console.print(f"   [green]✓[/green] Framework: [cyan]{', '.join(meta.frameworks) if meta.frameworks else 'None detected'}[/cyan]")
    console.print(f"   [green]✓[/green] Type: [cyan]{meta.project_type}[/cyan]")
    console.print(f"Suggested Frames: {', '.join(meta.suggested_frames)}")
    # The original code had an `except` block here, but with ProjectStructureAnalyzer,
    # we assume it either succeeds or raises an error that should propagate if not handled.
    # For now, we'll remove the try/except around detection as the new analyzer is more robust.
    # If specific errors need handling, they should be added here.

    # --- Step 2: Mode Selection ---
    console.print("\n[bold cyan]🎚️  Select Operation Mode[/bold cyan]")
    console.print("1. [bold green]Vibe Mode[/bold green] (Silent, Critical Only) - Best for active dev")
    console.print("2. [bold yellow]Normal Mode[/bold yellow] (High+Critical) - Standard PR checks")
    console.print("3. [bold red]Strict Mode[/bold red] (All Issues) - Zero tolerance / Security audit")
    
    mode_choice = Prompt.ask("Select Mode", choices=["1", "2", "3"], default="2")
    
    mode_config = {}
    if mode_choice == "1": # Vibe
        mode_config = {"fail_fast": False, "min_severity": "critical", "quiet": True}
        meta.suggested_frames = [f for f in meta.suggested_frames if f in ["security", "env-security"]] # Vibe uses minimal frames
    elif mode_choice == "3": # Strict
        mode_config = {"fail_fast": True, "min_severity": "low", "strict": True}
    else: # Normal
        mode_config = {"fail_fast": False, "min_severity": "high"}

    # Load existing config for defaults
    existing_config = {}
    config_path = warden_dir / "config.yaml"
    if config_path.exists():

        try:
            with open(config_path) as f:
                existing_config = yaml.safe_load(f) or {}
        except: pass

    # --- Step 3: LLM Config ---
    llm_config, new_env_vars = configure_llm(existing_config)
    
    # Update .env
    env_path = Path(".env")
    if new_env_vars:
        mode = "a" if env_path.exists() else "w"
        current_env = env_path.read_text() if env_path.exists() else ""
        with open(env_path, mode) as f:
            if mode == "a": f.write("\n")
            for k, v in new_env_vars.items():
                if k not in current_env:
                    f.write(f"{k}={v}\n")
        console.print("[green]Updated .env[/green]")

    provider = llm_config["provider"]
    model = llm_config["model"]

    # --- Step 4: Vector Database ---
    vector_config = configure_vector_db()


    # --- Step 4: Generate Config ---
    if not config_path.exists():

        
        # Build frames_config based on detection
        frames_config = {}
        for frame in meta.suggested_frames:
            frames_config[frame] = {"enabled": True}
        
        # Apply mode settings
        if mode_choice == "1": # Vibe
            for f in frames_config:
                 frames_config[f]["severity_threshold"] = "critical"
        
        config_data = {
            "version": "1.0.0",
            "project": {
                "name": Path.cwd().name,
                "language": meta.language,
                "type": meta.project_type,
                "frameworks": meta.frameworks
            },
            "dependencies": {
                "architectural": "latest",
                "security": "latest"
            },
            "llm": {
                "provider": provider,
                "model": model,
                "timeout": 300
            },
            "frames": meta.suggested_frames,
            "frames_config": frames_config,
            "settings": {
                "fail_fast": mode_config["fail_fast"],
                "enable_classification": True,
                "mode": ["vibe", "normal", "strict"][int(mode_choice)-1]
            },
            "semantic_search": vector_config
        }
        # Preserve Azure details if selected
        if provider == "azure":
            config_data['llm']['azure'] = {
                "endpoint": "${AZURE_OPENAI_ENDPOINT}",
                "api_key": "${AZURE_OPENAI_API_KEY}",
                "deployment_name": "${AZURE_OPENAI_DEPLOYMENT_NAME}",
                "api_version": "2024-02-15-preview"
            }
        
        # New root manifest
        root_config_path = Path.cwd() / "warden.yaml"
        with open(root_config_path, "w") as f:
            yaml.dump(config_data, f, default_flow_style=False)
        console.print(f"[green]Created project manifest: [bold]{root_config_path.name}[/bold][/green]")
        
        # Also symlink or copy to .warden/config.yaml for internal consistency if needed
        # But moving forward, let's use the root file as the main config
        config_path = root_config_path 

    else:
        # Config exists - Offer Update/Merge
        console.print(f"[yellow]Config file exists: {config_path}[/yellow]")
        if Confirm.ask("Update configuration with detected settings? (Merges frames & mode)", default=False):

            
            # Update Frames - merge new suggestion into existing
            existing_frames = set(existing_config.get('frames', []))
            new_frames = set(meta.suggested_frames)
            merged_frames = list(existing_frames.union(new_frames))
            existing_config['frames'] = merged_frames
            
            # Update Frames Config - enable new frames
            if 'frames_config' not in existing_config:
                existing_config['frames_config'] = {}
            
            for frame in meta.suggested_frames:
                if frame not in existing_config['frames_config']:
                    existing_config['frames_config'][frame] = {"enabled": True}
                    if mode_choice == "1":
                        existing_config['frames_config'][frame]["severity_threshold"] = "critical"
            
            # Update Project Metadata
            existing_config['project']['language'] = meta.language
            existing_config['project']['type'] = meta.project_type
            existing_config['project']['frameworks'] = meta.frameworks
            
            # Update Settings based on Mode
            if 'settings' not in existing_config: existing_config['settings'] = {}
            existing_config['settings']['fail_fast'] = mode_config["fail_fast"]
            existing_config['settings']['mode'] = ["vibe", "normal", "strict"][int(mode_choice)-1]
            if "min_severity" in mode_config:
                 existing_config['settings']['min_severity'] = mode_config["min_severity"]
                 
            # Ensure Semantic Search is present
            existing_config["semantic_search"] = vector_config
            
            # Save
            with open(config_path, "w") as f:
                yaml.dump(existing_config, f, default_flow_style=False)
            console.print("[green]Merged configuration successfully.[/green]")


    # --- Step 5: Ignore File ---
    _generate_ignore_file(Path.cwd(), meta)

    # --- Step 6: Example Rules ---
    rules_dir = warden_dir / "rules"
    rules_dir.mkdir(exist_ok=True)
    example_rule_path = rules_dir / "my_custom_rules.yaml"
    if not example_rule_path.exists():
        example_content = """# Example Custom Rule Definition
# Check https://github.com/warden-ai/docs for full syntax

rules:
  - id: "company-no-print"
    description: "Do not use print statements in production code"
    severity: "warning"
    patterns:
      - "print("
    exclude:
      - "tests/**"
      - "scripts/**"
"""
        with open(example_rule_path, "w") as f:
            f.write(example_content)
        console.print(f"[green]Created example rules: {example_rule_path}[/green]")

    # --- Step 7: Update Config with Comments ---
    # We re-read the just created/merged config to append comments if needed
    # Or ensures generation includes it.
    
    # If we created a new config in Step 4, we likely used yaml.dump.
    # yaml.dump removes comments. We need to append the example usage at the end manually
    # or handle it better.
    
    # Let's read the file as text and check if the comment exists, if not, append it.
    if config_path.exists():
        current_content = config_path.read_text()
        comment_block = """
# --- Custom Rules Configuration ---
# To enable your custom rules, uncomment the following lines:
#
# custom_rules:
#   - .warden/rules/my_custom_rules.yaml
#
"""
        if "custom_rules:" not in current_content:
            with open(config_path, "a") as f:
                f.write(comment_block)
            console.print("[green]Added custom rules example to config.yaml[/green]")

        # Semantic Search is now AUTO-ADDED, so no hint needed.
        
        # HINT: CI Output Formats
        if "ci:" not in current_content and "output:" not in current_content:
            ci_hint = """
# --- CI/CD Output Configuration ---
# Generate reports in multiple formats (Markdown, JSON, SARIF)
#
# ci:
#   enabled: true
#   output:
#     - format: markdown
#       path: .warden/reports/warden-report.md
#     - format: sarif
#       path: .warden/reports/warden-report.sarif
"""
            with open(config_path, "a") as f:
                f.write(ci_hint)

        console.print("[green]Added configuration examples (Rules, Semantic Search, CI)[/green]")

    # --- Step 8: Semantic Indexing ---
    _setup_semantic_search(config_path)

    # --- Step 9: Agent Configuration (New) ---
    if agent or Confirm.ask("\nConfigure for AI Agents (Cursor/Claude)?", default=False):
        try:
            from warden.cli.commands.init_helpers import configure_agent_tools
            configure_agent_tools(Path.cwd())
        except Exception as e:
            console.print(f"[red]Failed to configure agent tools: {e}[/red]")

    # --- Step 9: Baseline ---
    if Confirm.ask("\nCreate Baseline from current issues? (Recommended for existing projects)", default=True):
        try:
             asyncio.run(_create_baseline(Path.cwd(), config_path))
        except KeyboardInterrupt:
             console.print("\n[yellow]⚠️  Baseline creation skipped by user.[/yellow]")
        except Exception as e:
             console.print(f"[red]Warning: Failed to create baseline: {e}[/red]")

    # --- Step 9: CI/CD (Simplified) ---
    if ci or Confirm.ask("\nGenerate CI/CD Workflow?", default=False):
        github_dir = Path(".github/workflows")
        github_dir.mkdir(parents=True, exist_ok=True)
        workflow_path = github_dir / "warden-ci.yml"
        
        if not workflow_path.exists():
            # Use detected branch or default
            branch = "main"
            try:
                branch = subprocess.check_output(["git", "branch", "--show-current"], text=True).strip()
            except: pass
            
            content = f"""name: Warden Scan
on:
  push:
    branches: [{branch}]
  pull_request:
    branches: [{branch}]
jobs:
  warden:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-python@v4
        with:
          python-version: '3.11'
      - run: pip install warden-core
      - run: warden scan . --format sarif --output warden.sarif
        continue-on-error: {'true' if mode_choice == '1' else 'false'}
"""
            with open(workflow_path, "w") as f:
                f.write(content)
            console.print(f"[green]Created CI workflow: {workflow_path}[/green]")

    # --- Step 10: Install dependencies ---
    console.print("\n[bold blue]📦 Installing Frames & Rules...[/bold blue]")
    try:
        from warden.cli.commands.update import update_command
        
        # 1. Update Registry (to know what is Core)
        try:
             update_command()
        except:
             console.print("[yellow]Warning: Could not update registry. Core frames might be outdated.[/yellow]")

        # 2. Install (will pick up Core frames automatically)
        run_install(frame_id=None)
    except Exception as e:
        console.print(f"[red]Warning: Auto-install failed: {e}[/red]")
        console.print("[dim]Run 'warden install' manually to download frames.[/dim]")

    console.print("\n[bold green]✨ Warden Initialization Complete![/bold green]")
    console.print("Run [bold cyan]warden scan[/bold cyan] to start.")
