from __future__ import annotations

import urllib.error
import urllib.request
from pathlib import Path
from typing import Any
from urllib.parse import quote

from niannian.storage.cloud_base import FileCloudBackend, HttpOpener, LiveCloudBackend
from niannian.storage.quark_api import (
    DEFAULT_CHUNK_SIZE_BYTES,
    DEFAULT_MAX_UPLOAD_BYTES,
    DEFAULT_QUARK_API_BASE,
    QuarkDriveClient,
)
QUARK_BROWSER_UA = (
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) "
    "AppleWebKit/537.36 (KHTML, like Gecko) Chrome/134.0.0.0 Safari/537.36"
)


def looks_like_quark_cookie(token: str | None) -> bool:
    if not token:
        return False
    return "__pus=" in token or (token.count(";") >= 2 and "=" in token)


class QuarkLiveBackend(LiveCloudBackend):
    """夸克 L1 live — Bearer refresh_token 或浏览器 Cookie。"""

    def __init__(
        self,
        mirror_root: Path,
        *,
        refresh_token: str | None,
        api_base: str | None = None,
        timeout: float = 30.0,
        http_opener: HttpOpener | None = None,
        credential_manager: Any | None = None,
        auth_kind: str = "bearer",
        remote_root: str | None = None,
        chunk_size_bytes: int | None = None,
        max_upload_bytes: int | None = None,
    ) -> None:
        kind = auth_kind if auth_kind in ("cookie", "bearer") else "bearer"
        if kind == "bearer" and looks_like_quark_cookie(refresh_token):
            kind = "cookie"
        self._auth_kind = kind
        self._drive_client: QuarkDriveClient | None = None
        if kind == "cookie" and refresh_token:
            state_path = mirror_root.parent.parent / "sync" / "quark_fids.json"
            self._drive_client = QuarkDriveClient(
                refresh_token,
                remote_root=remote_root,
                api_base=api_base or DEFAULT_QUARK_API_BASE,
                timeout=timeout,
                http_opener=http_opener,
                state_path=state_path,
                credential_manager=credential_manager,
                chunk_size_bytes=chunk_size_bytes or DEFAULT_CHUNK_SIZE_BYTES,
                max_upload_bytes=max_upload_bytes or DEFAULT_MAX_UPLOAD_BYTES,
            )
        super().__init__(
            "quark",
            mirror_root,
            token=refresh_token,
            api_base=api_base or DEFAULT_QUARK_API_BASE,
            timeout=timeout,
            http_opener=http_opener,
            credential_manager=credential_manager,
            provider="quark",
            remote_root=remote_root,
        )

    def _live_headers(self, *, with_content_type: bool = False) -> dict[str, str]:
        if self._auth_kind == "cookie":
            headers = {
                "Cookie": self.token or "",
                "User-Agent": QUARK_BROWSER_UA,
                "Accept": "application/json, text/plain, */*",
            }
        else:
            headers = {
                "Authorization": f"Bearer {self.token}",
                "User-Agent": "niannian/0.2.0",
            }
        if with_content_type:
            headers["Content-Type"] = "application/octet-stream"
        return headers

    def _auth_headers(self) -> dict[str, str]:
        return self._live_headers(with_content_type=True)

    def _sync_cookie_from_client(self) -> None:
        if self._drive_client is None:
            return
        new_cookie = self._drive_client.cookie
        if new_cookie and new_cookie != self.token:
            self.token = new_cookie

    def _remote_put(self, rel_path: str, data: bytes) -> None:
        if self._drive_client is not None:
            self._drive_client.upload_object(rel_path, data)
            self._sync_cookie_from_client()
            return
        req = urllib.request.Request(
            self._object_url(rel_path),
            data=data,
            method="PUT",
            headers=self._live_headers(with_content_type=True),
        )
        self._request(req)

    def _remote_get(self, rel_path: str) -> bytes | None:
        if self._drive_client is not None:
            data = self._drive_client.download_object(rel_path)
            self._sync_cookie_from_client()
            return data
        req = urllib.request.Request(
            self._object_url(rel_path),
            method="GET",
            headers=self._live_headers(with_content_type=False),
        )
        try:
            return self._request(req)
        except urllib.error.HTTPError as exc:
            if exc.code == 404:
                return None
            raise


def build_quark_backend(
    root: Path,
    *,
    refresh_token: str | None = None,
    cookie: str | None = None,
    force_mirror: bool = False,
    api_base: str | None = None,
    http_opener: HttpOpener | None = None,
    credential_manager: Any | None = None,
    **kwargs: Any,
) -> FileCloudBackend | QuarkLiveBackend:
    token = cookie or refresh_token
    auth_kind = "cookie" if cookie else "bearer"
    if credential_manager is not None:
        vault_cookie = credential_manager.get_cookie("quark")
        if vault_cookie:
            token = vault_cookie
            auth_kind = "cookie"
        else:
            token = (
                credential_manager.get_access_token("quark")
                or credential_manager.get_refresh_token("quark")
                or token
            )
            auth_kind = "bearer"
    elif looks_like_quark_cookie(token):
        auth_kind = "cookie"
    if token and not force_mirror:
        return QuarkLiveBackend(
            root,
            refresh_token=token,
            api_base=api_base,
            http_opener=http_opener,
            credential_manager=credential_manager,
            auth_kind=auth_kind,
            **{k: v for k, v in kwargs.items() if k in ("timeout", "remote_root", "chunk_size_bytes", "max_upload_bytes")},
        )
    return FileCloudBackend("quark", root, token=token)
