from __future__ import annotations

from pathlib import Path
from typing import Any

from niannian.storage.cloud_base import FileCloudBackend, HttpOpener, LiveCloudBackend
from niannian.storage.onedrive_api import (
    DEFAULT_CHUNK_SIZE_BYTES,
    DEFAULT_GRAPH_BASE,
    DEFAULT_MAX_UPLOAD_BYTES,
    DEFAULT_UPLOAD_MAX_BYTES,
    OneDriveGraphClient,
)


class OneDriveLiveBackend(LiveCloudBackend):
    """OneDrive L1 live — Microsoft Graph upload/download."""

    def __init__(
        self,
        mirror_root: Path,
        *,
        access_token: str | None,
        graph_base: str | None = None,
        upload_max_bytes: int | None = None,
        chunk_size_bytes: int | None = None,
        max_upload_bytes: int | None = None,
        timeout: float = 60.0,
        http_opener: HttpOpener | None = None,
        credential_manager: Any | None = None,
        remote_root: str | None = None,
    ) -> None:
        self._graph_client: OneDriveGraphClient | None = None
        if access_token:
            state_path = mirror_root.parent.parent / "sync" / "onedrive_state.json"
            self._graph_client = OneDriveGraphClient(
                access_token,
                remote_root=remote_root,
                graph_base=graph_base or DEFAULT_GRAPH_BASE,
                upload_max_bytes=upload_max_bytes or DEFAULT_UPLOAD_MAX_BYTES,
                chunk_size_bytes=chunk_size_bytes or DEFAULT_CHUNK_SIZE_BYTES,
                max_upload_bytes=max_upload_bytes or DEFAULT_MAX_UPLOAD_BYTES,
                timeout=timeout,
                http_opener=http_opener,
                state_path=state_path,
                credential_manager=credential_manager,
            )
        super().__init__(
            "onedrive",
            mirror_root,
            token=access_token,
            api_base=graph_base or DEFAULT_GRAPH_BASE,
            timeout=timeout,
            http_opener=http_opener,
            credential_manager=credential_manager,
            provider="onedrive",
            remote_root=remote_root,
        )

    def tier_ping(self) -> bool:
        if self._graph_client is not None:
            return self._graph_client.tier_ping()
        return super().is_configured()

    def _remote_put(self, rel_path: str, data: bytes) -> None:
        if self._graph_client is not None:
            self._sync_token()
            self._graph_client.upload_object(rel_path, data)
            return
        super()._remote_put(rel_path, data)

    def _remote_get(self, rel_path: str) -> bytes | None:
        if self._graph_client is not None:
            self._sync_token()
            return self._graph_client.download_object(rel_path)
        return super()._remote_get(rel_path)

    def _sync_token(self) -> None:
        if self._credential_manager is None:
            return
        new_token = self._credential_manager.get_access_token("onedrive")
        if new_token:
            self.token = new_token
            if self._graph_client is not None:
                self._graph_client._access_token = new_token


def build_onedrive_backend(
    root: Path,
    *,
    access_token: str | None = None,
    refresh_token: str | None = None,
    force_mirror: bool = False,
    graph_base: str | None = None,
    upload_max_bytes: int | None = None,
    chunk_size_bytes: int | None = None,
    max_upload_bytes: int | None = None,
    http_opener: HttpOpener | None = None,
    credential_manager: Any | None = None,
    **kwargs: Any,
) -> FileCloudBackend | OneDriveLiveBackend:
    token = access_token or refresh_token
    if credential_manager is not None:
        token = credential_manager.get_access_token("onedrive") or token
    if token and not force_mirror:
        return OneDriveLiveBackend(
            root,
            access_token=token,
            graph_base=graph_base,
            upload_max_bytes=upload_max_bytes,
            chunk_size_bytes=chunk_size_bytes,
            max_upload_bytes=max_upload_bytes,
            http_opener=http_opener,
            credential_manager=credential_manager,
            **{k: v for k, v in kwargs.items() if k in ("timeout", "remote_root")},
        )
    return FileCloudBackend("onedrive", root, token=token)
