"""
Утилиты для обработки изображений: нормализация, преобразование цветов и т.д.
"""

import numpy as np
from typing import Optional
from pathlib import Path

# Пытаемся импортировать доступные библиотеки для JP2
JP2_AVAILABLE = False
JP2_METHOD = None
JP2_ERROR = None

# Приоритет: imagecodecs > PIL > glymur
try:
    import imagecodecs
    import tifffile
    JP2_AVAILABLE = True
    JP2_METHOD = "imagecodecs"
    print("✓ Используется imagecodecs для JP2 (рекомендуемый метод)")
except ImportError as e:
    JP2_ERROR = str(e)
    try:
        from PIL import Image
        JP2_AVAILABLE = True
        JP2_METHOD = "pil"
        print("✓ Используется PIL для JP2 (ограниченная поддержка)")
    except ImportError:
        try:
            import glymur
            from glymur import Jp2k
            JP2_AVAILABLE = True
            JP2_METHOD = "glymur"
            print("✓ Используется Glymur для JP2")
        except ImportError:
            print("✗ Нет доступных библиотек для JP2")
            print("  Установите: pip install imagecodecs")


class ImageProcessor:
    """Обработчик изображений для солнечных снимков"""

    @staticmethod
    def load_jp2(filepath: str) -> Optional[np.ndarray]:
        """
        Загружает JP2 файл и возвращает numpy array

        Args:
            filepath: Путь к JP2 файлу

        Returns:
            numpy array с изображением (нормализованный 8-бит)
        """
        if not JP2_AVAILABLE:
            print(f"Ошибка: Нет доступных библиотек для JP2. Файл: {filepath}")
            print(f"  Причина: {JP2_ERROR}")
            return None

        try:
            img_data = None

            if JP2_METHOD == "imagecodecs":
                # Метод через imagecodecs (работает без внешнего OpenJPEG)
                import imagecodecs
                with open(filepath, 'rb') as f:
                    data = f.read()
                img_data = imagecodecs.jpeg2k_decode(data)

            elif JP2_METHOD == "pil":
                # Метод через PIL (базовая поддержка)
                from PIL import Image
                with Image.open(filepath) as img:
                    img_data = np.array(img)

            elif JP2_METHOD == "glymur":
                # Метод через Glymur
                from glymur import Jp2k
                jp2 = Jp2k(filepath)
                img_data = jp2[:]

            if img_data is None:
                print(f"Не удалось загрузить: {filepath}")
                return None

            # Нормализуем 16-битные данные в 8-бит
            if img_data.dtype == np.uint16:
                img_data = ImageProcessor.percent_normalize(img_data)
            elif img_data.dtype == np.uint8:
                pass  # уже 8-бит
            else:
                # Конвертируем в uint8
                if img_data.max() > 0:
                    img_data = ((img_data - img_data.min()) / (img_data.max() - img_data.min()) * 255).astype(np.uint8)
                else:
                    img_data = np.zeros_like(img_data, dtype=np.uint8)

            # Если изображение черно-белое, дублируем в RGB
            if len(img_data.shape) == 2:
                img_data = np.stack([img_data] * 3, axis=2)
            elif len(img_data.shape) == 3 and img_data.shape[2] == 1:
                img_data = np.concatenate([img_data] * 3, axis=2)

            return img_data

        except Exception as e:
            print(f"Ошибка загрузки JP2 ({JP2_METHOD}): {e}")
            return None

    @staticmethod
    def percent_normalize(img_16bit: np.ndarray, low_percent: float = 0.5, high_percent: float = 99.5) -> np.ndarray:
        """
        Процентная нормализация для астрономических изображений

        Args:
            img_16bit: 16-битное изображение
            low_percent: Нижний процент отсечения
            high_percent: Верхний процент отсечения

        Returns:
            8-битное нормализованное изображение
        """
        # Вычисляем перцентили
        low_val = np.percentile(img_16bit, low_percent)
        high_val = np.percentile(img_16bit, high_percent)

        # Защита от одинаковых значений
        if high_val <= low_val:
            high_val = low_val + 1

        # Клиппируем и нормализуем
        img_clipped = np.clip(img_16bit, low_val, high_val)
        img_normalized = ((img_clipped - low_val) / (high_val - low_val) * 255).astype(np.uint8)

        return img_normalized

    @staticmethod
    def apply_color_map(img_8bit: np.ndarray, color_map: str = "default") -> np.ndarray:
        """
        Применяет цветовую карту к черно-белому изображению

        Args:
            img_8bit: 8-битное черно-белое изображение
            color_map: Название цветовой карты

        Returns:
            RGB изображение
        """
        # Встроенные цветовые карты для разных спектров
        color_maps = {
            "AIA 335": "hot",      # золотистый
            "AIA 304": "magma",    # красный
            "AIA 193": "plasma",   # пурпурный
            "AIA 171": "viridis",  # зеленый
            "AIA 211": "inferno",  # оранжевый
            "AIA 131": "cool",     # синий
            "LASCO C2": "gray",    # серый
            "default": "gray"
        }

        # Выбираем цветовую карту
        cmap_name = color_maps.get(color_map, color_maps["default"])

        # Если нет matplotlib, используем простую цветовую карту
        try:
            import matplotlib.pyplot as plt
            cmap = plt.get_cmap(cmap_name)
            colored = (cmap(img_8bit / 255.0)[:, :, :3] * 255).astype(np.uint8)
            return colored
        except ImportError:
            # Упрощенная цветовая карта (красный-зеленый-синий)
            if cmap_name == "hot":
                r = img_8bit
                g = np.clip((img_8bit.astype(int) - 85) * 3, 0, 255).astype(np.uint8)
                b = np.clip((img_8bit.astype(int) - 170) * 3, 0, 255).astype(np.uint8)
                return np.stack([r, g, b], axis=2)
            elif cmap_name == "viridis":
                r = np.clip(img_8bit * 0.2, 0, 255).astype(np.uint8)
                g = np.clip(img_8bit * 0.6, 0, 255).astype(np.uint8)
                b = np.clip(img_8bit * 0.9, 0, 255).astype(np.uint8)
                return np.stack([r, g, b], axis=2)
            else:
                return np.stack([img_8bit] * 3, axis=2)

    @staticmethod
    def composite_layers(layers_data: list, opacities: list) -> np.ndarray:
        """
        Композитинг нескольких слоев изображений

        Args:
            layers_data: Список numpy массивов изображений
            opacities: Список прозрачностей (0-1)

        Returns:
            Скомпозированное изображение
        """
        if not layers_data:
            return None

        # Приводим все к одному размеру
        min_h = min(img.shape[0] for img in layers_data)
        min_w = min(img.shape[1] for img in layers_data)

        resized_layers = []
        for img in layers_data:
            if img.shape[0] != min_h or img.shape[1] != min_w:
                from PIL import Image
                pil_img = Image.fromarray(img)
                pil_img = pil_img.resize((min_w, min_h), Image.Resampling.LANCZOS)
                resized_layers.append(np.array(pil_img).astype(float))
            else:
                resized_layers.append(img.astype(float))

        # Композитим
        result = resized_layers[0] * opacities[0]
        for i in range(1, len(resized_layers)):
            result += resized_layers[i] * opacities[i]

        # Нормализуем
        result = np.clip(result, 0, 255).astype(np.uint8)

        return result

    @staticmethod
    def create_test_image(width: int = 1024, height: int = 1024) -> np.ndarray:
        """
        Создает тестовое изображение Солнца для отладки

        Args:
            width: Ширина изображения
            height: Высота изображения

        Returns:
            RGB изображение
        """
        # Создаем черный фон
        img = np.zeros((height, width, 3), dtype=np.uint8)

        # Рисуем круг Солнца
        y, x = np.ogrid[:height, :width]
        center_y, center_x = height // 2, width // 2
        radius = min(height, width) // 3

        mask = (x - center_x)**2 + (y - center_y)**2 <= radius**2

        # Градиент для Солнца
        distances = np.sqrt((x - center_x)**2 + (y - center_y)**2)
        gradient = 1 - (distances / radius)
        gradient = np.clip(gradient, 0, 1)

        # Цвета для разных каналов
        img[mask, 0] = (200 * gradient[mask]).astype(np.uint8)  # Red
        img[mask, 1] = (150 * gradient[mask]).astype(np.uint8)  # Green
        img[mask, 2] = (80 * gradient[mask]).astype(np.uint8)   # Blue

        # Добавляем солнечные пятна
        spots = [
            (center_x - 100, center_y - 50, 30),
            (center_x + 80, center_y + 40, 25),
            (center_x - 30, center_y + 90, 20),
            (center_x + 120, center_y - 80, 15),
        ]

        for sx, sy, r in spots:
            spot_mask = (x - sx)**2 + (y - sy)**2 <= r**2
            img[spot_mask] = [50, 30, 20]

        return img