import os
from datetime import datetime, timezone
from typing import Optional
from uuid import UUID

from fastapi import HTTPException, status
from sqlalchemy import String, cast, func
from sqlalchemy.exc import IntegrityError
from sqlalchemy.orm import Session
from sqlalchemy.sql import over
from starlette.responses import StreamingResponse

from src.apps.export_data.models.export_data import DownloadFiles
from src.apps.export_data.schemas.export_data import (
    DownloadFilesCreateSchema,
    DownloadFilesOutputSchema,
    DownloadFilesUpdateSchema,
)
from src.apps.files.models.file import File
from src.core.config import settings
from src.core.exceptions import APIException
from src.utils.constants import API_PREFIXES, EXPORT_DATA_SUB_FOLDER
from src.utils.enums import (
    ExportDataTypes,
    ExportDataTypesFileName,
    ExportDataTypesHeaders,
)
from src.utils.helpers.functions import get_latest_file_by_prefix
from src.utils.pagination import QueryPaginator


async def create_download_file(
    db: Session,
    payload: DownloadFilesCreateSchema = DownloadFilesCreateSchema(),
) -> DownloadFilesOutputSchema:
    """
    Create a new download file entry in the database.
    Args:
        db (Session): Database session dependency.
        payload (DownloadFilesCreateSchema): Data for creating the download file.
    Returns:
        DownloadFilesOutputSchema: The created download file entry.
    Raises:
        APIException: If the file does not exist or if there is an error during creation.
    """
    try:
        uploaded_file = db.query(File).filter(File.id == payload.file_id).first()
        if not uploaded_file:
            raise APIException(
                module="create_download_file",
                error={"exception": str(e)},
                status_code=status.HTTP_404_NOT_FOUND,
                message="File not found.",
            )
    except Exception as e:
        raise APIException(
            module="create_download_file",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Failed to retrieve file.",
        )

    try:
        new_download_file = DownloadFiles(
            file_id=uploaded_file.id,
            file_type=payload.file_type,
        )
        db.add(new_download_file)
        db.commit()
        db.refresh(new_download_file)
        return DownloadFilesOutputSchema.from_orm(new_download_file)
    except Exception as e:
        raise APIException(
            module="create_download_file",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Failed to create download file.",
        )


async def get_all_latest_download_files(
    db: Session,
) -> dict:
    """
    For every type in ExportDataTypes, get the latest DownloadFile by created_at DESC.
    If a type has no file, value will be None.
    """
    try:
        # Iterate over all ExportDataTypes to get the latest file path for each type
        result = {}
        directory = os.path.join(os.getcwd(), EXPORT_DATA_SUB_FOLDER, "masterdata")
        os.makedirs(directory, exist_ok=True)
        for export_type in ExportDataTypes:
            file_prefix = ExportDataTypesFileName[export_type.name].value
            latest_file = get_latest_file_by_prefix(directory, file_prefix)
            if latest_file:
                result[export_type.name] = {
                    "file_path": os.path.relpath(latest_file, os.getcwd()),
                    "file_name": os.path.basename(latest_file),
                }
            else:
                result[export_type.name] = None

        return result

    except Exception as e:
        raise APIException(
            module="get_all_latest_download_files",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Failed to fetch latest download files.",
        )


async def get_all_latest_uploaded_files(
    db: Session,
) -> dict:
    """
    For every type in ExportDataTypes, get the latest DownloadFile by created_at DESC.
    If a type has no file, value will be None.
    """
    try:
        # Iterate over all ExportDataTypes to get the latest file path for each type
        result = {}
        directory = os.path.join(os.getcwd(), EXPORT_DATA_SUB_FOLDER, "uploaddata")
        os.makedirs(directory, exist_ok=True)

        for export_type in ExportDataTypes:
            file_prefix = ExportDataTypesFileName[export_type.name].value
            latest_file = get_latest_file_by_prefix(directory, file_prefix)
            if latest_file:
                result[export_type.name] = {
                    "file_path": os.path.relpath(latest_file, os.getcwd()),
                    "file_name": os.path.basename(latest_file),
                }
            else:
                result[export_type.name] = None

        return result

    except Exception as e:
        raise APIException(
            module="get_all_latest_uploaded_files",
            error={"exception": str(e)},
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            message="Failed to fetch latest uploaded files.",
        )
