"""
Docker utility functions for container management
"""
import subprocess
import time
from typing import List, Dict, Optional, Tuple
from .logger import Logger


class DockerUtils:
    """Helper functions for Docker operations"""
    
    def __init__(self, logger: Logger, version: str = "unknown"):
        self.logger = logger
        self.version = version
        self.project_dir = None  # Will be set by test runner
        
    def set_project_dir(self, project_dir: str):
        """Set the project directory for docker-compose commands"""
        self.project_dir = project_dir
    
    def _run_command(self, cmd: list, ignore_errors: bool = False, cwd: str = None, timeout: int = 60) -> tuple[bool, str]:
        """Run a command and return (success, output)"""
        try:
            result = subprocess.run(
                cmd,
                capture_output=True,
                text=True,
                check=False,
                cwd=cwd,
                timeout=timeout
            )
            success = result.returncode == 0 or ignore_errors
            return success, result.stdout + result.stderr
        except subprocess.TimeoutExpired:
            return False, f"Command timed out after {timeout} seconds"
        except Exception as e:
            return False, str(e)
    
    def check_docker_installed(self) -> bool:
        """Check if Docker is installed"""
        success, _ = self._run_command(["docker", "--version"])
        return success
    
    def check_docker_compose_installed(self) -> bool:
        """Check if Docker Compose is installed"""
        success, _ = self._run_command(["docker", "compose", "version"])
        return success
    
    def get_container_status(self, container_name: str) -> Optional[str]:
        """Get status of a specific container"""
        success, output = self._run_command([
            "docker", "inspect", "-f", "{{.State.Status}}", container_name
        ])
        if success:
            return output.strip()
        return None
    
    def get_all_container_statuses(self) -> Dict[str, str]:
        """Get status of all StreamA2026 containers"""
        containers = {
            "streama2026-postgres": "postgres",
            "streama2026-redis": "redis",
            "streama2026-backend": "backend",
            "streama2026-daemon": "daemon",
            "streama2026-frontend": "frontend",
            "streama2026-caddy": "caddy",
            "streama2026-nginx": "nginx",
            "streama2026-backup": "backup"
        }
        
        statuses = {}
        for container, name in containers.items():
            status = self.get_container_status(container)
            statuses[name] = status if status else "not_found"
        
        return statuses
    
    def start_container(self, container_name: str) -> bool:
        """Start a stopped container"""
        success, _ = self._run_command(["docker", "start", container_name])
        return success
    
    def restart_container(self, container_name: str) -> bool:
        """Restart a container"""
        success, _ = self._run_command(["docker", "restart", container_name])
        return success
    
    def compose_up(self, services: Optional[List[str]] = None, build: bool = False) -> bool:
        """Run docker-compose up"""
        if not self.project_dir:
            self.logger.error("DOCKER", "Project directory not set")
            return False
        
        command = ["docker", "compose", "up", "-d"]
        if build:
            command.append("--build")
        if services:
            command.extend(services)
        
        success, output = self._run_command(command, capture_output=False)
        return success
    
    def compose_down(self) -> bool:
        """Run docker-compose down"""
        if not self.project_dir:
            self.logger.error("DOCKER", "Project directory not set")
            return False
        
        success, _ = self._run_command(["docker", "compose", "down"])
        return success
    
    def compose_ps(self) -> Dict[str, str]:
        """Get docker-compose ps output"""
        if not self.project_dir:
            self.logger.error("DOCKER", "Project directory not set")
            return {}
        
        success, output = self._run_command(["docker", "compose", "ps"])
        if not success:
            return {}
        
        # Parse output to get container statuses
        lines = output.split('\n')
        statuses = {}
        for line in lines[1:]:  # Skip header
            if line.strip():
                parts = line.split()
                if len(parts) >= 3:
                    name = parts[0].replace("streama2026-", "")
                    status = parts[2]
                    statuses[name] = status
        
        return statuses
    
    def wait_for_health(self, container_name: str, timeout: int = 60) -> bool:
        """Wait for container to become healthy with active polling"""
        start_time = time.time()
        check_interval = 2  # Check every 2 seconds
        
        while time.time() - start_time < timeout:
            success, output = self._run_command([
                "docker", "inspect", "-f", "{{.State.Health.Status}}", container_name
            ])
            
            if success and output.strip() == "healthy":
                return True
            
            time.sleep(check_interval)
        
        return False
    
    def get_container_logs(self, container_name: str, tail: int = 50) -> str:
        """Get recent logs from a container"""
        success, output = self._run_command([
            "docker", "logs", "--tail", str(tail), container_name
        ])
        if success:
            return output
        return ""
    
    def rebuild_containers(self, services: list = None) -> bool:
        """Rebuild specific containers"""
        if services is None:
            services = ["backend", "frontend"]
        
        cmd = ["docker", "compose", "up", "-d", "--build"] + services
        success, _ = self._run_command(cmd)
        return success
    
    def reset_environment(self, logger=None) -> bool:
        """Reset Docker environment - stop containers and remove volumes for fresh installation"""
        if logger:
            logger.info("DOCKER", "=== FULL RESET - THIS WILL DELETE ALL DATA ===")
            logger.info("DOCKER", "This will remove all containers, volumes, and network")
        
        # Stop and remove containers
        cmd_stop = ["docker", "compose", "down"]
        success, _ = self._run_command(cmd_stop, cwd=self.project_dir)
        
        if not success:
            if logger:
                logger.error("DOCKER", "Failed to stop containers")
            return False
        
        if logger:
            logger.success("DOCKER", "Containers stopped and removed")
        
        # Remove network
        network = "streama-network"
        cmd_rm_network = ["docker", "network", "rm", network]
        success_network, _ = self._run_command(cmd_rm_network)
        if logger:
            if success_network:
                logger.success("DOCKER", f"Network {network} removed")
            else:
                logger.info("DOCKER", f"Network {network} not found or already removed")
        
        # Remove volumes
        volumes = [
            "streama2026_postgres_data",
            "streama2026_redis_data",
            "streama2026_stream_storage",
            "streama2026_ffmpeg_cache"
        ]
        
        for volume in volumes:
            cmd_rm = ["docker", "volume", "rm", volume]
            # Ignore errors if volume doesn't exist
            success, _ = self._run_command(cmd_rm)
            if logger:
                if success:
                    logger.success("DOCKER", f"Volume {volume} removed")
                else:
                    logger.info("DOCKER", f"Volume {volume} not found or already removed")
        
        if logger:
            logger.success("DOCKER", "All volumes removed")
        
        # Start fresh
        cmd_up = ["docker", "compose", "up", "-d"]
        success, _ = self._run_command(cmd_up, cwd=self.project_dir)
        
        if not success:
            if logger:
                logger.error("DOCKER", "Failed to start containers")
            return False
        
        if logger:
            logger.success("DOCKER", "Containers started with fresh volumes")
        
        return True
    
    def check_network_exists(self, network_name: str) -> bool:
        """Check if a Docker network exists"""
        cmd = ["docker", "network", "ls", "--format", "{{.Name}}"]
        success, output = self._run_command(cmd)
        
        if not success:
            return False
        
        networks = output.strip().split('\n')
        return network_name in networks
    
    def create_network(self, network_name: str) -> bool:
        """Create a Docker network"""
        cmd = ["docker", "network", "create", network_name]
        success, _ = self._run_command(cmd)
        return success
    
    def check_volume_exists(self, volume_name: str) -> bool:
        """Check if Docker volume exists"""
        success, _ = self._run_command([
            "docker", "volume", "inspect", volume_name
        ])
        return success
    
    def get_container_ip(self, container_name: str) -> Optional[str]:
        """Get IP address of a container"""
        success, output = self._run_command([
            "docker", "inspect", "-f", "{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}", 
            container_name
        ])
        if success and output.strip():
            return output.strip()
        return None
    
    def connect_to_network(self, container_name: str, network_name: str) -> bool:
        """Connect a container to a network"""
        success, _ = self._run_command([
            "docker", "network", "connect", network_name, container_name
        ])
        return success
    
    def get_container_name_by_service(self, service_name: str) -> Optional[str]:
        """Get actual container name by service name from docker compose"""
        if not self.project_dir:
            return None
        
        # Use docker compose ps to get the actual container name
        success, output = self._run_command(
            ["docker", "compose", "ps", "-q", service_name],
            cwd=self.project_dir,
            timeout=10
        )
        
        if success and output.strip():
            # Get the full container name from the container ID
            container_id = output.strip().split('\n')[0]
            name_success, name_output = self._run_command(
                ["docker", "inspect", "-f", "{{.Name}}", container_id],
                timeout=10
            )
            if name_success and name_output.strip():
                # Remove leading slash if present
                name = name_output.strip()
                return name.lstrip('/')
        
        return None
