import json
import mimetypes
import time
from pathlib import Path
from urllib import request
from urllib.parse import urljoin

from app.config import settings


class PaperlessClient:
    def __init__(self) -> None:
        self.base_url = settings.paperless_base_url.rstrip('/') + '/'
        self.token = settings.paperless_api_token

    def _req(self, method: str, path: str, payload: dict | None = None) -> dict:
        url = urljoin(self.base_url, path.lstrip('/'))
        body = None if payload is None else json.dumps(payload).encode('utf-8')
        headers = {
            'Authorization': f'Token {self.token}',
            'Accept': 'application/json',
        }
        if body is not None:
            headers['Content-Type'] = 'application/json'
        req = request.Request(url, data=body, headers=headers, method=method)
        with request.urlopen(req, timeout=120) as resp:
            raw = resp.read().decode('utf-8', errors='replace')
            return json.loads(raw) if raw else {}

    def get_document(self, document_id: int | str) -> dict:
        return self._req('GET', f'/api/documents/{document_id}/')

    def update_document(self, document_id: int | str, payload: dict) -> dict:
        return self._req('PATCH', f'/api/documents/{document_id}/', payload)

    def add_note(self, document_id: int | str, note: str) -> dict:
        return self._req('POST', f'/api/documents/{document_id}/notes/', {'note': note})

    def _list_endpoint(self, path: str) -> list[dict]:
        data = self._req('GET', path)
        if isinstance(data, list):
            return data
        if isinstance(data, dict):
            results = data.get('results')
            if isinstance(results, list):
                return results
        return []

    def list_tags(self) -> list[dict]:
        return self._list_endpoint('/api/tags/?page_size=200')

    def create_tag(self, name: str) -> dict:
        return self._req('POST', '/api/tags/', {'name': name})

    def get_or_create_tag(self, name: str) -> dict:
        needle = name.strip().casefold()
        for tag in self.list_tags():
            if str(tag.get('name', '')).strip().casefold() == needle:
                return tag
        return self.create_tag(name)

    def list_document_types(self) -> list[dict]:
        return self._list_endpoint('/api/document_types/?page_size=200')

    def list_correspondents(self) -> list[dict]:
        return self._list_endpoint('/api/correspondents/?page_size=200')

    def list_tasks(self, task_id: str | None = None) -> list[dict]:
        path = '/api/tasks/?page_size=100'
        if task_id:
            path += f'&task_id={task_id}'
        return self._list_endpoint(path)

    def wait_for_task(self, task_id: str, timeout_seconds: int = 90, poll_seconds: float = 1.0) -> dict:
        deadline = time.time() + timeout_seconds
        last = {}
        while time.time() < deadline:
            tasks = self.list_tasks(task_id=task_id)
            if tasks:
                last = tasks[0]
                status = str(last.get('status') or '').upper()
                if status in {'SUCCESS', 'FAILURE', 'CANCELLED'}:
                    return last
            time.sleep(poll_seconds)
        return last

    def upload_document(self, file_path: str, title: str) -> dict:
        path = Path(file_path)
        boundary = "----DorisSchoolhousePaperlessUpload"
        mime = mimetypes.guess_type(path.name)[0] or "application/octet-stream"
        parts: list[bytes] = []
        for key, value in {"title": title}.items():
            parts.extend([
                f"--{boundary}\r\n".encode(),
                f'Content-Disposition: form-data; name="{key}"\r\n\r\n'.encode(),
                str(value).encode(),
                b"\r\n",
            ])
        parts.extend([
            f"--{boundary}\r\n".encode(),
            f'Content-Disposition: form-data; name="document"; filename="{path.name}"\r\n'.encode(),
            f"Content-Type: {mime}\r\n\r\n".encode(),
            path.read_bytes(),
            b"\r\n",
            f"--{boundary}--\r\n".encode(),
        ])
        body = b"".join(parts)
        req = request.Request(
            urljoin(self.base_url, "/api/documents/post_document/"),
            data=body,
            headers={
                "Authorization": f"Token {self.token}",
                "Accept": "application/json",
                "Content-Type": f"multipart/form-data; boundary={boundary}",
            },
            method="POST",
        )
        with request.urlopen(req, timeout=120) as resp:
            raw = resp.read().decode("utf-8", errors="replace")
            if not raw:
                return {}
            parsed = json.loads(raw)
            if isinstance(parsed, str):
                return {"task_id": parsed}
            return parsed
