#!/usr/bin/env python3
"""
Debian Atomic Blueprint Generator for Debian Forge

This module provides enhanced blueprint generation for Debian atomic images,
integrating with repository management and dependency resolution systems.
"""

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

try:
    from debian_repository_manager import DebianRepositoryManager
    from debian_package_resolver import DebianPackageResolver
except ImportError:
    DebianRepositoryManager = None
    DebianPackageResolver = None

@dataclass
class AtomicBlueprintConfig:
    """Configuration for atomic blueprint generation"""
    name: str
    description: str
    version: str
    base_packages: List[str]
    additional_packages: List[str] = None
    excluded_packages: List[str] = None
    suite: str = "bookworm"
    architecture: str = "amd64"
    include_recommends: bool = False
    ostree_ref: str = None
    users: List[Dict[str, Any]] = None
    services: Dict[str, List[str]] = None
    filesystem_customizations: Dict[str, Any] = None

class DebianAtomicBlueprintGenerator:
    """Generates optimized Debian atomic blueprints"""
    
    def __init__(self, config_dir: str = None):
        if DebianRepositoryManager and config_dir:
            self.repository_manager = DebianRepositoryManager(config_dir)
        elif DebianRepositoryManager:
            # Use temporary directory for testing
            import tempfile
            temp_dir = tempfile.mkdtemp(prefix="debian-forge-")
            self.repository_manager = DebianRepositoryManager(temp_dir)
        else:
            self.repository_manager = None
        
        self.package_resolver = DebianPackageResolver() if DebianPackageResolver else None
        self.base_packages = [
            "systemd",
            "systemd-sysv", 
            "dbus",
            "udev",
            "ostree",
            "linux-image-amd64"
        ]
    
    def generate_base_blueprint(self, config: AtomicBlueprintConfig = None) -> Dict[str, Any]:
        """Generate base atomic blueprint"""
        if config is None:
            config = AtomicBlueprintConfig(
                name="debian-atomic-base",
                description="Debian Atomic Base System",
                version="1.0.0",
                base_packages=self.base_packages
            )
        
        # Resolve package dependencies
        all_packages = config.base_packages + (config.additional_packages or [])
        resolved_packages = self._resolve_packages(all_packages, config.suite, config.architecture)
        
        # Generate blueprint
        blueprint = {
            "name": config.name,
            "description": config.description,
            "version": config.version,
            "distro": f"debian-{config.suite}",
            "arch": config.architecture,
            "packages": [{"name": pkg} for pkg in resolved_packages],
            "modules": [],
            "groups": [],
            "customizations": self._generate_base_customizations(config)
        }
        
        # Add OSTree configuration
        if config.ostree_ref:
            blueprint["ostree"] = {
                "ref": config.ostree_ref,
                "parent": f"debian/{config.suite}/base"
            }
        
        return blueprint
    
    def generate_workstation_blueprint(self) -> Dict[str, Any]:
        """Generate workstation atomic blueprint"""
        workstation_packages = [
            "firefox-esr",
            "libreoffice",
            "gnome-core",
            "gdm3",
            "network-manager",
            "pulseaudio",
            "fonts-dejavu"
        ]
        
        config = AtomicBlueprintConfig(
            name="debian-atomic-workstation",
            description="Debian Atomic Workstation",
            version="1.0.0",
            base_packages=self.base_packages,
            additional_packages=workstation_packages,
            ostree_ref="debian/bookworm/workstation"
        )
        
        blueprint = self.generate_base_blueprint(config)
        blueprint["customizations"]["services"]["enabled"].extend([
            "gdm3",
            "NetworkManager",
            "pulseaudio"
        ])
        
        return blueprint
    
    def generate_server_blueprint(self) -> Dict[str, Any]:
        """Generate server atomic blueprint"""
        server_packages = [
            "nginx",
            "postgresql",
            "redis",
            "fail2ban",
            "logrotate",
            "rsyslog"
        ]
        
        config = AtomicBlueprintConfig(
            name="debian-atomic-server",
            description="Debian Atomic Server",
            version="1.0.0",
            base_packages=self.base_packages,
            additional_packages=server_packages,
            ostree_ref="debian/bookworm/server"
        )
        
        blueprint = self.generate_base_blueprint(config)
        blueprint["customizations"]["services"]["enabled"].extend([
            "nginx",
            "postgresql", 
            "redis-server",
            "fail2ban"
        ])
        
        return blueprint
    
    def generate_container_blueprint(self) -> Dict[str, Any]:
        """Generate container atomic blueprint"""
        container_packages = [
            "podman",
            "buildah",
            "skopeo",
            "containers-common",
            "crun"
        ]
        
        config = AtomicBlueprintConfig(
            name="debian-atomic-container",
            description="Debian Atomic Container Host",
            version="1.0.0",
            base_packages=self.base_packages,
            additional_packages=container_packages,
            ostree_ref="debian/bookworm/container"
        )
        
        blueprint = self.generate_base_blueprint(config)
        blueprint["customizations"]["services"]["enabled"].extend([
            "podman"
        ])
        
        # Add container-specific configurations
        blueprint["customizations"]["filesystem"] = {
            "/var/lib/containers": {
                "type": "directory",
                "mode": "0755"
            }
        }
        
        return blueprint
    
    def generate_minimal_blueprint(self) -> Dict[str, Any]:
        """Generate minimal atomic blueprint"""
        minimal_packages = [
            "systemd",
            "systemd-sysv",
            "ostree",
            "linux-image-amd64"
        ]
        
        config = AtomicBlueprintConfig(
            name="debian-atomic-minimal",
            description="Debian Atomic Minimal System",
            version="1.0.0",
            base_packages=minimal_packages,
            ostree_ref="debian/bookworm/minimal"
        )
        
        return self.generate_base_blueprint(config)
    
    def _resolve_packages(self, packages: List[str], suite: str, architecture: str) -> List[str]:
        """Resolve package dependencies"""
        if not self.package_resolver:
            return packages
        
        try:
            resolution = self.package_resolver.resolve_package_dependencies(
                packages, suite, architecture, include_recommends=False
            )
            
            if resolution.conflicts:
                print(f"Warning: Package conflicts detected: {resolution.conflicts}")
            
            if resolution.missing:
                print(f"Warning: Missing packages: {resolution.missing}")
            
            return resolution.install_order
            
        except Exception as e:
            print(f"Package resolution failed: {e}")
            return packages
    
    def _generate_base_customizations(self, config: AtomicBlueprintConfig) -> Dict[str, Any]:
        """Generate base customizations for blueprint"""
        customizations = {
            "user": config.users or [
                {
                    "name": "debian",
                    "description": "Debian atomic user",
                    "password": "$6$rounds=656000$debian$atomic.system.user",
                    "home": "/home/debian",
                    "shell": "/bin/bash",
                    "groups": ["wheel", "sudo"],
                    "uid": 1000,
                    "gid": 1000
                }
            ],
            "services": config.services or {
                "enabled": ["sshd", "systemd-networkd", "systemd-resolved"],
                "disabled": ["systemd-timesyncd"]
            },
            "kernel": {
                "append": "ostree=/ostree/boot.1/debian/bookworm/0"
            }
        }
        
        if config.filesystem_customizations:
            customizations["filesystem"] = config.filesystem_customizations
        
        return customizations
    
    def generate_osbuild_manifest(self, blueprint: Dict[str, Any]) -> Dict[str, Any]:
        """Generate OSBuild manifest from blueprint"""
        manifest = {
            "version": "2",
            "pipelines": [
                {
                    "name": "build",
                    "runner": "org.osbuild.linux",
                    "stages": []
                }
            ]
        }
        
        # Add debootstrap stage
        debootstrap_stage = {
            "type": "org.osbuild.debootstrap",
            "options": {
                "suite": "bookworm",
                "mirror": "http://deb.debian.org/debian",
                "arch": blueprint.get("arch", "amd64"),
                "variant": "minbase",
                "apt_proxy": "http://192.168.1.101:3142"
            }
        }
        manifest["pipelines"][0]["stages"].append(debootstrap_stage)
        
        # Add APT configuration stage
        apt_config_stage = {
            "type": "org.osbuild.apt.config",
            "options": {
                "sources": self._get_apt_sources(),
                "preferences": {},
                "proxy": "http://192.168.1.101:3142"
            }
        }
        manifest["pipelines"][0]["stages"].append(apt_config_stage)
        
        # Add package installation stage
        package_names = [pkg["name"] for pkg in blueprint["packages"]]
        apt_stage = {
            "type": "org.osbuild.apt",
            "options": {
                "packages": package_names,
                "recommends": False,
                "update": True,
                "apt_proxy": "http://192.168.1.101:3142"
            }
        }
        manifest["pipelines"][0]["stages"].append(apt_stage)
        
        # Add OSTree commit stage
        ostree_stage = {
            "type": "org.osbuild.ostree.commit",
            "options": {
                "repo": blueprint.get("name", "debian-atomic"),
                "branch": blueprint.get("ostree", {}).get("ref", f"debian/bookworm/{blueprint['name']}"),
                "subject": f"Debian atomic {blueprint['name']} system",
                "body": f"Built from blueprint: {blueprint['name']} v{blueprint['version']}"
            }
        }
        manifest["pipelines"][0]["stages"].append(ostree_stage)
        
        return manifest
    
    def _get_apt_sources(self) -> Dict[str, Any]:
        """Get APT sources configuration"""
        if not self.repository_manager:
            return {
                "main": "deb http://deb.debian.org/debian bookworm main",
                "security": "deb http://security.debian.org/debian-security bookworm-security main",
                "updates": "deb http://deb.debian.org/debian bookworm-updates main"
            }
        
        return self.repository_manager.generate_apt_config("bookworm", proxy="http://192.168.1.101:3142")
    
    def save_blueprint(self, blueprint: Dict[str, Any], output_dir: str = "blueprints") -> str:
        """Save blueprint to file"""
        output_path = Path(output_dir) / f"{blueprint['name']}.json"
        output_path.parent.mkdir(parents=True, exist_ok=True)
        
        with open(output_path, 'w') as f:
            json.dump(blueprint, f, indent=2)
        
        return str(output_path)
    
    def validate_blueprint(self, blueprint: Dict[str, Any]) -> Dict[str, Any]:
        """Validate blueprint structure and content"""
        validation = {
            "valid": True,
            "errors": [],
            "warnings": [],
            "suggestions": []
        }
        
        # Check required fields
        required_fields = ["name", "description", "version", "packages"]
        for field in required_fields:
            if field not in blueprint:
                validation["valid"] = False
                validation["errors"].append(f"Missing required field: {field}")
        
        # Validate packages
        if "packages" in blueprint:
            if not blueprint["packages"]:
                validation["warnings"].append("No packages specified")
            
            package_names = [pkg.get("name") if isinstance(pkg, dict) else pkg for pkg in blueprint["packages"]]
            
            # Check for essential packages
            essential_packages = ["systemd", "ostree"]
            missing_essential = [pkg for pkg in essential_packages if pkg not in package_names]
            if missing_essential:
                validation["suggestions"].append(f"Consider adding essential packages: {missing_essential}")
        
        # Validate customizations
        if "customizations" in blueprint and "services" in blueprint["customizations"]:
            services = blueprint["customizations"]["services"]
            if "enabled" in services and "disabled" in services:
                conflicts = set(services["enabled"]) & set(services["disabled"])
                if conflicts:
                    validation["valid"] = False
                    validation["errors"].append(f"Services both enabled and disabled: {list(conflicts)}")
        
        return validation
    
    def generate_all_blueprints(self, output_dir: str = "blueprints") -> List[str]:
        """Generate all standard blueprints"""
        blueprints = [
            ("base", self.generate_base_blueprint()),
            ("workstation", self.generate_workstation_blueprint()),
            ("server", self.generate_server_blueprint()),
            ("container", self.generate_container_blueprint()),
            ("minimal", self.generate_minimal_blueprint())
        ]
        
        saved_files = []
        for name, blueprint in blueprints:
            try:
                output_path = self.save_blueprint(blueprint, output_dir)
                saved_files.append(output_path)
                print(f"Generated {name} blueprint: {output_path}")
            except Exception as e:
                print(f"Failed to generate {name} blueprint: {e}")
        
        return saved_files

def main():
    """Example usage of blueprint generator"""
    print("Debian Atomic Blueprint Generator")
    
    generator = DebianAtomicBlueprintGenerator()
    
    # Generate all blueprints
    print("\nGenerating all blueprints...")
    saved_files = generator.generate_all_blueprints()
    
    print(f"\nGenerated {len(saved_files)} blueprints:")
    for file_path in saved_files:
        print(f"  - {file_path}")
    
    # Example: Generate and validate a custom blueprint
    print("\nGenerating custom blueprint...")
    config = AtomicBlueprintConfig(
        name="debian-atomic-custom",
        description="Custom Debian Atomic System",
        version="1.0.0",
        base_packages=["systemd", "ostree"],
        additional_packages=["vim", "curl", "wget"],
        ostree_ref="debian/bookworm/custom"
    )
    
    custom_blueprint = generator.generate_base_blueprint(config)
    validation = generator.validate_blueprint(custom_blueprint)
    
    print(f"Custom blueprint validation: {'Valid' if validation['valid'] else 'Invalid'}")
    if validation['errors']:
        print(f"Errors: {validation['errors']}")
    if validation['warnings']:
        print(f"Warnings: {validation['warnings']}")

if __name__ == '__main__':
    main()
