import io
import cv2
import os
import logging
import numpy as np
from PIL import Image, ImageFilter, ImageEnhance

logger = logging.getLogger(__name__)

def prepare_ad_photo(input_path: str, output_path: str, target_size: int = 96) -> dict:
    try:
        # 1. Загружаем изображение для OpenCV
        if isinstance(input_path, bytes):
            img = cv2.imdecode(np.frombuffer(input_path, np.uint8), cv2.IMREAD_COLOR)
        else:
            img = cv2.imread(input_path)
        if img is None:
            return {"success": False, "error": "Не удалось прочитать файл изображения."}
        if isinstance(input_path, bytes):
            img = cv2.imdecode(np.frombuffer(input_path, np.uint8), cv2.IMREAD_COLOR)
        else:
            img = cv2.imread(input_path)
        if img is None:
            return {"success": False, "error": "Не удалось прочитать файл изображения."}

        gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

        # 2. Загружаем каскад Хаара для поиска лица
        cascade_path = cv2.data.haarcascades + 'haarcascade_frontalface_default.xml'
        face_cascade = cv2.CascadeClassifier(cascade_path)

        # 3. Ищем лица
        # Оставляем оптимальное значение minNeighbors=8 для баланса
        faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=8, minSize=(50, 50))

        if len(faces) == 0:
            return {"success": False, "error": "Лицо не найдено! Пожалуйста, сделайте фото анфас при хорошем освещении."}

        if len(faces) > 1:
            faces = sorted(faces, key=lambda f: f[2] * f[3], reverse=True)

        x, y, w, h = faces[0]

        # =========================================================
        # АБСОЛЮТНАЯ ЗАЩИТА ОТ ГРАФИКИ (НОРМАЛИЗОВАННАЯ МАТЕМАТИКА)
        # =========================================================
        # 1. Вырезаем центральную зону лица
        face_roi_original = gray[y + int(h*0.2):y + int(h*0.8), x + int(w*0.2):x + int(w*0.8)]
        
        # 2. Сжимаем до 100x100 пикселей. 
        # Это уравнивает 4K-фотографии и мелкие картинки, делая метрики стабильными.
        face_roi = cv2.resize(face_roi_original, (100, 100))
        
        std_dev = np.std(face_roi)
        laplacian_var = cv2.Laplacian(face_roi, cv2.CV_64F).var()
        unique_colors = len(np.unique(face_roi))

        logger.info(f"Анализ нормализованного лица: std_dev={std_dev:.2f}, laplacian={laplacian_var:.2f}, colors={unique_colors}")

        # ПРОВЕРКА 1: Плоские, замыленные картинки
        if std_dev < 10.0 or laplacian_var < 30.0:
            logger.warning(f"Заблокирован плоский рисунок/замыливание: std={std_dev:.1f}, lap={laplacian_var:.1f}")
            return {"success": False, "error": "Изображение слишком размыто или имеет плоскую текстуру. Отправьте реальное фото."}

        # ПРОВЕРКА 2: Векторы, комиксы и жесткий 2D-арт (Умный порог резкости + цвета)
        # У реального человека на хорошую камеру laplacian может доходить до 500-600, 
        # но при этом всегда будет богатая палитра цветов (шум матрицы). 
        # У мультиков высокая резкость всегда сопровождается бедной палитрой.
        if laplacian_var > 850.0 or (laplacian_var > 400.0 and unique_colors < 150):
            logger.warning(f"Заблокирован резкий 2D-арт: lap={laplacian_var:.1f}, colors={unique_colors}")
            return {"success": False, "error": "Изображение похоже на графический арт или сильно отфильтровано. Пожалуйста, загрузите естественное фото."}

        # ПРОВЕРКА 3: 3D-рендеры и нейросети (Мало шума)
        # На реальной фотографии (даже с хорошим светом) шум матрицы телефона 
        # даст минимум 110-120 уникальных оттенков на 10000 пикселей.
        if unique_colors < 105:
            logger.warning(f"Заблокирован 3D-рендер по палитре: unique_colors={unique_colors}")
            return {"success": False, "error": "Изображение распознано как 3D-модель или рисунок (неестественная палитра). Ждем реальное фото!"}
        # =========================================================

        # 4. Кадрируем (делаем квадрат с отступами вокруг лица)
        margin = int(w * 0.3)
        y1 = max(0, y - int(margin * 1.5))
        y2 = min(img.shape[0], y + h + margin)
        x1 = max(0, x - margin)
        x2 = min(img.shape[1], x + w + margin)

        side = min(x2 - x1, y2 - y1)
        center_x, center_y = (x1 + x2) // 2, (y1 + y2) // 2
        crop_x1 = max(0, center_x - side // 2)
        crop_y1 = max(0, center_y - side // 2)
        crop_x2 = crop_x1 + side
        crop_y2 = crop_y1 + side

        # 5. Обработка через Pillow
        pil_img = Image.open(io.BytesIO(input_path))
        
        # ВАЖНО: Убираем альфа-канал (прозрачность), иначе BMP выдаст ошибку
        if pil_img.mode in ('RGBA', 'P', 'LA'):
            background = Image.new('RGB', pil_img.size, (255, 255, 255))
            background.paste(pil_img, mask=pil_img.split()[3] if pil_img.mode == 'RGBA' else None)
            pil_img = background
        elif pil_img.mode != 'RGB':
            pil_img = pil_img.convert('RGB')
            
        cropped_img = pil_img.crop((crop_x1, crop_y1, crop_x2, crop_y2))

        # Сжимаем до нужного размера (96x96)
        final_img = cropped_img.resize((target_size, target_size), Image.Resampling.LANCZOS)
        
        # Добавляем немного резкости, чтобы лицо не мылилось при 96px
        final_img = final_img.filter(ImageFilter.UnsharpMask(radius=1.5, percent=100, threshold=3))

        # 6. Сохраняем в формате BMP
        final_img.save(output_path, format="BMP")

        # 7. Проверяем финальный вес
        size_kb = os.path.getsize(output_path) / 1024
        
        if size_kb > 99:
             return {"success": False, "error": f"Сбой: размер файла ({size_kb:.1f} KB) превысил лимит в 99 KB."}

        return {"success": True, "size_kb": round(size_kb, 1)}

    except Exception as e:
        logger.error(f"Ошибка в photo_processor: {e}")
        return {"success": False, "error": str(e)}