#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
LogBoard+ - Docker функции
Автор: Сергей Антропов
Сайт: https://devops.org.ru
"""

import json
import os
from typing import List, Dict, Optional

import docker

from core.config import (
    DEFAULT_TAIL,
    DEFAULT_PROJECT,
    DEFAULT_PROJECTS,
    SKIP_UNHEALTHY
)
from core.logger import docker_logger

# Инициализация Docker клиента
docker_client = docker.from_env()

def load_excluded_containers() -> List[str]:
    """
    Загружает список исключенных контейнеров из JSON файла
    Автор: Сергей Антропов
    Сайт: https://devops.org.ru
    """
    try:
        with open("app/excluded_containers.json", "r", encoding="utf-8") as f:
            data = json.load(f)
            return data.get("excluded_containers", [])
    except FileNotFoundError:
        docker_logger.warning("Файл app/excluded_containers.json не найден, используем пустой список")
        return []
    except json.JSONDecodeError as e:
        docker_logger.error(f"Ошибка парсинга app/excluded_containers.json: {e}")
        return []
    except Exception as e:
        docker_logger.error(f"Ошибка загрузки app/excluded_containers.json: {e}")
        return []

def save_excluded_containers(containers: List[str]) -> bool:
    """
    Сохраняет список исключенных контейнеров в JSON файл
    Автор: Сергей Антропов
    Сайт: https://devops.org.ru
    """
    try:
        data = {
            "excluded_containers": containers,
            "description": "Список контейнеров, которые генерируют слишком много логов и исключаются из отображения"
        }
        with open("app/excluded_containers.json", "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
        return True
    except Exception as e:
        docker_logger.error(f"Ошибка сохранения app/excluded_containers.json: {e}")
        return False

def get_all_projects() -> List[str]:
    """
    Получает список всех проектов Docker Compose с учетом исключенных контейнеров
    Автор: Сергей Антропов
    Сайт: https://devops.org.ru
    """
    projects = set()
    excluded_containers = load_excluded_containers()
    
    try:
        containers = docker_client.containers.list(all=True)
        
        # Словарь для подсчета контейнеров по проектам
        project_containers = {}
        standalone_containers = []
        
        for c in containers:
            try:
                # Пропускаем исключенные контейнеры
                if c.name in excluded_containers:
                    continue
                    
                labels = c.labels or {}
                project = labels.get("com.docker.compose.project")
                
                if project:
                    if project not in project_containers:
                        project_containers[project] = 0
                    project_containers[project] += 1
                else:
                    standalone_containers.append(c.name)
                    
            except Exception:
                continue
        
        # Добавляем проекты, у которых есть хотя бы один неисключенный контейнер
        for project, count in project_containers.items():
            if count > 0:
                projects.add(project)
        
        # Добавляем standalone, если есть неисключенные контейнеры без проекта
        if standalone_containers:
            projects.add("standalone")
            
    except Exception as e:
        docker_logger.error(f"Ошибка получения списка проектов: {e}")
        return []
    
    result = sorted(list(projects))
    docker_logger.info(f"Доступные проекты (с учетом исключенных контейнеров): {result}")
    return result

def list_containers(projects: Optional[List[str]] = None, include_stopped: bool = False) -> List[Dict]:
    """
    Получает список контейнеров с поддержкой множественных проектов
    Автор: Сергей Антропов
    Сайт: https://devops.org.ru
    """
    # Загружаем список исключенных контейнеров из JSON файла
    excluded_containers = load_excluded_containers()
    
    docker_logger.info(f"Список исключенных контейнеров: {excluded_containers}")
    
    items = []
    excluded_count = 0
    
    try:
        # Получаем список контейнеров с базовой обработкой ошибок
        containers = docker_client.containers.list(all=include_stopped)
        
        for c in containers:
            try:
                # Базовая информация о контейнере (без health check)
                basic_info = {
                    "id": c.id[:12],
                    "name": c.name,
                    "status": c.status,
                    "image": "unknown",
                    "service": c.name,
                    "project": None,
                    "health": None,
                    "ports": [],
                    "url": None,
                }
                
                # Безопасно получаем метки
                try:
                    labels = c.labels or {}
                    basic_info["project"] = labels.get("com.docker.compose.project")
                    basic_info["service"] = labels.get("com.docker.compose.service") or c.name
                except Exception:
                    pass  # Используем значения по умолчанию
                
                # Безопасно получаем информацию об образе
                try:
                    if c.image and c.image.tags:
                        basic_info["image"] = c.image.tags[0]
                    elif c.image:
                        basic_info["image"] = c.image.short_id
                except Exception:
                    pass  # Оставляем "unknown"
                
                # Безопасно получаем информацию о портах
                try:
                    ports = c.ports or {}
                    if ports:
                        basic_info["ports"] = list(ports.keys())
                        
                        # Пытаемся найти HTTP/HTTPS порт для создания URL
                        for port_mapping in ports.values():
                            if port_mapping:
                                for mapping in port_mapping:
                                    if isinstance(mapping, dict) and mapping.get("HostPort"):
                                        host_port = mapping["HostPort"]
                                        # Проверяем, что это HTTP порт (80, 443, 8080, 3000, etc.)
                                        host_port_int = int(host_port)
                                        if (host_port_int in [80, 443] or 
                                            (1 <= host_port_int <= 7999) or 
                                            (8000 <= host_port_int <= 65535)):
                                            protocol = "https" if host_port == "443" else "http"
                                            basic_info["url"] = f"{protocol}://localhost:{host_port}"
                                            basic_info["host_port"] = host_port
                                            break
                                if basic_info["url"]:
                                    break
                except Exception:
                    pass  # Оставляем пустые значения
                
                # Фильтрация по проектам
                if projects:
                    # Если проект не указан, считаем его standalone
                    container_project = basic_info["project"] or "standalone"
                    if container_project not in projects:
                        continue
                
                # Фильтрация исключенных контейнеров
                if basic_info["name"] in excluded_containers:
                    excluded_count += 1
                    docker_logger.warning(f"Пропускаем исключенный контейнер: {basic_info['name']}")
                    continue
                
                # Добавляем контейнер в список
                items.append(basic_info)
                
            except Exception as e:
                # Пропускаем контейнеры с критическими ошибками
                docker_logger.warning(f"Пропускаем проблемный контейнер {c.name if hasattr(c, 'name') else 'unknown'} (ID: {c.id[:12]}): {e}")
                continue
        
    except Exception as e:
        docker_logger.error(f"Ошибка получения списка контейнеров: {e}")
        return []
    
    # Сортируем по проекту, сервису и имени
    items.sort(key=lambda x: (x.get("project") or "", x.get("service") or "", x.get("name") or ""))
    
    # Подсчитываем статистику по проектам
    project_stats = {}
    for item in items:
        project = item.get("project") or "standalone"
        if project not in project_stats:
            project_stats[project] = {"visible": 0, "excluded": 0}
        project_stats[project]["visible"] += 1
    
    # Подсчитываем исключенные контейнеры по проектам
    for c in containers:
        try:
            if c.name in excluded_containers:
                labels = c.labels or {}
                project = labels.get("com.docker.compose.project") or "standalone"
                if project not in project_stats:
                    project_stats[project] = {"visible": 0, "excluded": 0}
                project_stats[project]["excluded"] += 1
        except Exception:
            continue
    
    docker_logger.info(f"Статистика: найдено {len(items)} контейнеров, исключено {excluded_count} контейнеров")
    for project, stats in project_stats.items():
        docker_logger.info(f"   📦 {project}: {stats['visible']} видимых, {stats['excluded']} исключенных")
    
    return items

def get_remote_hosts() -> List[str]:
    """
    Получает список удаленных хостов из папки logs/remote
    Автор: Сергей Антропов
    Сайт: https://devops.org.ru
    """
    remote_hosts = []
    remote_logs_dir = os.path.join(os.getcwd(), 'logs', 'remote')
    
    try:
        if os.path.exists(remote_logs_dir):
            for item in os.listdir(remote_logs_dir):
                item_path = os.path.join(remote_logs_dir, item)
                if os.path.isdir(item_path):
                    remote_hosts.append(item)
    except Exception as e:
        docker_logger.error(f"Ошибка получения списка удаленных хостов: {e}")
    
    return sorted(remote_hosts)

def get_remote_containers(hostname: str, include_stopped: bool = False) -> List[Dict]:
    """
    Получает список контейнеров для удаленного хоста
    Автор: Сергей Антропов
    Сайт: https://devops.org.ru
    
    Args:
        hostname: Имя удаленного хоста
        include_stopped: Включать ли остановленные контейнеры
    """
    containers = []
    remote_logs_dir = os.path.join(os.getcwd(), 'logs', 'remote', hostname)
    
    try:
        if os.path.exists(remote_logs_dir):
            for filename in os.listdir(remote_logs_dir):
                if filename.endswith('.log'):
                    # Извлекаем имя контейнера из имени файла
                    # Формат: container-name-YYYYMMDD.log
                    container_name = filename.replace('.log', '')
                    # Убираем дату из конца
                    if '-' in container_name:
                        parts = container_name.split('-')
                        if len(parts) > 1 and parts[-1].isdigit() and len(parts[-1]) == 8:
                            container_name = '-'.join(parts[:-1])
                    
                    # Получаем информацию о файле
                    file_path = os.path.join(remote_logs_dir, filename)
                    stat = os.stat(file_path)
                    
                    # Проверяем, активен ли контейнер (логи обновлялись в последние 5 минут)
                    import time
                    current_time = time.time()
                    last_modified = stat.st_mtime
                    is_active = (current_time - last_modified) < 300  # 5 минут = 300 секунд
                    
                    # Добавляем контейнер только если он активен или если include_stopped=True
                    if is_active or include_stopped:
                        containers.append({
                            "id": f"remote-{hostname}-{container_name}",
                            "name": container_name,
                            "status": "running" if is_active else "stopped",
                            "image": "remote",
                            "service": container_name,
                            "project": "remote",
                            "health": "healthy" if is_active else "unhealthy",
                            "ports": [],
                            "url": None,
                            "hostname": hostname,
                            "is_remote": True,
                            "last_modified": last_modified,
                            "size": stat.st_size
                        })
    except Exception as e:
        docker_logger.error(f"Ошибка получения контейнеров для хоста {hostname}: {e}")
    
    return containers

def get_all_projects_with_remote() -> List[str]:
    """
    Получает список всех проектов включая удаленные
    Автор: Сергей Антропов
    Сайт: https://devops.org.ru
    """
    # Получаем локальные проекты
    local_projects = get_all_projects()
    
    # Добавляем удаленные хосты как проекты
    remote_hosts = get_remote_hosts()
    remote_projects = [f"remote-{host}" for host in remote_hosts]
    
    # Объединяем и сортируем
    all_projects = local_projects + remote_projects
    return sorted(all_projects)

def list_containers_with_remote(projects: Optional[List[str]] = None, include_stopped: bool = False) -> List[Dict]:
    """
    Получает список всех контейнеров включая удаленные
    Автор: Сергей Антропов
    Сайт: https://devops.org.ru
    """
    # Получаем локальные контейнеры
    local_containers = list_containers(projects, include_stopped)
    
    # Добавляем информацию о том, что это локальные контейнеры
    for container in local_containers:
        container["hostname"] = "localhost"
        container["is_remote"] = False
    
    # Получаем удаленные контейнеры
    remote_hosts = get_remote_hosts()
    remote_containers = []
    
    for hostname in remote_hosts:
        # Проверяем, нужно ли включать этот хост
        if projects is None or any(f"remote-{hostname}" in project for project in projects):
            host_containers = get_remote_containers(hostname, include_stopped)
            remote_containers.extend(host_containers)
    
    # Объединяем локальные и удаленные контейнеры
    all_containers = local_containers + remote_containers
    
    # Фильтруем demo и test контейнеры
    filtered_containers = []
    for container in all_containers:
        container_name = container.get("name", "").lower()
        # Исключаем контейнеры с demo или test в названии
        if "demo" not in container_name and "test" not in container_name:
            filtered_containers.append(container)
    
    # Фильтруем по проектам, если указаны
    if projects:
        project_filtered_containers = []
        for container in filtered_containers:
            if container["is_remote"]:
                # Для удаленных контейнеров проверяем соответствие хоста
                if any(f"remote-{container['hostname']}" in project for project in projects):
                    project_filtered_containers.append(container)
            else:
                # Для локальных контейнеров проверяем проект
                if container["project"] in projects or "standalone" in projects:
                    project_filtered_containers.append(container)
        return project_filtered_containers
    
    return filtered_containers
