from __future__ import annotations

from fastapi import APIRouter, Depends, File, Form, Query, Request, UploadFile
from fastapi.responses import StreamingResponse
from starlette.background import BackgroundTask

from backend.app.api.schemas import ArchivePrepareRequest, DeleteRequest, FileInfoResponse, MkdirRequest, MkdirResponse, RenameRequest, RenameResponse, SaveRequest, SaveResponse, TaskCreateResponse, TaskDetailResponse, UploadResponse, ViewResponse
from backend.app.dependencies import get_archive_download_task_service, get_delete_task_service, get_file_ops_service, get_remote_file_service
from backend.app.services.archive_download_task_service import ArchiveDownloadTaskService
from backend.app.services.delete_task_service import DeleteTaskService
from backend.app.services.file_ops_service import FileOpsService
from backend.app.services.remote_file_service import RemoteFileService

router = APIRouter(prefix="/files")


@router.post("/mkdir", response_model=MkdirResponse)
async def mkdir(
    request: MkdirRequest,
    service: FileOpsService = Depends(get_file_ops_service),
) -> MkdirResponse:
    return service.mkdir(parent_path=request.parent_path, name=request.name)


@router.post("/rename", response_model=RenameResponse)
async def rename(
    request: RenameRequest,
    service: FileOpsService = Depends(get_file_ops_service),
) -> RenameResponse:
    return service.rename(path=request.path, new_name=request.new_name)


@router.post("/delete", response_model=TaskCreateResponse, status_code=202)
async def delete(
    request: DeleteRequest,
    service: DeleteTaskService = Depends(get_delete_task_service),
) -> TaskCreateResponse:
    if request.paths is not None:
        return service.create_batch_delete_task(paths=request.paths, recursive_paths=request.recursive_paths or [])
    return service.create_delete_task(path=request.path, recursive=request.recursive)


@router.post("/upload", response_model=UploadResponse)
async def upload(
    target_path: str = Form(...),
    overwrite: bool = Form(False),
    file: UploadFile = File(...),
    service: FileOpsService = Depends(get_file_ops_service),
) -> UploadResponse:
    return service.upload(target_path=target_path, upload_file=file, overwrite=overwrite)


@router.get("/view", response_model=ViewResponse)
async def view(
    path: str,
    for_edit: bool = False,
    service: FileOpsService = Depends(get_file_ops_service),
    remote_service: RemoteFileService = Depends(get_remote_file_service),
) -> ViewResponse:
    if remote_service.handles_path(path):
        return remote_service.view(path=path, for_edit=for_edit)
    return service.view(path=path, for_edit=for_edit)


@router.get("/info", response_model=FileInfoResponse)
async def info(
    path: str,
    service: FileOpsService = Depends(get_file_ops_service),
    remote_service: RemoteFileService = Depends(get_remote_file_service),
) -> FileInfoResponse:
    if remote_service.handles_path(path):
        return remote_service.info(path=path)
    return service.info(path=path)


@router.get("/download")
async def download(
    path: list[str] = Query(...),
    service: FileOpsService = Depends(get_file_ops_service),
    remote_service: RemoteFileService = Depends(get_remote_file_service),
) -> StreamingResponse:
    prepared = remote_service.prepare_download(paths=path) if any(remote_service.handles_path(item) for item in path) else service.prepare_download(paths=path)
    response = StreamingResponse(
        prepared["content"],
        headers=prepared["headers"],
        media_type=prepared["content_type"],
    )
    if prepared.get("cleanup"):
        response.background = BackgroundTask(prepared["cleanup"])
    return response


@router.post("/download/archive-prepare", response_model=TaskCreateResponse, status_code=202)
async def archive_prepare(
    request: ArchivePrepareRequest,
    service: ArchiveDownloadTaskService = Depends(get_archive_download_task_service),
) -> TaskCreateResponse:
    return service.create_archive_prepare_task(paths=request.paths)


@router.get("/download/archive/{task_id}")
async def archive_download(
    task_id: str,
    service: ArchiveDownloadTaskService = Depends(get_archive_download_task_service),
) -> StreamingResponse:
    prepared = service.prepare_ready_archive_download(task_id=task_id)
    return StreamingResponse(
        prepared["content"],
        headers=prepared["headers"],
        media_type=prepared["content_type"],
    )


@router.post("/download/archive/{task_id}/cancel", response_model=TaskDetailResponse)
async def archive_cancel(
    task_id: str,
    service: ArchiveDownloadTaskService = Depends(get_archive_download_task_service),
) -> TaskDetailResponse:
    return TaskDetailResponse(**service.cancel_archive_prepare_task(task_id=task_id))


@router.get("/video")
async def video(
    path: str,
    request: Request,
    service: FileOpsService = Depends(get_file_ops_service),
) -> StreamingResponse:
    prepared = service.prepare_video_stream(path=path, range_header=request.headers.get("range"))
    return StreamingResponse(
        prepared["content"],
        status_code=prepared["status_code"],
        headers=prepared["headers"],
        media_type=prepared["content_type"],
    )


@router.get("/pdf")
async def pdf(
    path: str,
    service: FileOpsService = Depends(get_file_ops_service),
) -> StreamingResponse:
    prepared = service.prepare_pdf_stream(path=path)
    return StreamingResponse(
        prepared["content"],
        headers=prepared["headers"],
        media_type=prepared["content_type"],
    )


@router.get("/image")
async def image(
    path: str,
    service: FileOpsService = Depends(get_file_ops_service),
    remote_service: RemoteFileService = Depends(get_remote_file_service),
) -> StreamingResponse:
    if remote_service.handles_path(path):
        prepared = remote_service.prepare_image_stream(path=path)
        return StreamingResponse(
            prepared["content"],
            headers=prepared["headers"],
            media_type=prepared["content_type"],
        )
    prepared = service.prepare_image_stream(path=path)
    return StreamingResponse(
        prepared["content"],
        headers=prepared["headers"],
        media_type=prepared["content_type"],
    )


@router.get("/thumbnail")
async def thumbnail(
    path: str,
    service: FileOpsService = Depends(get_file_ops_service),
) -> StreamingResponse:
    prepared = service.prepare_thumbnail_stream(path=path)
    return StreamingResponse(
        prepared["content"],
        headers=prepared["headers"],
        media_type=prepared["content_type"],
    )


@router.post("/save", response_model=SaveResponse)
async def save(
    request: SaveRequest,
    service: FileOpsService = Depends(get_file_ops_service),
) -> SaveResponse:
    return service.save(
        path=request.path,
        content=request.content,
        expected_modified=request.expected_modified,
    )
