"""Google Drive destination — auto-uploads files using service account or OAuth2."""

from __future__ import annotations

import json
import logging
import mimetypes
import os
from typing import Dict, Optional

from kgout.destinations.base import BaseDestination

logger = logging.getLogger("kgout")


def _load_credentials(creds_path: str):
    """Auto-detect credential type and return appropriate google-auth credentials.

    Supports:
    - Service account JSON (type: "service_account")
    - kgout OAuth2 token (type: "kgout_oauth2") — generated by kgout-auth
    - Standard OAuth2 authorized_user JSON (type: "authorized_user")
    """
    try:
        from google.oauth2 import service_account as sa_module
        from google.oauth2.credentials import Credentials as UserCredentials
    except ImportError:
        raise ImportError(
            "Google auth libraries not installed.\n"
            "Install with: pip install kgout[gdrive]"
        )

    with open(creds_path, "r") as f:
        data = json.load(f)

    cred_type = data.get("type", "")
    scopes = ["https://www.googleapis.com/auth/drive.file"]

    if cred_type == "service_account":
        logger.info("🔑 Using service account credentials")
        return sa_module.Credentials.from_service_account_file(creds_path, scopes=scopes)

    elif cred_type in ("kgout_oauth2", "authorized_user"):
        logger.info("🔑 Using OAuth2 user credentials")
        return UserCredentials(
            token=data.get("token"),
            refresh_token=data.get("refresh_token"),
            client_id=data.get("client_id"),
            client_secret=data.get("client_secret"),
            token_uri=data.get("token_uri", "https://oauth2.googleapis.com/token"),
            scopes=scopes,
        )

    else:
        raise ValueError(
            f"Unknown credential type '{cred_type}' in {creds_path}.\n"
            f"Expected 'service_account' or 'kgout_oauth2'.\n"
            f"Run 'kgout-auth' to generate a valid token file, or use a "
            f"service account JSON from Google Cloud Console."
        )


class GDriveDestination(BaseDestination):
    """Upload new/modified files to a Google Drive folder.

    Supports two authentication methods:
    - **OAuth2 user credentials** (recommended): Generated by ``kgout-auth``.
      Files are owned by your Google account. Works with free Gmail.
    - **Service account**: For Google Workspace / Shared Drives.

    Parameters
    ----------
    credentials_path : str
        Path to credentials JSON (service account or kgout OAuth2 token).
    folder_id : str
        Google Drive folder ID (from the folder URL).
    """

    def __init__(self, credentials_path: str, folder_id: str):
        self._creds_path = credentials_path
        self._folder_id = folder_id
        self._service = None
        # Track uploaded file IDs so we can update rather than duplicate
        self._uploaded: Dict[str, str] = {}  # relpath -> drive file ID

    @property
    def name(self) -> str:
        return "gdrive"

    def start(self):
        try:
            from googleapiclient.discovery import build
        except ImportError:
            raise ImportError(
                "Google Drive dependencies not installed.\n"
                "Install with: pip install kgout[gdrive]"
            )

        if not os.path.isfile(self._creds_path):
            raise FileNotFoundError(
                f"Credentials file not found: {self._creds_path}\n"
                f"Run 'kgout-auth' to generate one, or provide a service account JSON."
            )

        creds = _load_credentials(self._creds_path)
        self._service = build("drive", "v3", credentials=creds, cache_discovery=False)

        logger.info(
            "☁️  Google Drive connected → folder %s",
            self._folder_id,
        )

    def stop(self):
        self._service = None

    def _guess_mimetype(self, filepath: str) -> str:
        mime, _ = mimetypes.guess_type(filepath)
        return mime or "application/octet-stream"

    def sync(self, filepath: str, relpath: str, event: str):
        """Upload or update a file in Google Drive."""
        if not self._service:
            logger.error("GDrive service not initialized — skipping %s", relpath)
            return

        # Warn on very large files (>100MB) — these can be slow and eat quota
        try:
            size_mb = os.path.getsize(filepath) / (1024 * 1024)
            if size_mb > 100:
                logger.warning(
                    "⚠️  Large file: %s (%.0f MB) — upload may be slow", relpath, size_mb
                )
        except OSError:
            pass

        from googleapiclient.http import MediaFileUpload

        mime = self._guess_mimetype(filepath)
        media = MediaFileUpload(filepath, mimetype=mime, resumable=True)

        if relpath in self._uploaded and event == "modified":
            # Update existing file
            file_id = self._uploaded[relpath]
            try:
                self._service.files().update(
                    fileId=file_id,
                    media_body=media,
                    supportsAllDrives=True,
                ).execute()
                logger.info("   ↳ Updated on GDrive: %s", relpath)
            except Exception as exc:
                logger.error("   ↳ GDrive update failed for %s: %s", relpath, exc)
                # Fall through to create
                self._upload_new(filepath, relpath, mime)
        else:
            self._upload_new(filepath, relpath, mime)

    def _upload_new(self, filepath, relpath, mime):
        """Create a new file on Drive."""
        from googleapiclient.http import MediaFileUpload

        # Always create a fresh media object
        fresh_media = MediaFileUpload(filepath, mimetype=mime, resumable=True)

        metadata = {
            "name": os.path.basename(relpath),
            "parents": [self._folder_id],
        }

        # If relpath has subdirectories, include them in the name
        # (flat upload, no folder creation — keeps it simple)
        if os.sep in relpath or "/" in relpath:
            metadata["name"] = relpath.replace(os.sep, "_").replace("/", "_")

        try:
            result = self._service.files().create(
                body=metadata,
                media_body=fresh_media,
                fields="id,name",
                supportsAllDrives=True,
            ).execute()

            self._uploaded[relpath] = result["id"]
            logger.info(
                "   ↳ Uploaded to GDrive: %s (id: %s)",
                result["name"],
                result["id"],
            )
        except Exception as exc:
            logger.error("   ↳ GDrive upload failed for %s: %s", relpath, exc)
