"""File API client helpers for uploading query attachments."""

from __future__ import annotations

import asyncio
import json
import mimetypes
from dataclasses import dataclass
from pathlib import Path
from typing import Any
from urllib import error, request
from uuid import uuid4


@dataclass
class LocalAttachment:
    """A local file to upload before sending a query."""

    path: str
    description: str = ""
    type: str = ""


def _guess_type(local_path: Path) -> str:
    guessed, _ = mimetypes.guess_type(str(local_path))
    return guessed or "file"


def _upload_attachment_sync(
    *,
    base_url: str,
    user_id: str,
    thread_id: str,
    attachment: LocalAttachment,
    token: str | None = None,
    debug: bool = False,
) -> dict[str, Any]:
    local_path = Path(attachment.path).expanduser().resolve()
    if not local_path.exists() or not local_path.is_file():
        raise FileNotFoundError(f"Attachment not found: {attachment.path}")

    endpoint = f"{base_url.rstrip('/')}/api/v2/upload_attachment"
    with local_path.open("rb") as file_handle:
        file_bytes = file_handle.read()

    guessed_type = attachment.type or _guess_type(local_path)

    # urllib does not support multipart form-data ergonomically, so this uses requests-like
    # semantics by manually building a boundary-safe payload.
    boundary = f"----agent-cli-upload-{uuid4().hex}"
    body = bytearray()

    def _append_field(name: str, value: str) -> None:
        body.extend(f"--{boundary}\r\n".encode("utf-8"))
        body.extend(
            f'Content-Disposition: form-data; name="{name}"\r\n\r\n{value}\r\n'.encode(
                "utf-8"
            )
        )

    def _append_file(name: str, filename: str, content: bytes, content_type: str) -> None:
        body.extend(f"--{boundary}\r\n".encode("utf-8"))
        body.extend(
            (
                f'Content-Disposition: form-data; name="{name}"; filename="{filename}"\r\n'
                f"Content-Type: {content_type}\r\n\r\n"
            ).encode("utf-8")
        )
        body.extend(content)
        body.extend(b"\r\n")

    _append_file("file", local_path.name, file_bytes, guessed_type)
    _append_field("thread_id", thread_id)
    _append_field("user_id", user_id)
    body.extend(f"--{boundary}--\r\n".encode("utf-8"))

    headers: dict[str, str] = {"Content-Type": f"multipart/form-data; boundary={boundary}"}
    if token:
        headers["Luckee-Authorization"] = token
        headers["x-user-token"] = token
    if user_id:
        headers["x-user-id"] = user_id

    req = request.Request(
        endpoint,
        data=bytes(body),
        headers=headers,
        method="POST",
    )
    try:
        with request.urlopen(req, timeout=30) as resp:
            raw = resp.read().decode("utf-8")
    except error.HTTPError as exc:
        details = exc.read().decode("utf-8", errors="replace")
        raise RuntimeError(f"Upload failed ({exc.code}): {details}") from exc
    except error.URLError as exc:
        raise RuntimeError(f"Upload failed: {exc.reason}") from exc

    if debug:
        print(f"[DEBUG][FILE_API][RESPONSE] {raw}")

    try:
        response_payload = json.loads(raw)
    except json.JSONDecodeError as exc:
        raise RuntimeError(f"Upload returned non-JSON response: {raw}") from exc

    relative_path = str(response_payload.get("relative_path") or "")
    if not relative_path:
        raise RuntimeError(f"Upload response missing relative_path: {response_payload}")

    return {
        "file_path": relative_path,
        "type": guessed_type,
        "description": attachment.description,
    }


async def upload_attachments(
    *,
    base_url: str,
    user_id: str,
    thread_id: str,
    attachments: list[LocalAttachment],
    token: str | None = None,
    debug: bool = False,
) -> list[dict[str, Any]]:
    """Upload local files and return websocket attachment payload entries."""
    uploaded: list[dict[str, Any]] = []
    for attachment in attachments:
        item = await asyncio.to_thread(
            _upload_attachment_sync,
            base_url=base_url,
            user_id=user_id,
            thread_id=thread_id,
            attachment=attachment,
            token=token,
            debug=debug,
        )
        uploaded.append(item)
    return uploaded

