"""Core MPMInitCommand class for project initialization.

This module contains the main command class responsible for orchestrating
the MPM project initialization process, including configuration generation,
agent delegation, and workflow management.
"""

import contextlib
import subprocess
import tempfile
from pathlib import Path
from typing import Any, Dict, List, Optional

from rich.console import Console
from rich.progress import Progress, SpinnerColumn, TextColumn

from claude_mpm.core.enums import OperationResult
from claude_mpm.core.logging_utils import get_logger
from claude_mpm.services.project.archive_manager import ArchiveManager
from claude_mpm.services.project.documentation_manager import DocumentationManager
from claude_mpm.services.project.enhanced_analyzer import EnhancedProjectAnalyzer
from claude_mpm.services.project.project_organizer import ProjectOrganizer
from claude_mpm.utils.display_helper import DisplayHelper

# Import from sibling modules in the mpm_init package
from . import display, git_activity, modes, prompts

logger = get_logger(__name__)


class MPMInitCommand:
    """Initialize projects for optimal Claude Code and Claude MPM usage."""

    def __init__(self, project_path: Optional[Path] = None):
        """Initialize the MPM-Init command."""
        self.project_path = project_path or Path.cwd()
        self.claude_mpm_script = self._find_claude_mpm_script()
        self.console = Console()

        # Initialize service components
        self.doc_manager = DocumentationManager(self.project_path)
        self.organizer = ProjectOrganizer(self.project_path)
        self.archive_manager = ArchiveManager(self.project_path)
        self.analyzer = EnhancedProjectAnalyzer(self.project_path)
        self.display = DisplayHelper(self.console)

    def initialize_project(
        self,
        project_type: Optional[str] = None,
        framework: Optional[str] = None,
        force: bool = False,
        verbose: bool = False,
        ast_analysis: bool = True,
        update_mode: bool = False,
        review_only: bool = False,
        organize_files: bool = False,
        preserve_custom: bool = True,
        skip_archive: bool = False,
        dry_run: bool = False,
        quick_update: bool = False,
        catchup: bool = False,
        non_interactive: bool = False,
        days: int = 30,
        export: Optional[str] = None,
    ) -> Dict:
        """
        Initialize project with Agentic Coder Optimizer standards.

        Args:
            project_type: Type of project (web, api, cli, library, etc.)
            framework: Specific framework if applicable
            force: Force initialization even if project already configured
            verbose: Show detailed output
            ast_analysis: Enable AST analysis for enhanced documentation
            update_mode: Update existing CLAUDE.md instead of recreating
            review_only: Review project state without making changes
            organize_files: Organize misplaced files into proper directories
            preserve_custom: Preserve custom sections when updating
            skip_archive: Skip archiving existing files
            dry_run: Show what would be done without making changes
            quick_update: Perform lightweight update based on recent git activity
            catchup: Show recent commit history from all branches for PM context
            non_interactive: Non-interactive mode - display report only without prompting
            days: Number of days for git history analysis (7, 14, 30, 60, or 90)
            export: Export report to file (path or "auto" for default location)

        Returns:
            Dict containing initialization results
        """
        try:
            # Determine initialization mode
            claude_md = self.project_path / "CLAUDE.md"
            has_existing = claude_md.exists()

            if review_only:
                return self._run_review_mode()

            if catchup:
                data = self._catchup()
                self._display_catchup(data)
                return {
                    "status": OperationResult.SUCCESS,
                    "mode": "catchup",
                    "catchup_data": data,
                }

            if quick_update:
                return self._run_quick_update_mode(
                    days=days,
                    non_interactive=non_interactive,
                    export=export,
                )

            if has_existing and not force and not update_mode:
                # Auto-select update mode if organize_files or dry_run is specified
                if organize_files or dry_run:
                    update_mode = True
                    self.console.print(
                        "[cyan]Auto-selecting update mode for organization tasks.[/cyan]\n"
                    )
                else:
                    # Offer update mode
                    self.console.print(
                        "[yellow]⚠️  Project already has CLAUDE.md file.[/yellow]\n"
                    )

                    # Show current documentation analysis
                    doc_analysis = self.doc_manager.analyze_existing_content()
                    self._display_documentation_status(doc_analysis)

                    # Ask user what to do
                    action = self._prompt_update_action()

                    if action == "update":
                        update_mode = True
                    elif action == "recreate":
                        force = True
                    elif action == "review":
                        return self._run_review_mode()
                    else:
                        return {
                            "status": OperationResult.CANCELLED,
                            "message": "Initialization cancelled",
                        }

            # Handle dry-run mode
            if dry_run:
                return self._run_dry_run_mode(organize_files, has_existing)

            # Run pre-initialization checks
            if not review_only:
                pre_check_result = self._run_pre_initialization_checks(
                    organize_files, skip_archive, has_existing
                )
                if pre_check_result.get("status") == OperationResult.ERROR:
                    return pre_check_result

            # Build the delegation prompt
            if update_mode:
                prompt = self._build_update_prompt(
                    project_type, framework, ast_analysis, preserve_custom
                )
            else:
                prompt = self._build_initialization_prompt(
                    project_type, framework, ast_analysis
                )

            # Show appropriate plan based on mode
            if update_mode:
                self._show_update_plan(ast_analysis, preserve_custom)
            else:
                self._show_initialization_plan(ast_analysis)

            # Execute via claude-mpm run command
            with Progress(
                SpinnerColumn(),
                TextColumn("[progress.description]{task.description}"),
                console=self.console,
            ) as progress:
                task_desc = (
                    "[cyan]Updating documentation..."
                    if update_mode
                    else "[cyan]Delegating to Agentic Coder Optimizer..."
                )
                task = progress.add_task(task_desc, total=None)

                # Run the initialization through subprocess
                result = self._run_initialization(prompt, verbose, update_mode)

                complete_desc = (
                    "[green]✓ Update complete"
                    if update_mode
                    else "[green]✓ Initialization complete"
                )
                progress.update(task, description=complete_desc)

            # Post-processing for update mode
            if update_mode and result.get("status") == OperationResult.SUCCESS:
                self._handle_update_post_processing()

            return result

        except Exception as e:
            logger.error(f"Failed to initialize project: {e}")
            self.console.print(f"[red]❌ Error: {e}[/red]")
            return {"status": OperationResult.ERROR, "message": str(e)}

    def _find_claude_mpm_script(self) -> Path:
        """Find the claude-mpm script location."""
        # Try to find claude-mpm in the project scripts directory first
        project_root = Path(__file__).parent.parent.parent.parent.parent
        script_path = project_root / "scripts" / "claude-mpm"
        if script_path.exists():
            return script_path
        # Otherwise assume it's in PATH
        return Path("claude-mpm")

    def _build_claude_mpm_command(self, verbose: bool) -> List[str]:
        """Build the claude-mpm run command with appropriate arguments."""
        cmd = [str(self.claude_mpm_script)]

        # Add top-level flags that go before 'run' subcommand
        cmd.append("--no-check-dependencies")

        # Now add the run subcommand
        cmd.append("run")

        # Add non-interactive mode
        # We'll pass the prompt via stdin instead of -i flag
        cmd.append("--non-interactive")

        # Add verbose flag if requested (run subcommand argument)
        if verbose:
            cmd.append("--verbose")

        return cmd

    def _run_initialization(
        self,
        prompt: str,
        verbose: bool,
        update_mode: bool = False,
    ) -> Dict:
        """Run the initialization through subprocess calling claude-mpm."""
        try:
            # Write prompt to temporary file
            with tempfile.NamedTemporaryFile(
                mode="w", suffix=".txt", delete=False
            ) as tmp_file:
                tmp_file.write(prompt)
                prompt_file = tmp_file.name

            try:
                # Build the command
                cmd = self._build_claude_mpm_command(verbose)
                # Add the input file flag
                cmd.extend(["-i", prompt_file])

                # Log the command if verbose
                if verbose:
                    self.console.print(f"[dim]Running: {' '.join(cmd)}[/dim]")
                    self.console.print(f"[dim]Prompt file: {prompt_file}[/dim]")

                # Execute the command
                result = subprocess.run(
                    cmd,
                    capture_output=True,
                    text=True,
                    cwd=str(self.project_path),
                    check=False,
                )
            finally:
                # Clean up temporary file
                with contextlib.suppress(Exception):
                    Path(prompt_file).unlink()

            # Display output if verbose
            if verbose and result.stdout:
                self.console.print(result.stdout)
            if verbose and result.stderr:
                self.console.print(f"[yellow]{result.stderr}[/yellow]")

            # Check result - be more lenient with return codes
            if result.returncode == 0 or (self.project_path / "CLAUDE.md").exists():
                response = {
                    "status": OperationResult.SUCCESS,
                    "message": "Project initialized successfully",
                    "files_created": [],
                    "files_updated": [],
                    "next_steps": [],
                }

                # Check if CLAUDE.md was created
                claude_md = self.project_path / "CLAUDE.md"
                if claude_md.exists():
                    response["files_created"].append("CLAUDE.md")

                # Check for other common files
                for file_name in ["CODE.md", "DEVELOPER.md", "STRUCTURE.md", "OPS.md"]:
                    file_path = self.project_path / file_name
                    if file_path.exists():
                        response["files_created"].append(file_name)

                # Add next steps
                response["next_steps"] = [
                    "Review the generated CLAUDE.md documentation",
                    "Verify the project structure meets your needs",
                    "Run 'claude-mpm run' to start using the optimized setup",
                ]

                # Display results
                self._display_results(response, verbose)

                return response

            # Extract meaningful error message
            error_msg = (
                result.stderr
                if result.stderr
                else result.stdout
                if result.stdout
                else "Unknown error occurred"
            )

            logger.error(f"claude-mpm run failed: {error_msg}")
            return {
                "status": OperationResult.ERROR,
                "message": f"Initialization failed: {error_msg}",
            }

        except FileNotFoundError:
            logger.error("claude-mpm command not found")
            self.console.print(
                "[red]Error: claude-mpm command not found. Ensure Claude MPM is properly installed.[/red]"
            )
            return {"status": OperationResult.ERROR, "message": "claude-mpm not found"}
        except Exception as e:
            logger.error(f"Initialization failed: {e}")
            return {"status": OperationResult.ERROR, "message": str(e)}

    def _run_pre_initialization_checks(
        self, organize_files: bool, skip_archive: bool, has_existing: bool
    ) -> Dict:
        """Run pre-initialization checks and preparations."""
        checks_passed = []
        warnings = []

        # Run comprehensive project readiness check
        _ready, actions = self.organizer.ensure_project_ready(
            auto_organize=organize_files,
            safe_mode=True,  # Only perform safe operations by default
        )

        if actions:
            checks_passed.extend(actions)

        # Get structure validation report
        validation = self.organizer.validate_structure()
        if validation["warnings"]:
            warnings.extend(validation["warnings"])
        if validation["errors"]:
            warnings.extend(validation["errors"])

        # Show structure grade
        if validation.get("grade"):
            checks_passed.append(f"Structure validation: {validation['grade']}")

        # Archive existing documentation if needed
        if has_existing and not skip_archive:
            if self.archive_manager.auto_archive_before_update(
                self.project_path / "CLAUDE.md", update_reason="Before mpm-init update"
            ):
                checks_passed.append("Archived existing CLAUDE.md")

        # Check for issues in validation report
        if validation.get("issues"):
            for issue in validation["issues"]:
                warnings.append(issue["description"])

        if warnings:
            self.console.print("\n[yellow]⚠️  Project issues detected:[/yellow]")
            for warning in warnings[:5]:
                self.console.print(f"  • {warning}")
            self.console.print()

        if checks_passed:
            self.console.print("[green]✅ Pre-initialization checks:[/green]")
            for check in checks_passed:
                self.console.print(f"  • {check}")
            self.console.print()

        return {
            "status": OperationResult.SUCCESS,
            "checks_passed": checks_passed,
            "warnings": warnings,
        }

    def _build_initialization_prompt(
        self,
        project_type: Optional[str] = None,
        framework: Optional[str] = None,
        ast_analysis: bool = True,
    ) -> str:
        """Build the initialization prompt for the agent."""
        return prompts.build_initialization_prompt(
            self.project_path, project_type, framework, ast_analysis
        )

    def _build_update_prompt(
        self,
        project_type: Optional[str],
        framework: Optional[str],
        ast_analysis: bool,
        preserve_custom: bool,
    ) -> str:
        """Build prompt for update mode."""
        # Get existing content analysis
        doc_analysis = self.doc_manager.analyze_existing_content()

        return prompts.build_update_prompt(
            self.project_path,
            doc_analysis,
            project_type,
            framework,
            ast_analysis,
            preserve_custom,
        )

    def _display_documentation_status(self, analysis: Dict) -> None:
        """Display current documentation status."""
        display.display_documentation_status(self.display, analysis)

    def _prompt_update_action(self) -> str:
        """Prompt user for update action."""
        return modes.prompt_update_action(self.console)

    def _run_review_mode(self) -> Dict:
        """Run review mode to analyze project without changes."""
        return modes.run_review_mode(
            self.console,
            self.display,
            self.organizer,
            self.doc_manager,
            self.analyzer,
        )

    def _run_quick_update_mode(
        self,
        days: int = 30,
        non_interactive: bool = False,
        export: Optional[str] = None,
    ) -> Dict:
        """Run quick update mode - lightweight update based on recent git activity."""
        return modes.run_quick_update_mode(
            self.console,
            self.display,
            self.project_path,
            self.analyzer,
            self.doc_manager,
            self.archive_manager,
            days=days,
            non_interactive=non_interactive,
            export=export,
        )

    def _catchup(self) -> Dict[str, Any]:
        """Get recent commit history for PM context."""
        return git_activity.catchup(self.project_path)

    def _display_catchup(self, data: Dict[str, Any]) -> None:
        """Display catchup information to console.

        Args:
            data: Commit history data from _catchup()
        """
        display.display_catchup(self.display, self.console, data)

    def _run_dry_run_mode(self, organize_files: bool, has_existing: bool) -> Dict:
        """Run dry-run mode to show what would be done without making changes."""
        return modes.run_dry_run_mode(
            self.console,
            self.organizer,
            self.doc_manager,
            organize_files,
            has_existing,
        )

    def _show_update_plan(self, ast_analysis: bool, preserve_custom: bool) -> None:
        """Show update mode plan."""
        display.show_update_plan(self.console, ast_analysis, preserve_custom)

    def _show_initialization_plan(self, ast_analysis: bool) -> None:
        """Show standard initialization plan."""
        display.show_initialization_plan(self.console, ast_analysis)

    def _handle_update_post_processing(self) -> None:
        """Handle post-processing after successful update."""
        modes.handle_update_post_processing(
            self.console,
            self.project_path,
            self.doc_manager,
            self.archive_manager,
        )

    def _display_results(self, result: Dict, verbose: bool):
        """Display initialization results."""
        display.display_results(self.display, self.console, result, verbose)

    def handle_context(
        self,
        session_id: Optional[str] = None,
        list_sessions: bool = False,
        days: int = 7,
    ) -> Dict[str, Any]:
        """Provide intelligent context for resuming work based on git history."""
        return git_activity.handle_context(
            self.project_path, session_id, list_sessions, days
        )

    def _build_research_context_prompt(
        self, git_analysis: Dict[str, Any], days: int
    ) -> str:
        """Build structured Research agent delegation prompt from git analysis."""
        return prompts.build_research_context_prompt(git_analysis, days)


__all__ = ["MPMInitCommand"]
