#!/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:
        # Определяем путь к файлу относительно корня проекта
        file_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "excluded_containers.json")
        
        with open(file_path, "r", encoding="utf-8") as f:
            data = json.load(f)
            return data.get("excluded_containers", [])
    except FileNotFoundError:
        docker_logger.warning(f"Файл excluded_containers.json не найден по пути {file_path}, используем пустой список")
        return []
    except json.JSONDecodeError as e:
        docker_logger.error(f"Ошибка парсинга excluded_containers.json: {e}")
        return []
    except Exception as e:
        docker_logger.error(f"Ошибка загрузки excluded_containers.json: {e}")
        return []

def save_excluded_containers(containers: List[str]) -> bool:
    """
    Сохраняет список исключенных контейнеров в JSON файл
    Автор: Сергей Антропов
    Сайт: https://devops.org.ru
    """
    try:
        # Определяем путь к файлу относительно корня проекта
        file_path = os.path.join(os.path.dirname(os.path.dirname(__file__)), "excluded_containers.json")
        
        data = {
            "excluded_containers": containers,
            "description": "Список контейнеров, которые генерируют слишком много логов и исключаются из отображения"
        }
        
        # Создаем директорию, если она не существует
        os.makedirs(os.path.dirname(file_path), exist_ok=True)
        
        with open(file_path, "w", encoding="utf-8") as f:
            json.dump(data, f, indent=2, ensure_ascii=False)
        
        docker_logger.info(f"Список исключенных контейнеров сохранен в {file_path}")
        return True
    except Exception as e:
        docker_logger.error(f"Ошибка сохранения 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
