# coding: utf-8
import os
import shutil
import tempfile
import zipfile
from typing import Dict, List, Union, Any  # noqa: F401

import aiofiles
from aiofiles.os import listdir
from fastapi import (  # noqa: F401
    APIRouter,
    Body,
    Cookie,
    Depends,
    Form,
    Header,
    Path,
    Query,
    Response,
    Security,
    status,
    HTTPException,
    UploadFile,
)
from fastapi.responses import FileResponse
from starlette.background import BackgroundTasks
from typing_extensions import Annotated

from server.data.definitions import (
    API_SUB_PATH,
    ECP_WORK_FILES_DIR,
    Paths,
    ErrorTypes,
    Messages,
    Groups,
    WEBUI_WORK_FILES_DIR,
)
from server.models.error import Error
from server.models.extra_models import TokenModel  # noqa: F401
from server.models.file import File
from server.security_api import get_token_ApiKeyAuth, checkTokenCapabilities
from server.utils.files import deleteFile, buildArchive
from . import appData

router = APIRouter(prefix=API_SUB_PATH)

# Get from environment variable the value, otherwise it takes the default value
PATH_ECP_WORK_DIR = f"{os.environ.get(ECP_WORK_FILES_DIR, Paths.DEFAULT_ECP_WORK_DIR.value)}"
PATH_ECP_CUSTOM_GRAPHICS_DIR = f"{PATH_ECP_WORK_DIR}/{Paths.CUSTOM_GRAPHICS_SUBDIR.value}"

# Get from environment variable the value, otherwise it takes the default value
PATH_WEBUI_WORK_DIR = f"{os.environ.get(WEBUI_WORK_FILES_DIR, Paths.DEFAULT_WEBUI_WORK_DIR.value)}"
PATH_WEBUI_REAL_WORK_DIR = f"{PATH_WEBUI_WORK_DIR}/{Paths.WEBUI_WORK_SUBDIR.value}"

# Change the work directory for uploads
tempfile.tempdir = PATH_WEBUI_REAL_WORK_DIR


@router.delete(
    "/customgraphics/{customgraphicfile}",
    responses={
        200: {"model": File, "description": "Deleted the custom graphic file."},
        400: {"model": Error, "description": "Bad request."},
        401: {"model": Error, "description": "Unauthorized."},
        404: {"model": Error, "description": "File not found."},
    },
    tags=["customGraphics"],
    response_model_by_alias=True,
)
async def customgraphics_customgraphicfile_delete(
    customgraphicfile: str = Path(description="The name of the custom graphic file."),
    token_ApiKeyAuth: TokenModel = Security(get_token_ApiKeyAuth),
) -> File:
    """Delete a specific custom graphic file."""
    # ------------------------------------------- Check token capabilities -------------------------------------------

    isTokenGranted = await checkTokenCapabilities(token=token_ApiKeyAuth, group=Groups.EDIT_CUSTOM_GRAPHICS.value)

    # -------------------------------------------- Delete the file ------------------------------------------

    file = await deleteFile(path=PATH_ECP_CUSTOM_GRAPHICS_DIR, deleteFileName=customgraphicfile)

    if not file:
        errorResponse = Error(
            status=status.HTTP_404_NOT_FOUND,
            type=ErrorTypes.FILE_NOT_FOUND.value,
            message=Messages.FILE_NOT_FOUND.value,
        )
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=errorResponse.dict())

    return file


@router.get(
    "/customgraphics/{customgraphicfile}",
    responses={
        200: {"model": File, "description": "Gets the custom graphic file."},
        400: {"model": Error, "description": "Bad request."},
        401: {"model": Error, "description": "Unauthorized."},
        404: {"model": Error, "description": "File not found."},
    },
    tags=["customGraphics"],
    response_model_by_alias=True,
)
async def customgraphics_customgraphicfile_get(
    background_tasks: BackgroundTasks,
    customgraphicfile: str = Path(description="The name of the promotion screen file."),
    token_ApiKeyAuth: TokenModel = Security(get_token_ApiKeyAuth),
) -> FileResponse:
    """Get a specific custom graphic file."""
    # ------------------------------------------- Check token capabilities -------------------------------------------

    isTokenGranted = await checkTokenCapabilities(token=token_ApiKeyAuth, group=Groups.DOWNLOAD_CUSTOM_GRAPHICS.value)

    # -------------------------------------------- Get the searched file ------------------------------------------

    if not customgraphicfile in await listdir(PATH_ECP_CUSTOM_GRAPHICS_DIR):
        errorResponse = Error(
            status=status.HTTP_404_NOT_FOUND,
            type=ErrorTypes.FILE_NOT_FOUND.value,
            message=Messages.FILE_NOT_FOUND.value,
        )
        raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=errorResponse.dict())

    zipPath, zipName = await buildArchive(
        serial=appData.serial,
        ecpVersion=appData.ecpVersion,
        fileName=customgraphicfile.split(".")[0],
        filePaths=[PATH_ECP_CUSTOM_GRAPHICS_DIR],
        files=[customgraphicfile],
    )

    background_tasks.add_task(os.unlink, zipPath)

    return FileResponse(
        zipPath,
        media_type="application/x-zip-compressed",
        headers={"Content-Disposition": f'attachment; filename="{zipName}.zip"'},
    )


@router.get(
    "/customgraphics",
    responses={
        200: {"model": List[File], "description": "Gets all custom graphics files."},
        400: {"model": Error, "description": "Bad request."},
        401: {"model": Error, "description": "Unauthorized."},
    },
    tags=["customGraphics"],
    response_model_by_alias=True,
)
async def customgraphics_get(
    background_tasks: BackgroundTasks,
    onlyList: bool = Query(False, description="The boolean for activating only list mode."),
    token_ApiKeyAuth: TokenModel = Security(get_token_ApiKeyAuth),
) -> Union[List, Any]:
    """Get all the available custom graphics files."""
    # ------------------------------------------- Check token capabilities -------------------------------------------

    isTokenGranted = await checkTokenCapabilities(token=token_ApiKeyAuth, group=Groups.DOWNLOAD_CUSTOM_GRAPHICS.value)

    # ------------------------------------- return the content -------------------------------------
    files = await listdir(PATH_ECP_CUSTOM_GRAPHICS_DIR)
    if onlyList:
        return [{"id": file_name} for file_name in files]
    else:

        zipPath, zipName = await buildArchive(
            serial=appData.serial,
            ecpVersion=appData.ecpVersion,
            fileName="custom-graphics",
            filePaths=[PATH_ECP_CUSTOM_GRAPHICS_DIR],
            files=files,
        )

        background_tasks.add_task(os.unlink, zipPath)

        return FileResponse(
            zipPath,
            media_type="application/x-zip-compressed",
            headers={"Content-Disposition": f'attachment; filename="{zipName}.zip"'},
        )


@router.post(
    "/customgraphics",
    responses={
        201: {"model": List[File], "description": "Shows the created File."},
        400: {"model": Error, "description": "Bad request."},
        401: {"model": Error, "description": "Unauthorized."},
    },
    tags=["customGraphics"],
    response_model_by_alias=True,
)
async def customgraphics_post(
    file: Annotated[UploadFile, File()] = None, token_ApiKeyAuth: TokenModel = Security(get_token_ApiKeyAuth)
) -> List:
    """Save a new picture."""
    # ------------------------------------------- Check token capabilities -------------------------------------------

    isTokenGranted = await checkTokenCapabilities(token=token_ApiKeyAuth, group=Groups.UPLOAD_CUSTOM_GRAPHICS.value)

    # -------------------------------------------- Check fields aren't empty ------------------------------------------
    if file is None or file.filename == "":
        errorResponse = Error(
            status=status.HTTP_400_BAD_REQUEST,
            type=ErrorTypes.REQUEST_IS_EMPTY.value,
            message=Messages.REQUEST_IS_EMPTY.value,
        )
        raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=errorResponse.dict())

    # -------------------------------------- Save the files into persistent path --------------------------------------
    # Save the uploaded file to disk
    uploadedFile = os.path.join(Paths.DEFAULT_WEBUI_WORK_DIR.value, Paths.WEBUI_WORK_SUBDIR.value, file.filename)

    try:

        # Move spooled Upload file to persistent memory
        with open(uploadedFile, "wb") as f_destination:
            shutil.copyfileobj(file.file, f_destination)

        # Extract all files into definitive dir
        if zipfile.is_zipfile(uploadedFile):
            # Extract the contents of the zip archive to the upload directory
            with zipfile.ZipFile(uploadedFile, "r") as zipRef:
                zipRef.extractall(PATH_ECP_CUSTOM_GRAPHICS_DIR)

            # Remove the zip file
            await aiofiles.os.unlink(uploadedFile)

    except (FileNotFoundError, PermissionError, IOError, UnicodeDecodeError, OSError) as error:
        errorResponse = Error(
            status=status.HTTP_500_INTERNAL_SERVER_ERROR, type=ErrorTypes.EXCEPTION.value, message=error
        )
        raise HTTPException(status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, detail=errorResponse.dict())

    return [{file.filename: Messages.UPLOAD_SUCCESSFUL.value}]
