from __future__ import annotations

import base64
import mimetypes
from pathlib import Path
from urllib.parse import quote

import httpx

def content_type(path: Path) -> str:
  return mimetypes.guess_type(path.name)[0] or 'application/octet-stream'

def upload(client, node: str, path: Path, remote_name: str, note: str, expires: str, remote_path: str = '') -> dict:
  content_b64 = base64.b64encode(path.read_bytes()).decode('ascii')
  params = {
    'file_name': remote_name,
    'content_b64': content_b64,
    'content_type': content_type(path),
    'note': note,
  }
  if remote_path:
    params['file_path'] = remote_path
  if expires:
    params['expires'] = expires
  result = client.call(node, 'files.upload', params)
  return result if isinstance(result, dict) else {'result': result}

def update_remote(client, node: str, file_id: str, file_name: str, file_path: str, note: str | None, expires: str) -> None:
  params = {
    'file_id': file_id,
    'file_name': file_name,
    'file_path': file_path,
  }
  if note is not None:
    params['note'] = note
  if expires:
    params['expires'] = expires
  client.call(node, 'files.update', params)

def delete_remote(client, node: str, file_id: str) -> None:
  client.call(node, 'files.delete', {'file_id': file_id})

def list_remote(client, node: str) -> list[dict]:
  result = client.call(node, 'files.list', {})
  files = result.get('files', []) if isinstance(result, dict) else []
  return files if isinstance(files, list) else []

def node_url(client, node: str, path: str) -> str:
  base = client.registry.get(node)
  if not base:
    raise RuntimeError(f'unknown node: {node}')
  return f"{base.rstrip('/')}{path}"

def download_url(client, node: str, file_id: str) -> str:
  return node_url(client, node, f"/api/files/{quote(file_id, safe='')}/download")

def events_url(client, node: str) -> str:
  return node_url(client, node, '/api/files/events')

def download(client, node: str, file_id: str, path: Path) -> None:
  path.parent.mkdir(parents=True, exist_ok=True)
  headers = {'Accept': '*/*', **client.auth_headers()}
  with httpx.Client(timeout=client.timeout) as http:
    with http.stream('GET', download_url(client, node, file_id), headers=headers) as response:
      if response.status_code == 404:
        raise FileNotFoundError(file_id)
      if response.status_code == 410:
        raise RuntimeError(f'remote file expired: {file_id}')
      response.raise_for_status()
      tmp = path.with_name(f'{path.name}.{Path.cwd().stat().st_ino}.tmp')
      with tmp.open('wb') as handle:
        for chunk in response.iter_bytes():
          handle.write(chunk)
      tmp.replace(path)
