"""Storage API views — file upload, download URL and metadata."""
from __future__ import annotations

from rest_framework import status
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
from rest_framework.request import Request
from rest_framework.response import Response

from simorgh.apps.storage.models import FileMetadata, FileUploadStatus
from simorgh.apps.storage.providers import StorageError, get_default_provider
from simorgh.apps.storage.services import store_file


def _serialize_file(f: FileMetadata) -> dict:
    return {
        "id": f.pk,
        "public_id": str(f.public_id),
        "filename": f.filename,
        "content_type": f.content_type,
        "size_bytes": f.size_bytes,
        "checksum_sha256": f.checksum_sha256,
        "upload_status": f.upload_status,
        "virus_scan_status": f.virus_scan_status,
        "processing_status": f.processing_status,
        "uploaded_by_id": f.uploaded_by_id,
        "app_context": f.app_context,
        "created_at": f.created_at.isoformat(),
    }


@api_view(["POST"])
@permission_classes([IsAuthenticated])
def storage_upload(request: Request) -> Response:
    """POST /api/v1/storage/upload/

    Accepts a multipart/form-data request with a single ``file`` field.
    Optional form fields:
      app_context — e.g. "helpdesk", "chat"

    Returns the created FileMetadata record.
    """
    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    file_obj = request.FILES.get("file")
    if file_obj is None:
        return Response({"error": {"code": "missing_file", "message": "No file provided."}},
                        status=status.HTTP_400_BAD_REQUEST)

    app_context = str(request.data.get("app_context", "")).strip()

    # Resolve an org_node_id from the request context; required by store_file.
    from simorgh.apps.organizations.models import OrganizationNode
    org_node_id_raw = request.data.get("organization_node_id")
    org_node_id: int | None = None
    if org_node_id_raw:
        try:
            org_node_id = int(org_node_id_raw)
            OrganizationNode.objects.get(pk=org_node_id, tenant=tenant)
        except (ValueError, OrganizationNode.DoesNotExist):
            return Response({"error": {"code": "invalid_org_node", "message": "Organization node not found."}},
                            status=status.HTTP_400_BAD_REQUEST)

    try:
        file_meta = store_file(
            filename=file_obj.name,
            content=file_obj,
            content_type=file_obj.content_type or "",
            tenant_id=tenant.pk,
            organization_node_id=org_node_id,
            uploaded_by_id=request.user.pk,
            app_context=app_context,
        )
    except StorageError as exc:
        return Response({"error": {"code": "storage_error", "message": str(exc)}},
                        status=status.HTTP_400_BAD_REQUEST)

    return Response(_serialize_file(file_meta), status=status.HTTP_201_CREATED)


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def storage_file_detail(request: Request, pk: int) -> Response:
    """GET /api/v1/storage/files/<pk>/

    Returns file metadata. The caller needs to be the uploader or staff.
    """
    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        f = FileMetadata.objects.get(pk=pk, tenant=tenant)
    except FileMetadata.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    if not request.user.is_staff and f.uploaded_by_id != request.user.pk:
        return Response({"detail": "Forbidden."}, status=status.HTTP_403_FORBIDDEN)

    return Response(_serialize_file(f))


@api_view(["GET"])
@permission_classes([IsAuthenticated])
def storage_file_download_url(request: Request, pk: int) -> Response:
    """GET /api/v1/storage/files/<pk>/download-url/

    Returns a short-lived download URL for the file.
    The caller must be the uploader or staff.
    """
    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        f = FileMetadata.objects.get(pk=pk, tenant=tenant, upload_status=FileUploadStatus.READY)
    except FileMetadata.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    if not request.user.is_staff and f.uploaded_by_id != request.user.pk:
        return Response({"detail": "Forbidden."}, status=status.HTTP_403_FORBIDDEN)

    provider = get_default_provider()
    try:
        url = provider.url(f.path)
    except Exception:
        return Response({"error": {"code": "url_error", "message": "Could not generate download URL."}},
                        status=status.HTTP_500_INTERNAL_SERVER_ERROR)

    return Response({"url": url, "filename": f.filename})


@api_view(["DELETE"])
@permission_classes([IsAuthenticated])
def storage_file_delete(request: Request, pk: int) -> Response:
    """DELETE /api/v1/storage/files/<pk>/

    Soft-deletes the FileMetadata record. The actual blob is kept for safety.
    Only the uploader or staff can delete.
    """
    tenant = getattr(request, "tenant", None)
    if tenant is None:
        return Response({"detail": "Tenant not found."}, status=status.HTTP_404_NOT_FOUND)

    try:
        f = FileMetadata.objects.get(pk=pk, tenant=tenant)
    except FileMetadata.DoesNotExist:
        return Response({"detail": "Not found."}, status=status.HTTP_404_NOT_FOUND)

    if not request.user.is_staff and f.uploaded_by_id != request.user.pk:
        return Response({"detail": "Forbidden."}, status=status.HTTP_403_FORBIDDEN)

    f.delete()  # SoftDeleteModel.delete()
    return Response(status=status.HTTP_204_NO_CONTENT)
