"""
Supabase storage operations for Amina CLI.

Handles:
- Downloading result files from signed URLs (preferred, no creds needed)
- Downloading from Supabase directly (fallback, requires creds)
- Uploading input files to Supabase
- File path management
"""

import os
from pathlib import Path
from typing import Optional

import httpx


# Default bucket for user files
DEFAULT_BUCKET = "user-files"


class StorageError(Exception):
    """Raised when storage operations fail."""

    pass


def download_from_signed_url(
    signed_url: str,
    local_path: Path,
    timeout: float = 60.0,
) -> Path:
    """
    Download a file from a signed URL.

    This is the preferred method - no Supabase credentials needed.
    The signed URL was generated by the Modal worker.

    Args:
        signed_url: Time-limited signed URL from the API response
        local_path: Where to save the file locally

    Returns:
        Path to the downloaded file

    Raises:
        StorageError: If download fails
    """
    try:
        # Ensure parent directory exists
        local_path.parent.mkdir(parents=True, exist_ok=True)

        with httpx.Client(timeout=timeout, follow_redirects=True) as client:
            response = client.get(signed_url)
            response.raise_for_status()
            local_path.write_bytes(response.content)

        return local_path

    except httpx.HTTPStatusError as e:
        raise StorageError(f"Download failed (HTTP {e.response.status_code}): {e}")
    except Exception as e:
        raise StorageError(f"Download failed: {str(e)}")


def _get_supabase_client():
    """
    Get a Supabase client using environment variables.

    For CLI use, expects SUPABASE_URL and SUPABASE_ANON_KEY
    (or SUPABASE_KEY for backwards compatibility).

    Note: This is only needed for fallback direct access.
    Prefer using signed URLs which don't require credentials.

    Returns:
        Supabase client

    Raises:
        StorageError: If credentials not found
    """
    from supabase import create_client

    supabase_url = os.environ.get("SUPABASE_URL")
    supabase_key = os.environ.get("SUPABASE_ANON_KEY") or os.environ.get("SUPABASE_KEY")

    if not supabase_url or not supabase_key:
        raise StorageError(
            "Supabase credentials not found. Set SUPABASE_URL and SUPABASE_ANON_KEY "
            "environment variables, or use 'amina download' with a signed URL."
        )

    return create_client(supabase_url, supabase_key)


def download_file(
    remote_path: str,
    local_path: Optional[Path] = None,
    bucket: str = DEFAULT_BUCKET,
) -> Path:
    """
    Download a file from Supabase storage.

    Args:
        remote_path: Path in Supabase (e.g., "user/abc123/results/output.pdb")
        local_path: Local destination path (defaults to filename in current dir)
        bucket: Supabase storage bucket

    Returns:
        Path to downloaded file

    Raises:
        StorageError: If download fails
    """
    try:
        supabase = _get_supabase_client()

        # Default to filename in current directory
        if local_path is None:
            local_path = Path.cwd() / Path(remote_path).name

        # Ensure parent directory exists
        local_path.parent.mkdir(parents=True, exist_ok=True)

        # Download file
        response = supabase.storage.from_(bucket).download(remote_path)
        local_path.write_bytes(response)

        return local_path

    except Exception as e:
        raise StorageError(f"Failed to download '{remote_path}': {str(e)}")


def download_results(
    result: dict,
    output_dir: Optional[Path] = None,
    bucket: str = DEFAULT_BUCKET,
) -> list[Path]:
    """
    Download all output files from a tool result.

    Prefers signed URLs (no credentials needed) over direct Supabase access.
    The API response includes 'signed_urls' dict when available.

    Args:
        result: Tool execution result with 'output_files' and optional 'signed_urls'
        output_dir: Directory to save files (defaults to current dir)
        bucket: Supabase storage bucket (only used as fallback)

    Returns:
        List of downloaded file paths

    Raises:
        StorageError: If any download fails
    """
    output_files = result.get("output_files", {})
    signed_urls = result.get("signed_urls", {})

    if not output_files and not signed_urls:
        return []

    if output_dir is None:
        output_dir = Path.cwd()

    output_dir.mkdir(parents=True, exist_ok=True)

    downloaded = []

    # Prefer signed URLs (no credentials needed)
    if signed_urls:
        for file_type, signed_url in signed_urls.items():
            if not signed_url:
                continue

            # Get filename from output_files path, or construct from file_type
            remote_path = output_files.get(file_type, "")
            if remote_path:
                filename = Path(remote_path).name
            else:
                # Fallback: extract from signed URL or use file_type
                filename = f"{file_type.replace('_filepath', '')}.out"

            local_path = output_dir / filename
            download_from_signed_url(signed_url, local_path)
            downloaded.append(local_path)

        return downloaded

    # No signed URLs available - direct Supabase access requires credentials
    # Check if we have credentials before attempting
    supabase_url = os.environ.get("SUPABASE_URL")
    supabase_key = os.environ.get("SUPABASE_ANON_KEY") or os.environ.get("SUPABASE_KEY")

    if not supabase_url or not supabase_key:
        # Provide a clear error message without exposing internal details
        raise StorageError("No signed URLs provided by the tool. This may be a tool configuration issue.")

    # Fallback: direct Supabase access (requires credentials)
    for file_type, remote_path in output_files.items():
        if not remote_path:
            continue

        filename = Path(remote_path).name
        local_path = output_dir / filename
        download_file(remote_path, local_path, bucket)
        downloaded.append(local_path)

    return downloaded


def upload_file(
    local_path: Path,
    remote_path: str,
    bucket: str = DEFAULT_BUCKET,
) -> str:
    """
    Upload a local file to Supabase storage.

    Args:
        local_path: Path to local file
        remote_path: Destination path in Supabase
        bucket: Supabase storage bucket

    Returns:
        The remote path where file was uploaded

    Raises:
        StorageError: If upload fails
    """
    try:
        if not local_path.exists():
            raise StorageError(f"Local file not found: {local_path}")

        supabase = _get_supabase_client()

        with open(local_path, "rb") as f:
            supabase.storage.from_(bucket).upload(
                path=remote_path,
                file=f,
                file_options={"upsert": "true"},
            )

        return remote_path

    except Exception as e:
        raise StorageError(f"Failed to upload '{local_path}': {str(e)}")


def get_signed_url(
    remote_path: str,
    expires_in: int = 3600,
    bucket: str = DEFAULT_BUCKET,
) -> str:
    """
    Get a signed URL for a file.

    This allows downloading without Supabase credentials.

    Args:
        remote_path: Path in Supabase
        expires_in: URL expiration time in seconds (default 1 hour)
        bucket: Supabase storage bucket

    Returns:
        Signed URL for the file
    """
    try:
        supabase = _get_supabase_client()
        result = supabase.storage.from_(bucket).create_signed_url(remote_path, expires_in)
        return result["signedURL"]
    except Exception as e:
        raise StorageError(f"Failed to get signed URL for '{remote_path}': {str(e)}")


def list_files(
    path: str,
    bucket: str = DEFAULT_BUCKET,
) -> list[dict]:
    """
    List files in a Supabase storage path.

    Args:
        path: Directory path in Supabase
        bucket: Supabase storage bucket

    Returns:
        List of file metadata dicts
    """
    try:
        supabase = _get_supabase_client()
        result = supabase.storage.from_(bucket).list(path)
        return result
    except Exception as e:
        raise StorageError(f"Failed to list files at '{path}': {str(e)}")
