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

import click
import json
import subprocess
import sys
import os
import urllib.request
import urllib.parse
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 fetch_apps_from_github(github_url, project_name):
    """Fetch apps configuration from GitHub repository."""
    try:
        # Clean up the GitHub URL and construct raw content URL
        if github_url.endswith('.git'):
            github_url = github_url[:-4]
        if github_url.endswith('/'):
            github_url = github_url[:-1]
        
        # Convert GitHub URL to raw content URL
        if 'github.com' in github_url:
            # Convert https://github.com/user/repo to https://raw.githubusercontent.com/user/repo/main
            github_url = github_url.replace('github.com', 'raw.githubusercontent.com')
            if not github_url.endswith('/main') and not github_url.endswith('/master'):
                github_url += '/main'
        
        # Construct the path to the apps.json file
        apps_json_url = f"{github_url}/docker-images/projects/{project_name}/apps.json"
        
        print_info(f"Fetching apps configuration from: {apps_json_url}")
        
        # Fetch the file content
        with urllib.request.urlopen(apps_json_url) as response:
            content = response.read().decode('utf-8')
            apps_list = json.loads(content)
            
        # Convert array format to object format for compatibility
        apps_config = {}
        for app in apps_list:
            app_name = app.get('name')
            if app_name:
                apps_config[app_name] = {
                    'url': app.get('url'),
                    'is_repo': True,
                    'resolution': {
                        'branch': app.get('branch', 'main')
                    }
                }
        
        return apps_config
        
    except urllib.error.HTTPError as e:
        if e.code == 404:
            print_error(f"Apps configuration not found: {apps_json_url}")
            print_info(f"Make sure the project '{project_name}' exists in the repository")
        else:
            print_error(f"HTTP error {e.code}: {e.reason}")
        return None
    except urllib.error.URLError as e:
        print_error(f"Failed to fetch from GitHub: {e.reason}")
        return None
    except json.JSONDecodeError as e:
        print_error(f"Error parsing apps.json from GitHub: {e}")
        return None
    except Exception as e:
        print_error(f"Unexpected error fetching from GitHub: {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"]
    
    # All apps from GitHub are repository-based
    url = app_config.get("url")
    resolution = app_config.get("resolution", {})
    branch = resolution.get("branch")
    
    # Add branch if specified
    if branch:
        cmd.extend(["--branch", branch])
    
    # Add the repository URL
    if url:
        cmd.append(url)
    else:
        # Fallback to app name if no URL provided
        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.argument('github_url')
@click.argument('project_name')
@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(github_url, project_name, dry_run, force, specific_app, exclude):
    """Install apps from GitHub repository apps.json configuration file.
    
    This command fetches the apps.json file from a GitHub repository at
    docker-images/projects/{PROJECT_NAME}/apps.json and installs all configured apps
    using 'bench get-app'. The frappe app is automatically excluded from installation.
    
    Arguments:
        GITHUB_URL: GitHub repository URL (e.g., https://github.com/user/repo)
        PROJECT_NAME: Project name within the repository's projects directory
    
    Examples:
        ws-frappe-cli get-apps https://github.com/user/repo whitestork
        ws-frappe-cli get-apps https://github.com/user/repo.git dbouk --dry-run
        ws-frappe-cli get-apps https://github.com/user/repo medis --app erpnext
        ws-frappe-cli get-apps https://github.com/user/repo nvox --exclude hrms --force
    """
    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)
    
    # Fetch apps configuration from GitHub
    print_info(f"Fetching apps for project '{project_name}' from GitHub repository: {github_url}")
    apps_config = fetch_apps_from_github(github_url, project_name)
    
    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 project '{project_name}'")
        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():
        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})")
    
    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()