"""Get Apps command for installing apps from apps.json configuration."""

import click
import json
import subprocess
import sys
import os
from pathlib import Path

from ..utils import (
    Colors, print_info, print_success, print_warning, print_error,
    validate_bench_directory, prompt_yes_no, run_command
)


def load_apps_config(apps_json_path):
    """Load apps configuration from apps.json file."""
    try:
        with open(apps_json_path, 'r') as f:
            return json.load(f)
    except FileNotFoundError:
        print_error(f"{apps_json_path} not found")
        return None
    except json.JSONDecodeError as e:
        print_error(f"Error parsing {apps_json_path}: {e}")
        return None


def build_get_app_command(app_name, app_config):
    """Build the bench get-app command for a specific app."""
    cmd = ["bench", "get-app"]
    
    # Check if it's a repository-based app
    if app_config.get("is_repo", False):
        resolution = app_config.get("resolution", {})
        branch = resolution.get("branch")
        commit_hash = resolution.get("commit_hash")
        
        # Add branch if specified
        if branch:
            cmd.extend(["--branch", branch])
        
        # For repo-based apps, we need the repository URL
        # Check if there's a custom URL in the config, otherwise use default pattern
        if 'url' in app_config:
            repo_url = app_config['url']
        else:
            # Default pattern: assumes the app is in frappe GitHub organization
            repo_url = f"https://github.com/frappe/{app_name}.git"
        
        cmd.append(repo_url)
    else:
        # For non-repo apps, just use the app name
        cmd.append(app_name)
    
    return cmd


def run_get_app_command(cmd, app_name):
    """Execute the get-app command for an app."""
    print_info(f"Installing {app_name}...")
    print(f"  Command: {Colors.YELLOW}{' '.join(cmd)}{Colors.NC}")
    
    try:
        result = subprocess.run(
            cmd,
            capture_output=True,
            text=True,
            check=True
        )
        print_success(f"Successfully installed {app_name}")
        if result.stdout and result.stdout.strip():
            # Only show output if it's not empty
            lines = result.stdout.strip().split('\n')
            for line in lines:
                if line.strip():
                    print(f"    {line}")
        return True
    except subprocess.CalledProcessError as e:
        print_error(f"Failed to install {app_name}")
        if e.stderr:
            print(f"    {Colors.RED}{e.stderr.strip()}{Colors.NC}")
        return False
    except FileNotFoundError:
        print_error("'bench' command not found. Make sure Frappe Bench is installed and in PATH.")
        return False


def find_apps_json(search_path=None):
    """Find apps.json file in current directory or specified path."""
    if search_path:
        apps_json_path = Path(search_path) / "sites" / "apps.json"
    else:
        apps_json_path = Path.cwd() / "sites" / "apps.json"
    
    # Also check for apps.json in the root directory
    if not apps_json_path.exists():
        root_apps_json = apps_json_path.parent.parent / "apps.json"
        if root_apps_json.exists():
            return root_apps_json
    
    return apps_json_path


@click.command()
@click.option('--apps-file',
              type=click.Path(exists=True),
              help='Path to apps.json file (default: sites/apps.json)')
@click.option('--dry-run',
              is_flag=True,
              help='Show what would be installed without actually installing')
@click.option('--force',
              is_flag=True,
              help='Skip confirmation prompt')
@click.option('--app',
              'specific_app',
              help='Install only a specific app from the apps.json')
@click.option('--exclude',
              multiple=True,
              help='Exclude specific apps from installation (can be used multiple times)')
def get_apps(apps_file, dry_run, force, specific_app, exclude):
    """Install apps from apps.json configuration file.
    
    This command reads the apps.json file (typically found in sites/apps.json)
    and installs all configured apps using 'bench get-app'. The frappe app
    is automatically excluded from installation.
    
    Examples:
        ws-frappe-cli get-apps                    # Install all apps from sites/apps.json
        ws-frappe-cli get-apps --dry-run          # Preview what would be installed
        ws-frappe-cli get-apps --app erpnext      # Install only ERPNext
        ws-frappe-cli get-apps --exclude hrms     # Install all except HRMS
        ws-frappe-cli get-apps --force            # Skip confirmation prompt
    """
    print()
    print(f"{Colors.BLUE}{'=' * 50}{Colors.NC}")
    print(f"{Colors.GREEN}  Frappe Apps Installer{Colors.NC}")
    print(f"{Colors.BLUE}{'=' * 50}{Colors.NC}")
    print()
    
    # Validate bench directory
    current_dir = Path.cwd()
    if not validate_bench_directory(current_dir):
        print_error("This command must be run from a bench directory!")
        print_info("Please navigate to a bench directory or run 'ws-frappe-cli setup' first.")
        sys.exit(1)
    
    # Find apps.json file
    if apps_file:
        apps_json_path = Path(apps_file)
    else:
        apps_json_path = find_apps_json()
    
    print_info(f"Looking for apps configuration: {apps_json_path}")
    
    if not apps_json_path.exists():
        print_error(f"Apps configuration file not found: {apps_json_path}")
        print()
        print_info("Expected locations:")
        print(f"  • {current_dir / 'sites' / 'apps.json'}")
        print(f"  • {current_dir / 'apps.json'}")
        print()
        print_info("You can specify a custom location with --apps-file option")
        sys.exit(1)
    
    # Load apps configuration
    print_info(f"Reading apps from: {apps_json_path}")
    apps_config = load_apps_config(apps_json_path)
    
    if apps_config is None:
        sys.exit(1)
    
    # Filter apps based on options
    apps_to_install = {}
    
    for name, config in apps_config.items():
        # Skip frappe app
        if name.lower() == "frappe":
            continue
            
        # If specific app is requested, only include that one
        if specific_app and name.lower() != specific_app.lower():
            continue
            
        # Skip excluded apps
        if name.lower() in [exc.lower() for exc in exclude]:
            continue
            
        apps_to_install[name] = config
    
    if not apps_to_install:
        if specific_app:
            print_warning(f"App '{specific_app}' not found in {apps_json_path}")
        else:
            print_warning("No apps to install (only frappe found, which is excluded)")
        return
    
    print()
    print_success(f"Found {len(apps_to_install)} app(s) to install:")
    for app_name, app_config in apps_to_install.items():
        if app_config.get("is_repo", False):
            resolution = app_config.get("resolution", {})
            branch = resolution.get("branch", "main")
            url = app_config.get("url", f"https://github.com/frappe/{app_name}.git")
            print(f"  • {Colors.YELLOW}{app_name}{Colors.NC} (from {url}@{branch})")
        else:
            print(f"  • {Colors.YELLOW}{app_name}{Colors.NC}")
    
    if excluded_count := len(exclude):
        print()
        print_info(f"Excluded {excluded_count} app(s): {', '.join(exclude)}")
    
    if dry_run:
        print()
        print_info("Dry run mode - showing commands that would be executed:")
        print()
        for app_name, app_config in apps_to_install.items():
            cmd = build_get_app_command(app_name, app_config)
            print(f"  {Colors.YELLOW}{' '.join(cmd)}{Colors.NC}")
        print()
        print_info("Use without --dry-run to actually install the apps")
        return
    
    # Confirm before proceeding (unless --force is used)
    if not force:
        print()
        if not prompt_yes_no("Do you want to proceed with installation?", default="n"):
            print_warning("Installation cancelled")
            return
    
    # Install each app
    print()
    successful_installs = []
    failed_installs = []
    
    for app_name, app_config in apps_to_install.items():
        cmd = build_get_app_command(app_name, app_config)
        
        if run_get_app_command(cmd, app_name):
            successful_installs.append(app_name)
        else:
            failed_installs.append(app_name)
        print()
    
    # Summary
    print(f"{Colors.BLUE}{'=' * 50}{Colors.NC}")
    print(f"{Colors.GREEN}  Installation Summary{Colors.NC}")
    print(f"{Colors.BLUE}{'=' * 50}{Colors.NC}")
    print()
    
    if successful_installs:
        print_success(f"Successfully installed ({len(successful_installs)}):")
        for app in successful_installs:
            print(f"  • {app}")
        print()
    
    if failed_installs:
        print_error(f"Failed to install ({len(failed_installs)}):")
        for app in failed_installs:
            print(f"  • {app}")
        print()
        print_warning("Troubleshooting tips:")
        print("  • Check if the repository URLs are correct")
        print("  • Verify your network connection")
        print("  • Ensure you have proper permissions")
        print("  • Check if the branch/commit exists")
        print("  • Try installing failed apps manually")
        print()
    
    if successful_installs and not failed_installs:
        print_success("All apps installed successfully!")
    elif successful_installs and failed_installs:
        print_warning("Installation completed with some failures")
    else:
        print_error("No apps were installed successfully")
    
    print()


if __name__ == "__main__":
    get_apps()