#!/usr/bin/env python3
"""
Debian Forge CLI Integration Module

This module provides integration between debian-forge and debian-forge-cli,
ensuring 1:1 compatibility with the upstream osbuild/image-builder-cli.
"""

import json
import subprocess
import os
from typing import Dict, List, Optional, Any
from dataclasses import dataclass
from pathlib import Path

@dataclass
class ImageBuildRequest:
    """Image build request for CLI integration"""
    blueprint_path: str
    output_format: str
    output_path: str
    architecture: str = "amd64"
    distro: str = "debian-12"
    extra_repos: Optional[List[str]] = None
    ostree_ref: Optional[str] = None
    ostree_parent: Optional[str] = None
    ostree_url: Optional[str] = None

@dataclass
class ImageBuildResult:
    """Result of image build operation"""
    success: bool
    output_path: str
    build_time: float
    image_size: int
    error_message: Optional[str] = None
    metadata: Optional[Dict[str, Any]] = None

class DebianForgeCLI:
    """Integration with debian-forge-cli (fork of osbuild/image-builder-cli)"""
    
    def __init__(self, cli_path: str = "../debian-forge-cli", data_dir: str = "./cli-data"):
        self.cli_path = Path(cli_path)
        self.data_dir = Path(data_dir)
        self.cli_binary = self.cli_path / "cmd" / "image-builder" / "image-builder"
        
        # Ensure data directory exists
        self.data_dir.mkdir(exist_ok=True)
        
        # Verify CLI binary exists
        if not self.cli_binary.exists():
            raise FileNotFoundError(f"CLI binary not found at {self.cli_binary}")
    
    def list_images(self, filter_args: Optional[List[str]] = None, 
                   format_type: str = "json") -> List[Dict[str, Any]]:
        """List available images using the CLI"""
        cmd = [str(self.cli_binary), "list", "--data-dir", str(self.data_dir)]
        
        if filter_args:
            for filter_arg in filter_args:
                cmd.extend(["--filter", filter_arg])
        
        if format_type:
            cmd.extend(["--format", format_type])
        
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, check=True)
            
            if format_type == "json":
                return json.loads(result.stdout)
            else:
                return [{"output": result.stdout}]
                
        except subprocess.CalledProcessError as e:
            print(f"CLI list command failed: {e}")
            print(f"Error output: {e.stderr}")
            return []
        except json.JSONDecodeError as e:
            print(f"Failed to parse CLI output: {e}")
            return []
    
    def build_image(self, request: ImageBuildRequest) -> ImageBuildResult:
        """Build an image using the CLI"""
        cmd = [
            str(self.cli_binary), "build",
            "--blueprint", request.blueprint_path,
            "--output", request.output_path,
            "--format", request.output_format,
            "--data-dir", str(self.data_dir)
        ]
        
        # Add architecture if specified
        if request.architecture:
            cmd.extend(["--arch", request.architecture])
        
        # Add distro if specified
        if request.distro:
            cmd.extend(["--distro", request.distro])
        
        # Add extra repositories
        if request.extra_repos:
            for repo in request.extra_repos:
                cmd.extend(["--extra-repo", repo])
        
        # Add OSTree options
        if request.ostree_ref:
            cmd.extend(["--ostree-ref", request.ostree_ref])
        if request.ostree_parent:
            cmd.extend(["--ostree-parent", request.ostree_parent])
        if request.ostree_url:
            cmd.extend(["--ostree-url", request.ostree_url])
        
        try:
            print(f"Executing CLI build command: {' '.join(cmd)}")
            
            start_time = time.time()
            result = subprocess.run(cmd, capture_output=True, text=True, check=True)
            build_time = time.time() - start_time
            
            # Check if output file was created
            output_file = Path(request.output_path)
            if output_file.exists():
                image_size = output_file.stat().st_size
                return ImageBuildResult(
                    success=True,
                    output_path=str(output_file),
                    build_time=build_time,
                    image_size=image_size,
                    metadata={"cli_output": result.stdout}
                )
            else:
                return ImageBuildResult(
                    success=False,
                    output_path=request.output_path,
                    build_time=build_time,
                    image_size=0,
                    error_message="Output file not created"
                )
                
        except subprocess.CalledProcessError as e:
            return ImageBuildResult(
                success=False,
                output_path=request.output_path,
                build_time=0,
                image_size=0,
                error_message=f"CLI build failed: {e.stderr}"
            )
    
    def describe_image(self, image_path: str) -> Dict[str, Any]:
        """Describe an image using the CLI"""
        cmd = [
            str(self.cli_binary), "describeimg",
            "--data-dir", str(self.data_dir),
            image_path
        ]
        
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, check=True)
            
            # Try to parse as JSON, fallback to text
            try:
                return json.loads(result.stdout)
            except json.JSONDecodeError:
                return {"description": result.stdout, "raw_output": True}
                
        except subprocess.CalledProcessError as e:
            return {"error": f"Failed to describe image: {e.stderr}"}
    
    def list_distros(self) -> List[str]:
        """List available distributions"""
        cmd = [
            str(self.cli_binary), "distro",
            "--data-dir", str(self.data_dir)
        ]
        
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, check=True)
            return [line.strip() for line in result.stdout.splitlines() if line.strip()]
        except subprocess.CalledProcessError as e:
            print(f"Failed to list distros: {e.stderr}")
            return []
    
    def list_repositories(self) -> List[Dict[str, Any]]:
        """List available repositories"""
        cmd = [
            str(self.cli_binary), "repos",
            "--data-dir", str(self.data_dir)
        ]
        
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, check=True)
            
            # Try to parse as JSON
            try:
                return json.loads(result.stdout)
            except json.JSONDecodeError:
                # Parse text output
                repos = []
                current_repo = {}
                
                for line in result.stdout.splitlines():
                    line = line.strip()
                    if line.startswith("Repository:"):
                        if current_repo:
                            repos.append(current_repo)
                        current_repo = {"name": line.split(":", 1)[1].strip()}
                    elif ":" in line and current_repo:
                        key, value = line.split(":", 1)
                        current_repo[key.strip().lower()] = value.strip()
                
                if current_repo:
                    repos.append(current_repo)
                
                return repos
                
        except subprocess.CalledProcessError as e:
            print(f"Failed to list repositories: {e.stderr}")
            return []
    
    def get_cli_version(self) -> str:
        """Get CLI version information"""
        cmd = [str(self.cli_binary), "version"]
        
        try:
            result = subprocess.run(cmd, capture_output=True, text=True, check=True)
            return result.stdout.strip()
        except subprocess.CalledProcessError as e:
            return f"Version unknown: {e.stderr}"
    
    def validate_blueprint(self, blueprint_path: str) -> Dict[str, Any]:
        """Validate a blueprint using the CLI"""
        # First check if blueprint file exists
        if not Path(blueprint_path).exists():
            return {"valid": False, "error": "Blueprint file not found"}
        
        # Try to parse the blueprint as JSON
        try:
            with open(blueprint_path, 'r') as f:
                blueprint_data = json.load(f)
            
            # Basic validation
            required_fields = ["name", "version"]
            missing_fields = [field for field in required_fields if field not in blueprint_data]
            
            if missing_fields:
                return {
                    "valid": False, 
                    "error": f"Missing required fields: {missing_fields}"
                }
            
            # Try to use CLI to validate (if it supports validation)
            try:
                cmd = [
                    str(self.cli_binary), "build",
                    "--blueprint", blueprint_path,
                    "--dry-run",
                    "--data-dir", str(self.data_dir)
                ]
                
                result = subprocess.run(cmd, capture_output=True, text=True, check=True)
                return {"valid": True, "cli_validation": "passed"}
                
            except subprocess.CalledProcessError:
                # CLI doesn't support dry-run, but JSON is valid
                return {"valid": True, "json_validation": "passed"}
                
        except json.JSONDecodeError as e:
            return {"valid": False, "error": f"Invalid JSON: {e}"}
    
    def create_debian_blueprint(self, name: str, version: str, 
                               packages: List[str], 
                               customizations: Optional[Dict[str, Any]] = None) -> str:
        """Create a Debian-specific blueprint for CLI use"""
        blueprint = {
            "name": name,
            "version": version,
            "description": f"Debian atomic blueprint for {name}",
            "packages": packages,
            "modules": [],
            "groups": [],
            "customizations": customizations or {}
        }
        
        # Add Debian-specific customizations
        if "debian" not in blueprint["customizations"]:
            blueprint["customizations"]["debian"] = {
                "repositories": [
                    {
                        "name": "debian-main",
                        "baseurl": "http://deb.debian.org/debian",
                        "enabled": True
                    }
                ]
            }
        
        # Save blueprint to data directory
        blueprint_path = self.data_dir / f"{name}-{version}.json"
        with open(blueprint_path, 'w') as f:
            json.dump(blueprint, f, indent=2)
        
        return str(blueprint_path)
    
    def test_cli_integration(self) -> Dict[str, Any]:
        """Test CLI integration functionality"""
        results = {
            "cli_binary_exists": self.cli_binary.exists(),
            "cli_version": self.get_cli_version(),
            "data_dir_created": self.data_dir.exists(),
            "distros_available": len(self.list_distros()) > 0,
            "repos_available": len(self.list_repositories()) > 0
        }
        
        # Test blueprint creation
        try:
            test_blueprint = self.create_debian_blueprint(
                "test-integration", "1.0.0", ["bash", "coreutils"]
            )
            blueprint_validation = self.validate_blueprint(test_blueprint)
            results["blueprint_creation"] = blueprint_validation["valid"]
            
            # Clean up test blueprint
            if Path(test_blueprint).exists():
                os.remove(test_blueprint)
                
        except Exception as e:
            results["blueprint_creation"] = False
            results["blueprint_error"] = str(e)
        
        return results

# Import time module for build timing
import time
