"""
Сервисы (бизнес-логика)
"""
import tempfile
import os
from typing import Dict, Type

from core.models import UploadRequest, UploadResult, DataRequest, DataResult
from core.ports import ParserPort, StoragePort


# Глобальный словарь парсеров
PARSERS: Dict[str, Type[ParserPort]] = {}


def get_parser(report_type: str) -> ParserPort:
    """Получение парсера по типу отчета"""
    if report_type not in PARSERS:
        available_parsers = list(PARSERS.keys())
        raise ValueError(f"Неизвестный тип отчета '{report_type}'. Доступные типы: {available_parsers}")

    return PARSERS[report_type]()


class ReportService:
    """Сервис для работы с отчетами (только S3)"""

    def __init__(self, storage: StoragePort):
        self.storage = storage

    def upload_report(self, request: UploadRequest) -> UploadResult:
        """Загрузка отчета"""
        try:
            # Получаем парсер для данного типа отчета
            parser = get_parser(request.report_type)

            # Сохраняем файл во временную директорию
            suff = "." + str(request.file_name.split('.')[-1])
            with tempfile.NamedTemporaryFile(delete=False, suffix=suff) as temp_file:
                temp_file.write(request.file_content)
                temp_file_path = temp_file.name

            try:
                # Парсим файл
                parse_params = request.parse_params or {}
                df = parser.parse(temp_file_path, parse_params)

                # Генерируем object_id
                object_id = f"nin_excel_data_{request.report_type}"

                # Удаляем старый объект, если он существует и хранилище доступно
                if self.storage.object_exists(object_id):
                    self.storage.delete_object(object_id)
                    print(f"Старый объект удален: {object_id}")

                # Сохраняем в хранилище
                if self.storage.save_dataframe(df, object_id):
                    return UploadResult(
                        success=True,
                        message="Отчет успешно загружен",
                        object_id=object_id
                    )
                else:
                    return UploadResult(
                        success=False,
                        message="Ошибка при сохранении в хранилище. Возможно, MinIO недоступен."
                    )

            finally:
                # Удаляем временный файл
                os.unlink(temp_file_path)

        except Exception as e:
            return UploadResult(
                success=False,
                message=f"Ошибка при обработке отчета: {str(e)}"
            )

    def get_data(self, request: DataRequest) -> DataResult:
        """Получение данных из отчета"""
        try:
            # Генерируем object_id
            object_id = f"nin_excel_data_{request.report_type}"

            # Проверяем существование объекта
            if not self.storage.object_exists(object_id):
                return DataResult(
                    success=False,
                    message=f"Отчет типа '{request.report_type}' не найден. Возможно, MinIO недоступен или отчет не был загружен."
                )

            # Загружаем DataFrame из хранилища
            df = self.storage.load_dataframe(object_id)
            if df is None:
                return DataResult(
                    success=False,
                    message="Ошибка при загрузке данных из хранилища. Возможно, MinIO недоступен."
                )

            # Получаем парсер
            parser = get_parser(request.report_type)

            # Получаем значение
            value = parser.get_value(df, request.get_params)

            # Формируем результат
            if value is not None:
                if hasattr(value, 'to_dict'):
                    result_data = dict(value)
                else:
                    result_data = {"value": value}
                return DataResult(success=True, data=result_data)
            else:
                return DataResult(success=False, message="Значение не найдено")

        except Exception as e:
            return DataResult(
                success=False,
                message=f"Ошибка при получении данных: {str(e)}"
            )
