"""
Storage API endpoints for pre-signed URLs and direct uploads
"""

from fastapi import APIRouter, Depends, HTTPException, UploadFile, File, Form, Security
from fastapi.responses import FileResponse, StreamingResponse
from fastapi.security import HTTPAuthorizationCredentials
from typing import Optional, Dict
from uuid import UUID, uuid4
import json
import base64
import time
import io
import os
from pathlib import Path

from ..auth import get_current_account
from ..auth_core import bearer_scheme
from ..storage import (
    get_storage_backend,
    parse_signed_storage_token,
)

router = APIRouter(prefix="/api/v1/storage", tags=["Storage"])


@router.post("/generate-upload-url")
async def generate_upload_url(
    filename: str,
    content_type: Optional[str] = "application/octet-stream",
    expires_in: int = 3600,
    account_id: UUID = Depends(get_current_account),
) -> Dict:
    """
    Generate a pre-signed URL for direct file upload.

    For S3: Returns a pre-signed POST URL with fields
    For Local: Returns an upload endpoint with token

    Frontend Usage:
    ```javascript
    // Get upload URL
    const response = await fetch('/api/v1/storage/generate-upload-url', {
        method: 'POST',
        body: JSON.stringify({
            filename: 'data.csv',
            content_type: 'text/csv'
        })
    });
    const uploadData = await response.json();

    // For S3 upload
    const formData = new FormData();
    Object.entries(uploadData.fields).forEach(([key, value]) => {
        formData.append(key, value);
    });
    formData.append('file', fileBlob);

    await fetch(uploadData.url, {
        method: 'POST',
        body: formData
    });
    ```
    """
    try:
        storage = get_storage_backend()

        # Generate unique key for the file
        key = f"uploads/{account_id}/{uuid4()}/{filename}"

        # Generate pre-signed upload URL
        upload_info = await storage.generate_upload_url(
            key=key, expires_in=expires_in, content_type=content_type
        )

        # Add the final key to response for reference
        upload_info["key"] = key
        upload_info["filename"] = filename

        return upload_info

    except Exception as e:
        raise HTTPException(500, f"Failed to generate upload URL: {str(e)}")


@router.post("/generate-download-url")
async def generate_download_url(
    key: str, expires_in: int = 3600, account_id: UUID = Depends(get_current_account)
) -> Dict:
    """
    Generate a pre-signed URL for file download.

    Returns a time-limited URL that can be used to download the file directly.
    """
    try:
        storage = get_storage_backend()

        # Check if file exists
        if not await storage.exists(key):
            raise HTTPException(404, f"File not found: {key}")

        # Get file size for metadata
        file_size = await storage.get_file_size(key)

        # Generate pre-signed download URL
        download_url = await storage.generate_presigned_url(
            key=key, expires_in=expires_in, operation="get_object"
        )

        return {
            "url": download_url,
            "key": key,
            "size": file_size,
            "expires_in": expires_in,
        }

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(500, f"Failed to generate download URL: {str(e)}")


@router.post("/upload")
async def upload_file_direct(
    file: UploadFile = File(...),
    key: Optional[str] = Form(None),
    token: Optional[str] = Form(None),
    account_id: UUID = Depends(get_current_account),
) -> Dict:
    """
    Direct file upload endpoint (mainly for local storage backend).
    For production S3, use pre-signed POST URLs instead.
    """
    try:
        storage = get_storage_backend()

        # Validate token if provided (for local storage security)
        if token:
            try:
                token_data = json.loads(base64.urlsafe_b64decode(token))
                if token_data.get("expires", 0) < time.time():
                    raise HTTPException(401, "Upload token expired")
                if key and token_data.get("key") != key:
                    raise HTTPException(401, "Invalid upload token")
            except (json.JSONDecodeError, KeyError):
                raise HTTPException(401, "Invalid upload token")

        # Generate key if not provided
        if not key:
            key = f"uploads/{account_id}/{uuid4()}/{file.filename}"

        # Save file
        file_url = await storage.save(file, key)

        return {
            "key": key,
            "url": file_url,
            "size": file.size,
            "content_type": file.content_type,
            "filename": file.filename,
        }

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(500, f"Failed to upload file: {str(e)}")


@router.get("/download/{key:path}")
async def download_file_direct(
    key: str,
    token: Optional[str] = None,
    credentials: Optional[HTTPAuthorizationCredentials] = Security(bearer_scheme),
):
    """
    Direct file download endpoint (mainly for local storage backend).
    For production S3, use pre-signed GET URLs instead.
    """
    import logging

    log = logging.getLogger(__name__)
    log.info(f"[DOWNLOAD] Requested key: {key}")

    try:
        storage = get_storage_backend()

        # Validate token if provided
        if token:
            token_data = None
            try:
                token_data = parse_signed_storage_token(token)
            except ValueError:
                await get_current_account(credentials)
                try:
                    padding = "=" * (-len(token) % 4)
                    token_data = json.loads(
                        base64.urlsafe_b64decode(f"{token}{padding}".encode("utf-8"))
                    )
                    if not isinstance(token_data, dict):
                        token_data = None
                except (ValueError, json.JSONDecodeError, TypeError):
                    token_data = None
                if token_data is None:
                    raise HTTPException(401, "Invalid download token")

            if token_data.get("expires", 0) < time.time():
                raise HTTPException(401, "Download token expired")
            if token_data.get("key") != key:
                raise HTTPException(401, "Invalid download token")
            operation = token_data.get("operation")
            if operation and operation != "get_object":
                raise HTTPException(401, "Invalid download token")
        else:
            await get_current_account(credentials)

        # Check if file exists
        if not await storage.exists(key):
            log.error(f"[DOWNLOAD] File does not exist: {key}")
            raise HTTPException(404, f"File not found: {key}")

        # For local storage, return file directly
        if hasattr(storage, "base_path"):
            # Use the storage backend's path resolution method
            file_path = storage._resolve_path(key)
            log.info(f"[DOWNLOAD] Resolved path: {file_path}")

            # Double-check the file exists at the resolved path
            if not os.path.exists(file_path):
                log.error(f"[DOWNLOAD] File not found at resolved path: {file_path}")
                raise HTTPException(404, f"File not found at path: {file_path}")

            return FileResponse(
                path=file_path,
                filename=Path(key).name,
                media_type="application/octet-stream",
            )

        # For S3, stream the content
        content = await storage.read(key)

        return StreamingResponse(
            io.BytesIO(content),
            media_type="application/octet-stream",
            headers={"Content-Disposition": f"attachment; filename={Path(key).name}"},
        )

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(500, f"Failed to download file: {str(e)}")


@router.delete("/{key:path}")
async def delete_file(
    key: str, account_id: UUID = Depends(get_current_account)
) -> Dict:
    """
    Delete a file from storage.
    """
    try:
        storage = get_storage_backend()

        # Check if file exists
        if not await storage.exists(key):
            raise HTTPException(404, f"File not found: {key}")

        # Delete file
        success = await storage.delete(key)

        return {"deleted": success, "key": key}

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(500, f"Failed to delete file: {str(e)}")


@router.get("/info/{key:path}")
async def get_file_info(
    key: str, account_id: UUID = Depends(get_current_account)
) -> Dict:
    """
    Get information about a stored file.
    """
    try:
        storage = get_storage_backend()

        # Check if file exists
        if not await storage.exists(key):
            raise HTTPException(404, f"File not found: {key}")

        # Get file metadata
        file_size = await storage.get_file_size(key)

        return {
            "key": key,
            "size": file_size,
            "filename": Path(key).name,
            "exists": True,
        }

    except HTTPException:
        raise
    except Exception as e:
        raise HTTPException(500, f"Failed to get file info: {str(e)}")
