"""Google Drive storage plugin — adapter that satisfies the StorageProvider protocol.

Part of the local-first backup system (Mercury Wave 3). Uploads encrypted backups
to the user's own Google Drive account. User data never lives on Vastian servers.

Setup: Run `vastian setup gdrive` to complete OAuth2 flow and store tokens
in config/local/gdrive_token.json.

Requires: google-api-python-client, google-auth-oauthlib (optional deps).
"""

from __future__ import annotations

import logging
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

logger = logging.getLogger(__name__)

# OAuth2 config — user provides their own Google Cloud project credentials
# or uses Vastian's default OAuth client (registered for Google Drive API).
_SCOPES = ["https://www.googleapis.com/auth/drive.file"]
_TOKEN_FILE = "gdrive_token.json"
_CREDENTIALS_FILE = "gdrive_credentials.json"
_VASTIAN_FOLDER = "Vastian Backups"


class GoogleDriveAdapter:
    """Adapter that satisfies the StorageProvider protocol for Google Drive.

    Protocol satisfaction: isinstance(GoogleDriveAdapter(...), StorageProvider) is True.
    """

    def __init__(self, config_dir: str | Path = "config/local") -> None:
        self._config_dir = Path(config_dir)
        self._token_path = self._config_dir / _TOKEN_FILE
        self._creds_path = self._config_dir / _CREDENTIALS_FILE
        self._service: Any = None

    def _ensure_service(self) -> None:
        """Lazily initialize the Google Drive API service."""
        if self._service is not None:
            return
        if not self.is_authenticated():
            raise RuntimeError(
                "Google Drive not authenticated. Run `vastian setup gdrive` "
                "to complete the OAuth2 flow."
            )
        try:
            from google.oauth2.credentials import Credentials
            from googleapiclient.discovery import build

            creds = Credentials.from_authorized_user_file(str(self._token_path), _SCOPES)
            self._service = build("drive", "v3", credentials=creds)
        except ImportError as err:
            raise RuntimeError(
                "Google Drive dependencies not installed. Run: "
                "pip install google-api-python-client google-auth-oauthlib"
            ) from err

    async def upload(self, local_path: str, remote_path: str) -> dict:
        """Upload a local file to Google Drive."""
        self._ensure_service()
        from googleapiclient.http import MediaFileUpload

        local = Path(local_path)
        if not local.exists():
            raise FileNotFoundError(f"Local file not found: {local_path}")

        media = MediaFileUpload(str(local), resumable=True)
        file_metadata = {"name": remote_path, "parents": [self._get_folder_id()]}

        result = (
            self._service.files()
            .create(body=file_metadata, media_body=media, fields="id,name,size")
            .execute()
        )

        return {
            "remote_path": remote_path,
            "remote_id": result.get("id", ""),
            "size": local.stat().st_size,
            "timestamp": datetime.now(UTC).isoformat(),
        }

    async def download(self, remote_path: str, local_path: str) -> dict:
        """Download a file from Google Drive to a local path."""
        self._ensure_service()
        import io

        from googleapiclient.http import MediaIoBaseDownload

        # Find the file by name in the Vastian folder
        query = f"name='{remote_path}' and '{self._get_folder_id()}' in parents"
        results = self._service.files().list(q=query, fields="files(id,size)").execute()
        files = results.get("files", [])
        if not files:
            raise FileNotFoundError(f"Remote file not found: {remote_path}")

        file_id = files[0]["id"]
        request = self._service.files().get_media(fileId=file_id)

        local = Path(local_path)
        local.parent.mkdir(parents=True, exist_ok=True)

        fh = io.FileIO(str(local), "wb")
        downloader = MediaIoBaseDownload(fh, request)
        done = False
        while not done:
            _, done = downloader.next_chunk()

        return {
            "local_path": str(local),
            "size": local.stat().st_size,
            "timestamp": datetime.now(UTC).isoformat(),
        }

    async def list_files(self, prefix: str = "") -> list[dict]:
        """List files in the Vastian Backups folder on Google Drive."""
        self._ensure_service()
        query = f"'{self._get_folder_id()}' in parents and trashed=false"
        if prefix:
            query += f" and name contains '{prefix}'"

        results = (
            self._service.files()
            .list(
                q=query,
                fields="files(id,name,size,modifiedTime)",
                pageSize=1000,
            )
            .execute()
        )

        return [
            {
                "remote_path": f.get("name", ""),
                "size": int(f.get("size", 0)),
                "modified": f.get("modifiedTime", ""),
                "remote_id": f.get("id", ""),
            }
            for f in results.get("files", [])
        ]

    async def delete(self, remote_path: str) -> bool:
        """Delete a file from Google Drive."""
        self._ensure_service()
        query = f"name='{remote_path}' and '{self._get_folder_id()}' in parents"
        results = self._service.files().list(q=query, fields="files(id)").execute()
        files = results.get("files", [])
        if not files:
            return False
        self._service.files().delete(fileId=files[0]["id"]).execute()
        return True

    def get_auth_url(self) -> str:
        """Get the OAuth2 authorization URL for Google Drive."""
        try:
            from google_auth_oauthlib.flow import InstalledAppFlow

            if not self._creds_path.exists():
                return (
                    "No credentials file found. Place your Google Cloud OAuth "
                    f"client JSON at {self._creds_path}"
                )
            flow = InstalledAppFlow.from_client_secrets_file(str(self._creds_path), _SCOPES)
            flow.redirect_uri = "urn:ietf:wg:oauth:2.0:oob"
            auth_url, _ = flow.authorization_url(prompt="consent")
            return auth_url
        except ImportError:
            return "Google auth dependencies not installed. Run: pip install google-auth-oauthlib"

    def is_authenticated(self) -> bool:
        """Check if valid Google Drive credentials exist."""
        return self._token_path.exists()

    def _get_folder_id(self) -> str:
        """Get or create the 'Vastian Backups' folder on Drive."""
        query = (
            f"name='{_VASTIAN_FOLDER}' and mimeType='application/vnd.google-apps.folder' "
            "and trashed=false"
        )
        results = self._service.files().list(q=query, fields="files(id)").execute()
        folders = results.get("files", [])
        if folders:
            return folders[0]["id"]

        # Create the folder
        folder_metadata = {
            "name": _VASTIAN_FOLDER,
            "mimeType": "application/vnd.google-apps.folder",
        }
        folder = self._service.files().create(body=folder_metadata, fields="id").execute()
        return folder["id"]


def register() -> None:
    """Register the Google Drive storage plugin with the loader."""
    from vastian.loader import register as loader_register

    loader_register("gdrive_storage", GoogleDriveAdapter)
