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

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

from fastapi import APIRouter, Depends, HTTPException, Query, Body, Header
from fastapi.responses import JSONResponse

import docker
import os
import re
from datetime import datetime

from core.auth import get_current_user, verify_api_key
from core.docker import docker_client, DEFAULT_TAIL, get_remote_containers
from core.logger import api_logger

router = APIRouter()

@router.get("/stats/{container_id}")
def api_logs_stats(container_id: str, current_user: str = Depends(get_current_user)):
    """Получить статистику логов контейнера (локального или удаленного)"""
    try:
        # Проверяем, является ли это удаленным контейнером
        if container_id.startswith('remote-'):
            stats = get_remote_log_stats(container_id)
        else:
            # Ищем локальный контейнер
            container = None
            for c in docker_client.containers.list(all=True):
                if c.id.startswith(container_id):
                    container = c
                    break
            
            if container is None:
                return JSONResponse({"error": "Container not found"}, status_code=404)
            
            # Получаем логи
            logs = container.logs(tail=1000).decode(errors="ignore")
            
            # Подсчитываем статистику
            stats = {"debug": 0, "info": 0, "warn": 0, "error": 0}
            
            for line in logs.split('\n'):
                if not line.strip():
                    continue
                
                line_lower = line.lower()
                if 'level=debug' in line_lower or 'debug' in line_lower:
                    stats["debug"] += 1
                elif 'level=info' in line_lower or 'info' in line_lower:
                    stats["info"] += 1
                elif 'level=warning' in line_lower or 'level=warn' in line_lower or 'warning' in line_lower or 'warn' in line_lower:
                    stats["warn"] += 1
                elif 'level=error' in line_lower or 'error' in line_lower:
                    stats["error"] += 1
        
        return JSONResponse(
            content=stats,
            headers={
                "Cache-Control": "no-cache, no-store, must-revalidate",
                "Pragma": "no-cache",
                "Expires": "0"
            }
        )
        
    except Exception as e:
        api_logger.error(f"Error getting log stats for {container_id}: {e}")
        return JSONResponse({"error": str(e)}, status_code=500)

@router.get("/{container_id}")
def api_logs(
    container_id: str, 
    tail: str = Query(str(DEFAULT_TAIL), description="Количество последних строк логов или 'all' для всех логов"),
    since: Optional[str] = Query(None, description="Время начала в формате ISO или относительное время (например, '10m', '1h')"),
    current_user: str = Depends(get_current_user)
):
    """
    Получить логи контейнера через AJAX (локального или удаленного)
    
    Args:
        container_id: ID контейнера (локального или удаленного в формате 'remote-hostname-container')
        tail: Количество последних строк или 'all' для всех логов (по умолчанию 500)
        since: Время начала для фильтрации логов
    
    Returns:
        JSON с логами и метаданными
    """
    try:
        # Проверяем, является ли это удаленным контейнером
        if container_id.startswith('remote-'):
            # Получаем логи удаленного контейнера
            tail_lines = DEFAULT_TAIL if tail.lower() == 'all' else int(tail)
            logs = get_remote_logs(container_id, tail=tail_lines, since=since)
            
            # Форматируем логи для совместимости с локальными
            log_lines = []
            for line in logs:
                if line.strip():
                    # Пытаемся извлечь временную метку из строки
                    timestamp_match = re.match(r'^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?)', line)
                    if timestamp_match:
                        timestamp = timestamp_match.group(1) + 'Z'  # Добавляем Z для совместимости
                        message = line[len(timestamp_match.group(0)):].strip()
                        log_lines.append({
                            'timestamp': timestamp,
                            'message': message,
                            'raw': line
                        })
                    else:
                        # Если не можем извлечь время, используем текущее
                        timestamp = datetime.now().isoformat() + 'Z'
                        log_lines.append({
                            'timestamp': timestamp,
                            'message': line,
                            'raw': line
                        })
            
            return JSONResponse(
                content={
                    "logs": log_lines,
                    "container_id": container_id,
                    "is_remote": True
                },
                headers={
                    "Cache-Control": "no-cache, no-store, must-revalidate",
                    "Pragma": "no-cache",
                    "Expires": "0"
                }
            )
        else:
            # Ищем локальный контейнер
            container = None
            for c in docker_client.containers.list(all=True):
                if c.id.startswith(container_id):
                    container = c
                    break
            
            if container is None:
                return JSONResponse({"error": "Container not found"}, status_code=404)
        
        # Формируем параметры для получения логов
        log_params = {
            'timestamps': True
        }
        
        # Обрабатываем параметр tail
        if tail.lower() == 'all':
            # Для всех логов не указываем параметр tail
            pass
        else:
            try:
                tail_lines = int(tail)
                log_params['tail'] = tail_lines
            except ValueError:
                # Если не удалось преобразовать в число, используем значение по умолчанию
                log_params['tail'] = DEFAULT_TAIL
        
        # Добавляем фильтр по времени, если указан (используем Unix timestamp секундной точности)
        if since:
            def _parse_since(value: str) -> Optional[int]:
                try:
                    # Числовое значение (unix timestamp)
                    if re.fullmatch(r"\d+", value or ""):
                        return int(value)
                    # ISO 8601 с Z
                    if value.endswith('Z'):
                        dt = datetime.fromisoformat(value.replace('Z', '+00:00'))
                        return int(dt.timestamp())
                    # Пытаемся распарсить как ISO без Z
                    try:
                        dt2 = datetime.fromisoformat(value)
                        if dt2.tzinfo is None:
                            # Считаем UTC, если таймзона не указана
                            from datetime import timezone
                            dt2 = dt2.replace(tzinfo=timezone.utc)
                        return int(dt2.timestamp())
                    except Exception:
                        pass
                except Exception:
                    return None
                return None

            parsed_since = _parse_since(since)
            if parsed_since is not None:
                log_params['since'] = parsed_since
        
        # Получаем логи
        logs = container.logs(**log_params).decode(errors="ignore")
        
        # Разбиваем на строки и обрабатываем
        log_lines = []
        for line in logs.split('\n'):
            if line.strip():
                # Извлекаем временную метку и сообщение
                timestamp_match = re.match(r'^(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d+Z)\s+(.+)$', line)
                if timestamp_match:
                    timestamp, message = timestamp_match.groups()
                    log_lines.append({
                        'timestamp': timestamp,
                        'message': message,
                        'raw': line
                    })
                else:
                    # Если временная метка не найдена, используем всю строку как сообщение
                    log_lines.append({
                        'timestamp': None,
                        'message': line,
                        'raw': line
                    })
        
        # Получаем информацию о контейнере
        container_info = {
            'id': container.id,
            'name': container.name,
            'status': container.status,
            'image': container.image.tags[0] if container.image.tags else container.image.id,
            'created': container.attrs['Created'],
            'state': container.attrs['State']
        }
        
        return JSONResponse(
            content={
                'container': container_info,
                'logs': log_lines,
                'total_lines': len(log_lines),
                'tail': tail,
                'since': since,
                'timestamp': datetime.now().isoformat()
            },
            headers={
                "Cache-Control": "no-cache, no-store, must-revalidate",
                "Pragma": "no-cache",
                "Expires": "0"
            }
        )
        
    except Exception as e:
        api_logger.error(f"Error getting logs for {container_id}: {e}")
        return JSONResponse({"error": str(e)}, status_code=500)

@router.post("/snapshot")
def api_snapshot(
    current_user: str = Depends(get_current_user),
    container_id: str = Body(..., embed=True),
    service: str = Body("", embed=True),
    content: str = Body("", embed=True),
):
    """Сохранить снимок логов"""
    import os
    from core.config import SNAP_DIR
    
    # Save posted content as a snapshot file
    safe_service = re.sub(r"[^a-zA-Z0-9_.-]+", "_", service or container_id[:12])
    ts = os.getenv("TZ_TS") or ""
    from datetime import datetime
    stamp = datetime.now().strftime("%Y%m%d-%H%M%S")
    fname = f"{safe_service}-{stamp}.log"
    fpath = os.path.join(SNAP_DIR, fname)
    with open(fpath, "w", encoding="utf-8") as f:
        f.write(content)
    url = f"/snapshots/{fname}"
    return {"file": fname, "url": url}


@router.post("/remote")
async def api_remote_logs(
    request_data: Dict = Body(...),
    authorization: str = Header(None)
):
    """
    Прием логов от удаленных клиентов
    
    Args:
        request_data: Данные запроса с логами
        authorization: Заголовок авторизации
    
    Returns:
        JSON с результатом обработки
    """
    try:
        # Проверяем авторизацию
        if not authorization or not authorization.startswith('Bearer '):
            api_logger.warning("Unauthorized remote log request - missing or invalid authorization header")
            raise HTTPException(status_code=401, detail="Unauthorized")
        
        api_key = authorization.replace('Bearer ', '')
        if not verify_api_key(api_key):
            api_logger.warning("Unauthorized remote log request - invalid API key")
            raise HTTPException(status_code=401, detail="Invalid API key")
        
        # Извлекаем данные из запроса
        hostname = request_data.get('hostname')
        container_name = request_data.get('container_name')
        logs = request_data.get('logs', [])
        timestamp = request_data.get('timestamp')
        
        if not all([hostname, container_name, logs]):
            api_logger.error("Invalid remote log request - missing required fields")
            raise HTTPException(status_code=400, detail="Missing required fields")
        
        # Создаем директорию для удаленных логов, если не существует
        remote_logs_dir = os.path.join(os.getcwd(), 'logs', 'remote', hostname)
        os.makedirs(remote_logs_dir, exist_ok=True)
        
        # Формируем имя файла для логов
        safe_container_name = re.sub(r"[^a-zA-Z0-9_.-]+", "_", container_name)
        log_filename = f"{safe_container_name}-{datetime.now().strftime('%Y%m%d')}.log"
        log_filepath = os.path.join(remote_logs_dir, log_filename)
        
        # Записываем логи в файл
        with open(log_filepath, 'a', encoding='utf-8') as f:
            for log_line in logs:
                f.write(f"{log_line}\n")
        
        # Логируем информацию о полученных логах
        api_logger.info(
            f"Received {len(logs)} log lines from host '{hostname}' "
            f"container '{container_name}' at {timestamp}"
        )
        
        return JSONResponse(
            content={
                "status": "success",
                "message": f"Received {len(logs)} log lines",
                "hostname": hostname,
                "container_name": container_name,
                "timestamp": timestamp,
                "log_file": log_filename
            },
            status_code=200
        )
        
    except HTTPException:
        raise
    except Exception as e:
        api_logger.error(f"Error processing remote logs: {e}")
        raise HTTPException(status_code=500, detail="Internal server error")

def get_remote_logs(container_id: str, tail: int = DEFAULT_TAIL, since: Optional[str] = None) -> List[str]:
    """
    Получить логи удаленного контейнера из файла
    
    Args:
        container_id: ID удаленного контейнера в формате 'remote-hostname-container'
        tail: Количество последних строк
        since: Время начала для фильтрации
    
    Returns:
        Список строк логов
    """
    try:
        # Парсим ID удаленного контейнера
        if not container_id.startswith('remote-'):
            return []
        
        parts = container_id.split('-', 2)  # Разделяем на 'remote', 'hostname', 'container'
        if len(parts) != 3:
            return []
        
        hostname = parts[1]
        container_name = parts[2]
        
        # Ищем файл логов
        remote_logs_dir = os.path.join(os.getcwd(), 'logs', 'remote', hostname)
        if not os.path.exists(remote_logs_dir):
            return []
        
        # Ищем файл с логами для этого контейнера
        log_file = None
        for filename in os.listdir(remote_logs_dir):
            if filename.startswith(f"{container_name}-") and filename.endswith('.log'):
                log_file = os.path.join(remote_logs_dir, filename)
                break
        
        if not log_file or not os.path.exists(log_file):
            return []
        
        # Читаем логи из файла
        with open(log_file, 'r', encoding='utf-8') as f:
            lines = f.readlines()
        
        # Фильтруем по времени, если указано
        if since:
            try:
                since_time = datetime.fromisoformat(since.replace('Z', '+00:00'))
                filtered_lines = []
                for line in lines:
                    # Пытаемся извлечь время из строки лога
                    # Предполагаем формат: 2025-08-20T16:15:56.608911 [container] INFO: message
                    time_match = re.match(r'(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?)', line)
                    if time_match:
                        line_time = datetime.fromisoformat(time_match.group(1).replace('Z', '+00:00'))
                        if line_time >= since_time:
                            filtered_lines.append(line)
                    else:
                        # Если не можем извлечь время, добавляем строку
                        filtered_lines.append(line)
                lines = filtered_lines
            except Exception as e:
                api_logger.warning(f"Error filtering logs by time: {e}")
        
        # Применяем tail
        if tail != 'all' and len(lines) > tail:
            lines = lines[-tail:]
        
        return [line.rstrip('\n') for line in lines]
        
    except Exception as e:
        api_logger.error(f"Error reading remote logs for {container_id}: {e}")
        return []

def get_remote_log_stats(container_id: str) -> Dict[str, int]:
    """
    Получить статистику логов удаленного контейнера
    
    Args:
        container_id: ID удаленного контейнера
    
    Returns:
        Словарь со статистикой
    """
    logs = get_remote_logs(container_id, tail=1000)
    stats = {"debug": 0, "info": 0, "warn": 0, "error": 0}
    
    for line in logs:
        if not line.strip():
            continue
        
        line_lower = line.lower()
        if 'level=debug' in line_lower or 'debug' in line_lower:
            stats["debug"] += 1
        elif 'level=info' in line_lower or 'info' in line_lower:
            stats["info"] += 1
        elif 'level=warning' in line_lower or 'level=warn' in line_lower or 'warning' in line_lower or 'warn' in line_lower:
            stats["warn"] += 1
        elif 'level=error' in line_lower or 'error' in line_lower:
            stats["error"] += 1
    
    return stats
