from typing import Optional, Union

import filetype
import requests

from airplane.api.client import api_client_from_env
from airplane.types import File


def upload(payload: Union[bytes, str], file_name: Optional[str] = None) -> File:
    """Upload a payload to Airplane and create an Airplane File object.

    Args:
        payload: Content of the file to upload.
        file_name: Name of the file to create. If not provided, the filename will
            be generated by trying to infer the file type from the payload. If
            the file type cannot be inferred, an error will be raised.

    Returns:
        An Airplane File object.

    Raises:
        HTTPError: If the file could not be uploaded.
        ValueError: If the file type could not be inferred and no filename was
            provided.
    """
    if isinstance(payload, str):
        payload = payload.encode("utf-8")
        if file_name is None:
            file_name = "file.txt"
    else:
        if file_name is None:
            kind = filetype.guess(payload)
            if kind is None:
                raise ValueError(
                    "Unable to determine file type, please specify a filename"
                )
            file_name = f"file.{kind.extension}"
    upload_response = api_client_from_env().create_upload(file_name, len(payload))

    try:
        response = requests.put(
            upload_response["writeOnlyURL"],
            data=payload,
            headers={"X-Goog-Content-Length-Range": f"0,{len(payload)}"},
            timeout=600,
        )
        response.raise_for_status()
    except requests.HTTPError as err:
        raise requests.HTTPError(
            f"Failed to upload file: {err.response.text}", response=err.response
        ) from err

    return File(
        id=upload_response["upload"]["id"],
        url=upload_response["readOnlyURL"],
    )
