import os
import multiprocessing
import uvicorn
from typing import Dict, List
from fastapi import FastAPI, File, UploadFile, HTTPException, status
from fastapi.responses import JSONResponse

from adapters.storage import MinIOStorageAdapter
from adapters.parsers import SvodkaPMParser, SvodkaCAParser, MonitoringFuelParser

from core.models import UploadRequest, DataRequest
from core.services import ReportService, PARSERS

from app.schemas import (
    ServerInfoResponse,
    UploadResponse, UploadErrorResponse,
    SvodkaPMTotalOGsRequest, SvodkaPMSingleOGRequest,
    SvodkaCARequest,
    MonitoringFuelMonthRequest, MonitoringFuelTotalRequest
)


# Парсеры
PARSERS.update({
    'svodka_pm': SvodkaPMParser,
    'svodka_ca': SvodkaCAParser,
    'monitoring_fuel': MonitoringFuelParser,
    # 'svodka_plan_sarnpz': SvodkaPlanSarnpzParser,
})

# Адаптеры
storage_adapter = MinIOStorageAdapter()


def get_report_service() -> ReportService:
    return ReportService(storage_adapter)


tags_metadata = [
    {
        "name": "Общее",
        "display_name": "Общее",
    },
    {
        "name": SvodkaPMParser.name,
        "description": "✅ Ready",
    },
    {
        "name": SvodkaCAParser.name,
        "description": "✅ Ready",
        "display_name": "Сводка ПМ",
    },
    {
        "name": MonitoringFuelParser.name,
        "description": "✅ Ready",
        "display_name": "Мониторинг топлива",
    },
    # {
    #     "name": MonitoringFuelParser.name,
    #     "description": "⚠️ WORK IN PROGRESS",
    # },

]

app = FastAPI(
    title="NIN Excel Parsers API",
    description="API для парсинга сводок и работы с данными экселей НиН",
    version="1.0.0",
    openapi_tags=tags_metadata,
)


@app.get("/", tags=["Общее"])
async def root():
    return {"message": "Svodka Parser API", "version": "1.0.0"}


@app.get("/parsers", tags=["Общее"],
         summary="Список доступных парсеров",
         description="Возвращает список идентификаторов всех доступных парсеров",
         response_model=Dict[str, List[str]],
         responses={
            200: {
                "content": {
                    "application/json": {
                        "example": {
                            "parsers": ["monitoring_fuel", "svodka_ca", "svodka_pm"]
                        }
                    }
                }
            }
        },)
async def get_available_parsers():
    """Получение списка доступных парсеров"""
    parsers = list(PARSERS.keys())
    return {"parsers": parsers}


@app.get("/server-info", tags=["Общее"],
         summary="Информация о сервере",
         response_model=ServerInfoResponse,)
async def server_info():
    return {
        "process_id": os.getpid(),
        "parent_id": os.getppid(),
        "cpu_cores": multiprocessing.cpu_count(),
        "memory_mb": os.sysconf('SC_PAGE_SIZE') * os.sysconf('SC_PHYS_PAGES') / (1024. ** 2)
    }


# @app.get("/svodka_pm/schema", tags=[SvodkaPMParser.name])
# async def get_svodka_pm_schema():
#     """Получение схемы параметров для парсера сводок ПМ факта и плана"""
#     parser = PARSERS['svodka_pm']()
#     return parser.get_schema()


# @app.get("/svodka_ca/schema", tags=[SvodkaCAParser.name])
# async def get_svodka_ca_schema():
#     """Получение схемы параметров для парсера сводки СА"""
#     parser = PARSERS['svodka_ca']()
#     return parser.get_schema()


# @app.get("/monitoring_fuel/schema", tags=[MonitoringFuelParser.name])
# async def get_monitoring_fuel_schema():
#     """Получение схемы параметров для парсера мониторинга топлива"""
#     parser = PARSERS['monitoring_fuel']()
#     return parser.get_schema()


@app.post("/svodka_pm/upload-zip", tags=[SvodkaPMParser.name],
          summary="Загрузка файлов сводок ПМ одним ZIP-архивом",
          response_model=UploadResponse,
          responses={
            400: {"model": UploadErrorResponse, "description": "Неверный формат архива или файлов"},
            500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
          },)
async def upload_svodka_pm_zip(
        zip_file: UploadFile = File(..., description="ZIP архив с Excel файлами (.zip)")
):
    """Загрузка файлов сводок ПМ (факта и плана) по всем ОГ в **одном ZIP-архиве**

    **Шаблоны названий файлов:**
    - Факт: `svodka_fact_pm_<OG_ID>.xlsm`
    - План: `svodka_plan_pm_<OG_ID>.xlsx`
    """
    report_service = get_report_service()
    try:
        if not zip_file.filename.lower().endswith('.zip'):
            return JSONResponse(
                status_code=status.HTTP_400_BAD_REQUEST,
                content=UploadErrorResponse(
                    message="Файл должен быть ZIP архивом",
                    error_code="INVALID_FILE_TYPE",
                    details={
                        "expected_formats": [".zip"],
                        "received_format": zip_file.filename.split('.')[-1] if '.' in zip_file.filename else "unknown"
                    }
                ).model_dump()
            )
        file_content = await zip_file.read()
        # Создаем запрос
        request = UploadRequest(
            report_type='svodka_pm',
            file_content=file_content,
            file_name=zip_file.filename
        )
        # Загружаем отчет
        result = report_service.upload_report(request)
        if result.success:
            return UploadResponse(
                success=True,
                message=result.message,
                object_id=result.object_id
            )
        else:
            return JSONResponse(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                content=UploadErrorResponse(
                    message=result.message,
                    error_code="ERR_UPLOAD"
                ).model_dump(),
            )

    except HTTPException:
        raise
    except Exception as e:
        return JSONResponse(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            content=UploadErrorResponse(
                message=f"Внутренняя ошибка сервера: {str(e)}",
                error_code="INTERNAL_SERVER_ERROR"
            ).model_dump()
        )


# @app.post("/svodka_pm/upload", tags=[SvodkaPMParser.name])
# async def upload_svodka_pm(
#     file: UploadFile = File(...)
# ):
#     report_service = get_report_service()
#     """
#     Загрузка отчета сводки факта СарНПЗ

#     - file: Excel файл для загрузки
#     """
#     try:
#         # Проверяем тип файла
#         if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
#             raise HTTPException(
#                 status_code=status.HTTP_400_BAD_REQUEST,
#                 detail="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)"
#             )

#         # Читаем содержимое файла
#         file_content = await file.read()

#         # Создаем запрос
#         request = UploadRequest(
#             report_type='svodka_pm',
#             file_content=file_content,
#             file_name=file.filename
#         )

#         # Загружаем отчет
#         result = report_service.upload_report(request)
#         # print(result)
#         if result.success:
#             return {
#                 "success": True,
#                 "message": result.message,
#                 "object_id": result.object_id
#             }
#         else:
#             raise HTTPException(status_code=500, detail=result.message)

#     except HTTPException:
#         raise
#     except Exception as e:
#         raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")


@app.post("/svodka_pm/get_single_og", tags=[SvodkaPMParser.name],
          summary="Получение данных по одному ОГ")
async def get_svodka_pm_single_og(
    request_data: SvodkaPMSingleOGRequest
):
    """Получение данных из сводок ПМ (факта и плана) по одному ОГ

    ### Структура параметров:
    - `id`: **Идентификатор МА** для запрашиваемого ОГ (обязательный)
    - `codes`: **Массив кодов** выбираемых строк (обязательный)
    - `columns`: **Массив названий** выбираемых столбцов (обязательный)
    - `search`: **Опциональный параметр** для фильтрации ("Итого" или null)

    ### Пример тела запроса:
    ```json
    {
        "id": "SNPZ",
        "codes": [78, 79],
        "columns": ["ПП", "СЭБ"]
    }
    ```
    """
    report_service = get_report_service()
    """
    Получение данных из отчета сводки факта СарНПЗ

    - id: ID ОГ
    - codes: коды выбираемых строк [78, 79]
    - columns: выбираемые колонки ["БП", "СЭБ"]
    - search: "Итого" не обязательный
    """
    try:
        # Создаем запрос
        request_dict = request_data.model_dump()
        request_dict['mode'] = 'single'
        request = DataRequest(
            report_type='svodka_pm',
            get_params=request_dict
        )

        # Получаем данные
        result = report_service.get_data(request)

        if result.success:
            return {
                "success": True,
                "data": result.data
            }
        else:
            raise HTTPException(status_code=404, detail=result.message)

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")


@app.post("/svodka_pm/get_total_ogs", tags=[SvodkaPMParser.name],
          summary="Получение данных по всем ОГ")
async def get_svodka_pm_total_ogs(
    request_data: SvodkaPMTotalOGsRequest
):
    """Получение данных из сводок ПМ (факта и плана) по всем ОГ

    ### Структура параметров:
    - `codes`: **Массив кодов** выбираемых строк (обязательный)
    - `columns`: **Массив названий** выбираемых столбцов (обязательный)
    - `search`: **Опциональный параметр** для фильтрации ("Итого" или null)

    ### Пример тела запроса:
    ```json
    {
        "codes": [78, 79, 394, 395, 396, 397, 81, 82, 83, 84],
        "columns": ["БП", "ПП", "СЭБ"]
    }
    ```
    """
    report_service = get_report_service()
    """
    Получение данных из отчета сводки факта СарНПЗ

    - codes: коды выбираемых строк [78, 79]
    - columns: выбираемые колонки ["БП", "СЭБ"]
    - search: "Итого"
    """
    try:
        # Создаем запрос
        request_dict = request_data.model_dump()
        request_dict['mode'] = 'total'
        request = DataRequest(
            report_type='svodka_pm',
            get_params=request_dict
        )

        # Получаем данные
        result = report_service.get_data(request)

        if result.success:
            return {
                "success": True,
                "data": result.data
            }
        else:
            raise HTTPException(status_code=404, detail=result.message)

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")


# @app.post("/svodka_pm/get_data", tags=[SvodkaPMParser.name])
# async def get_svodka_pm_data(
#     request_data: dict
# ):
#     report_service = get_report_service()
#     """
#     Получение данных из отчета сводки факта СарНПЗ

#     - indicator_id: ID индикатора
#     - code: Код для поиска
#     - search_value: Опциональное значение для поиска
#     """
#     try:
#         # Создаем запрос
#         request = DataRequest(
#             report_type='svodka_pm',
#             get_params=request_data
#         )

#         # Получаем данные
#         result = report_service.get_data(request)

#         if result.success:
#             return {
#                 "success": True,
#                 "data": result.data
#             }
#         else:
#             raise HTTPException(status_code=404, detail=result.message)

#     except HTTPException:
#         raise
#     except Exception as e:
#         raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")


@app.post("/svodka_ca/upload", tags=[SvodkaCAParser.name],
          summary="Загрузка файла отчета сводки СА",
          response_model=UploadResponse,
          responses={
              400: {"model": UploadErrorResponse, "description": "Неверный формат файла"},
              500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
          },)
async def upload_svodka_ca(
    file: UploadFile = File(..., description="Excel файл сводки СА (.xlsx, .xlsm, .xls)")
):
    """
    Загрузка и обработка Excel файла отчета сводки СА

    **Поддерживаемые форматы:**
    - Excel (.xlsx, .xlsm, .xls)
    """
    report_service = get_report_service()

    try:
        # Проверяем тип файла
        if not file.filename.endswith(('.xlsx', '.xlsm', '.xls')):
            return JSONResponse(
                status_code=status.HTTP_400_BAD_REQUEST,
                content=UploadErrorResponse(
                    message="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls)",
                    error_code="INVALID_FILE_TYPE",
                    details={
                        "expected_formats": [".xlsx", ".xlsm", ".xls"],
                        "received_format": file.filename.split('.')[-1] if '.' in file.filename else "unknown"
                    }
                ).model_dump()
            )

        # Читаем содержимое файла
        file_content = await file.read()

        # Создаем запрос
        request = UploadRequest(
            report_type='svodka_ca',
            file_content=file_content,
            file_name=file.filename
        )

        # Загружаем отчет
        result = report_service.upload_report(request)

        if result.success:
            return UploadResponse(
                success=True,
                message=result.message,
                object_id=result.object_id
            )
        else:
            return JSONResponse(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                content=UploadErrorResponse(
                    message=result.message,
                    error_code="ERR_UPLOAD"
                ).model_dump(),
            )

    except HTTPException:
        raise
    except Exception as e:
        return JSONResponse(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            content=UploadErrorResponse(
                message=f"Внутренняя ошибка сервера: {str(e)}",
                error_code="INTERNAL_SERVER_ERROR"
            ).model_dump()
        )


@app.post("/svodka_ca/get_data", tags=[SvodkaCAParser.name],
          summary="Получение данных из отчета сводки СА")
async def get_svodka_ca_data(
    request_data: SvodkaCARequest
):
    """
    Получение данных из отчета сводки СА по указанным режимам и таблицам

    ### Структура параметров:
    - `modes`: **Массив кодов** режимов - `plan`, `fact` или `normativ` (обязательный)
    - `tables`: **Массив названий** таблиц как есть (обязательный)

    ### Пример тела запроса:
    ```json
    {
        "modes": ["plan", "fact"],
        "tables": ["ТиП, %", "Топливо итого, тонн", "Топливо итого, %", "Потери итого, тонн"]
    }
    ```
    """
    report_service = get_report_service()

    try:
        # Создаем запрос
        request_dict = request_data.model_dump()
        request = DataRequest(
            report_type='svodka_ca',
            get_params=request_dict
        )

        # Получаем данные
        result = report_service.get_data(request)

        if result.success:
            return {
                "success": True,
                "data": result.data
            }
        else:
            raise HTTPException(status_code=404, detail=result.message)

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")


# @app.post("/monitoring_fuel/upload", tags=[MonitoringFuelParser.name])
# async def upload_monitoring_fuel(
#     file: UploadFile = File(...),
#     directory_path: str = None
# ):
#     report_service = get_report_service()
#     """
#     Загрузка отчета мониторинга топлива

#     - file: Excel файл для загрузки (или архив с файлами)
#     - directory_path: Путь к директории с файлами (опционально)
#     """
#     try:
#         # Проверяем тип файла
#         if not file.filename.endswith(('.xlsx', '.xlsm', '.xls', '.zip')):
#             raise HTTPException(
#                 status_code=status.HTTP_400_BAD_REQUEST,
#                 detail="Поддерживаются только Excel файлы (.xlsx, .xlsm, .xls) или архивы (.zip)"
#             )

#         # Читаем содержимое файла
#         file_content = await file.read()

#         # Создаем параметры для парсинга
#         parse_params = {}
#         if directory_path:
#             parse_params['directory_path'] = directory_path

#         # Создаем запрос
#         request = UploadRequest(
#             report_type='monitoring_fuel',
#             file_content=file_content,
#             file_name=file.filename,
#             parse_params=parse_params
#         )

#         # Загружаем отчет
#         result = report_service.upload_report(request)

#         if result.success:
#             return {
#                 "success": True,
#                 "message": result.message,
#                 "object_id": result.object_id
#             }
#         else:
#             raise HTTPException(status_code=500, detail=result.message)

#     except HTTPException:
#         raise
#     except Exception as e:
#         raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")


# @app.post("/monitoring_fuel/get_data", tags=[MonitoringFuelParser.name])
# async def get_monitoring_fuel_data(
#     request_data: dict
# ):
#     report_service = get_report_service()
#     """
#     Получение данных из отчета мониторинга топлива

#     - column: Название колонки для агрегации (normativ, total, total_svod)
#     """
#     try:
#         # Создаем запрос
#         request = DataRequest(
#             report_type='monitoring_fuel',
#             get_params=request_data
#         )

#         # Получаем данные
#         result = report_service.get_data(request)

#         if result.success:
#             return {
#                 "success": True,
#                 "data": result.data
#             }
#         else:
#             raise HTTPException(status_code=404, detail=result.message)

#     except HTTPException:
#         raise
#     except Exception as e:
#         raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")


# @app.post("/monitoring_fuel/upload_directory", tags=[MonitoringFuelParser.name])
# async def upload_monitoring_fuel_directory(
#     request_data: dict
# ):
#     report_service = get_report_service()
#     """
#     Загрузка отчета мониторинга топлива из директории

#     - directory_path: Путь к директории с файлами monitoring_SNPZ_*.xlsm
#     """
#     try:
#         import os
#         import glob

#         # Извлекаем directory_path из request_data
#         directory_path = request_data.get('directory_path')
#         if not directory_path:
#             raise HTTPException(
#                 status_code=status.HTTP_400_BAD_REQUEST,
#                 detail="Параметр 'directory_path' обязателен"
#             )

#         # Проверяем существование директории
#         if not os.path.exists(directory_path):
#             raise HTTPException(
#                 status_code=status.HTTP_400_BAD_REQUEST,
#                 detail=f"Директория не найдена: {directory_path}"
#             )

#         # Проверяем наличие файлов
#         file_pattern = os.path.join(directory_path, "monitoring_SNPZ_*.xlsm")
#         files = glob.glob(file_pattern)

#         if not files:
#             raise HTTPException(
#                 status_code=status.HTTP_400_BAD_REQUEST,
#                 detail=f"Не найдены файлы по паттерну {file_pattern}"
#             )

#         # Создаем параметры для парсинга
#         parse_params = {
#             'directory_path': directory_path,
#             'sheet_name': 'Мониторинг потребления',
#             'search_value': 'Установка'
#         }

#         # Создаем запрос (используем пустой файл, так как парсим директорию)
#         request = UploadRequest(
#             report_type='monitoring_fuel',
#             file_content=b'',  # Пустой контент, так как парсим директорию
#             file_name='directory_upload',
#             parse_params=parse_params
#         )

#         # Загружаем отчет
#         result = report_service.upload_report(request)

#         if result.success:
#             return {
#                 "success": True,
#                 "message": result.message,
#                 "object_id": result.object_id,
#                 "files_processed": len(files)
#             }
#         else:
#             raise HTTPException(status_code=500, detail=result.message)

#     except HTTPException:
#         raise
#     except Exception as e:
#         raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")


@app.post("/monitoring_fuel/upload-zip", tags=[MonitoringFuelParser.name],
          summary="Загрузка файлов сводок мониторинга топлива одним ZIP-архивом",
          response_model=UploadResponse,
          responses={
            400: {"model": UploadErrorResponse, "description": "Неверный формат архива или файлов"},
            500: {"model": UploadErrorResponse, "description": "Внутренняя ошибка сервера"}
          },)
async def upload_monitoring_fuel_zip(
        zip_file: UploadFile = File(..., description="ZIP архив с Excel файлами (.zip)")
):
    """Загрузка файлов сводок мониторинга топлива по всем ОГ в **одном ZIP-архиве**

    **Шаблоны названий файлов:**
    - `monitoring_SNPZ_{MM}.xlsm`, `MM` - номер месяца с ведущим 0
    """
    report_service = get_report_service()
    try:
        if not zip_file.filename.lower().endswith('.zip'):
            return JSONResponse(
                status_code=status.HTTP_400_BAD_REQUEST,
                content=UploadErrorResponse(
                    message="Файл должен быть ZIP архивом",
                    error_code="INVALID_FILE_TYPE",
                    details={
                        "expected_formats": [".zip"],
                        "received_format": zip_file.filename.split('.')[-1] if '.' in zip_file.filename else "unknown"
                    }
                ).model_dump()
            )
        file_content = await zip_file.read()
        # Создаем запрос
        request = UploadRequest(
            report_type='monitoring_fuel',
            file_content=file_content,
            file_name=zip_file.filename
        )
        # Загружаем отчет
        result = report_service.upload_report(request)
        if result.success:
            return UploadResponse(
                success=True,
                message=result.message,
                object_id=result.object_id
            )
        else:
            return JSONResponse(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                content=UploadErrorResponse(
                    message=result.message,
                    error_code="ERR_UPLOAD"
                ).model_dump(),
            )

    except HTTPException:
        raise
    except Exception as e:
        return JSONResponse(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            content=UploadErrorResponse(
                message=f"Внутренняя ошибка сервера: {str(e)}",
                error_code="INTERNAL_SERVER_ERROR"
            ).model_dump()
        )


@app.post("/monitoring_fuel/get_total_by_columns", tags=[MonitoringFuelParser.name],
          summary="Получение данных по колонкам и расчёт средних значений")
async def get_monitoring_fuel_total_by_columns(
    request_data: MonitoringFuelTotalRequest
):
    """Получение данных из сводок мониторинга топлива по колонкам и расчёт средних значений

    ### Структура параметров:
    - `columns`: **Массив названий** выбираемых столбцов (обязательный)

    ### Пример тела запроса:
    ```json
    {
        "columns": ["total", "normativ"]
    }
    ```
    """
    report_service = get_report_service()

    try:
        # Создаем запрос
        request_dict = request_data.model_dump()
        request_dict['mode'] = 'total'
        request = DataRequest(
            report_type='monitoring_fuel',
            get_params=request_dict
        )

        # Получаем данные
        result = report_service.get_data(request)

        if result.success:
            return {
                "success": True,
                "data": result.data
            }
        else:
            raise HTTPException(status_code=404, detail=result.message)

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")


@app.post("/monitoring_fuel/get_month_by_code", tags=[MonitoringFuelParser.name],
          summary="Получение данных за месяц")
async def get_monitoring_fuel_month_by_code(
    request_data: MonitoringFuelMonthRequest
):
    """Получение данных из сводок мониторинга топлива за указанный номер месяца

    ### Структура параметров:
    - `month`: **Номер месяца строкой с ведущим 0** (обязательный)

    ### Пример тела запроса:
    ```json
    {
        "month": "02"
    }
    ```
    """
    report_service = get_report_service()

    try:
        # Создаем запрос
        request_dict = request_data.model_dump()
        request_dict['mode'] = 'month'
        request = DataRequest(
            report_type='monitoring_fuel',
            get_params=request_dict
        )

        # Получаем данные
        result = report_service.get_data(request)

        if result.success:
            return {
                "success": True,
                "data": result.data
            }
        else:
            raise HTTPException(status_code=404, detail=result.message)

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(status_code=500, detail=f"Внутренняя ошибка сервера: {str(e)}")


if __name__ == "__main__":
    uvicorn.run(app, host="0.0.0.0", port=8080)
