"""
Google Drive API endpoints
"""

import logging
import os
from datetime import datetime
from typing import Annotated, Optional

from fastapi import APIRouter, Depends, Form, HTTPException, Request, status
from sqlalchemy.orm import Session

from app.auth import require_login
from app.config import settings
from app.database import get_db
from app.utils.oauth_helper import exchange_oauth_token
from app.utils.settings_service import save_setting_to_db
from app.utils.settings_sync import notify_settings_updated

# Set up logging
logger = logging.getLogger(__name__)

router = APIRouter()


@router.post("/google-drive/exchange-token")
@require_login
async def exchange_google_drive_token(
    request: Request,
    client_id: Annotated[str, Form(...)],
    client_secret: Annotated[str, Form(...)],
    redirect_uri: Annotated[str, Form(...)],
    code: Annotated[str, Form(...)],
    folder_id: Annotated[Optional[str], Form()] = None,
):
    """
    Exchange an authorization code for refresh and access tokens from Google.
    This is done on the server to avoid exposing client secret in the browser.
    """
    # Prepare the token request
    token_url = "https://oauth2.googleapis.com/token"

    payload = {
        "client_id": client_id,
        "client_secret": client_secret,
        "code": code,
        "redirect_uri": redirect_uri,
        "grant_type": "authorization_code",
    }

    # Use shared OAuth helper (handles secure logging and error handling)
    token_data = exchange_oauth_token(provider_name="Google Drive", token_url=token_url, payload=payload)

    # Return just what's needed by the frontend
    return {
        "refresh_token": token_data["refresh_token"],
        "access_token": token_data["access_token"],
        "expires_in": token_data.get("expires_in", 3600),
    }


@router.post("/google-drive/update-settings")
@require_login
async def update_google_drive_settings(
    request: Request,
    refresh_token: Annotated[str, Form(...)],
    client_id: Annotated[Optional[str], Form()] = None,
    client_secret: Annotated[Optional[str], Form()] = None,
    folder_id: Annotated[Optional[str], Form()] = None,
    use_oauth: Annotated[str, Form()] = "true",
    db: Session = Depends(get_db),
):
    """
    Update Google Drive settings in memory and persist to database
    """
    try:
        logger.info("Updating Google Drive settings in memory and database")

        user = request.session.get("user", {}) if hasattr(request, "session") else {}
        changed_by = (
            user.get("preferred_username") or user.get("username") or user.get("email") or user.get("id") or "wizard"
        )

        # Convert use_oauth string to boolean
        use_oauth_bool = use_oauth.lower() in ("true", "1", "yes", "y", "t")

        # Update settings in memory and persist to database
        if refresh_token:
            settings.google_drive_refresh_token = refresh_token
            save_setting_to_db(db, "google_drive_refresh_token", refresh_token, changed_by=changed_by)
            logger.info("Updated GOOGLE_DRIVE_REFRESH_TOKEN in memory and database")

        if client_id:
            settings.google_drive_client_id = client_id
            save_setting_to_db(db, "google_drive_client_id", client_id, changed_by=changed_by)
            logger.info("Updated GOOGLE_DRIVE_CLIENT_ID in memory and database")

        if client_secret:
            settings.google_drive_client_secret = client_secret
            save_setting_to_db(db, "google_drive_client_secret", client_secret, changed_by=changed_by)
            logger.info("Updated GOOGLE_DRIVE_CLIENT_SECRET in memory and database")

        if folder_id:
            settings.google_drive_folder_id = folder_id
            save_setting_to_db(db, "google_drive_folder_id", folder_id, changed_by=changed_by)
            logger.info("Updated GOOGLE_DRIVE_FOLDER_ID in memory and database")

        # Set the OAuth flag
        settings.google_drive_use_oauth = use_oauth_bool
        save_setting_to_db(
            db,
            "google_drive_use_oauth",
            str(use_oauth_bool).lower(),
            changed_by=changed_by,
        )
        logger.info(f"Updated GOOGLE_DRIVE_USE_OAUTH in memory and database to {use_oauth_bool}")

        notify_settings_updated()

        return {
            "status": "success",
            "message": "Google Drive settings have been updated in memory and database",
        }

    except Exception as e:
        logger.exception(f"Unexpected error updating Google Drive settings: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to update Google Drive settings: {str(e)}",
        )


@router.get("/google-drive/test-token")
@require_login
async def test_google_drive_token(request: Request):
    """
    Test if the configured Google Drive token is valid.
    Tests both OAuth and service account approaches based on configuration.
    """
    try:
        from app.tasks.upload_to_google_drive import get_drive_service_oauth, get_google_drive_service

        logger.info("Testing Google Drive token validity")

        # Check if OAuth is enabled and configured
        if getattr(settings, "google_drive_use_oauth", False):
            if not (
                settings.google_drive_client_id
                and settings.google_drive_client_secret
                and settings.google_drive_refresh_token
            ):
                logger.warning("Google Drive OAuth credentials not fully configured")
                return {
                    "status": "error",
                    "message": "Google Drive OAuth credentials are not fully configured",
                }

            try:
                # Test OAuth connection
                service = get_drive_service_oauth()

                # Get credentials for checking token validity
                import google.oauth2.credentials
                from google.auth.transport.requests import Request

                credentials = google.oauth2.credentials.Credentials(
                    token=None,
                    refresh_token=settings.google_drive_refresh_token,
                    token_uri="https://oauth2.googleapis.com/token",
                    client_id=settings.google_drive_client_id,
                    client_secret=settings.google_drive_client_secret,
                )

                # Force a refresh to update the token expiration
                if not credentials.valid:
                    credentials.refresh(Request())

                # Get token expiration info
                expiration_info = {}
                if hasattr(credentials, "expiry") and credentials.expiry:
                    now = datetime.now()
                    expiry = credentials.expiry
                    time_left = expiry - now
                    expiration_info = {
                        "expires_at": expiry.isoformat(),
                        "expires_in_seconds": max(0, int(time_left.total_seconds())),
                        "expires_in_human": format_time_remaining(time_left),
                    }

                # Test basic API operation
                about = service.about().get(fields="user").execute()
                user_email = about.get("user", {}).get("emailAddress", "Unknown")

                logger.info(f"Successfully connected to Google Drive as {user_email}")

                return {
                    "status": "success",
                    "message": f"OAuth token is valid! Connected as {user_email}",
                    "account": user_email,
                    "auth_type": "oauth",
                    "token_info": expiration_info,
                }
            except Exception as e:
                error_msg = str(e)
                logger.error(f"Google Drive OAuth token test failed: {error_msg}")

                # Check if this is a token-related error
                if "invalid_grant" in error_msg.lower() or "token" in error_msg.lower():
                    return {
                        "status": "error",
                        "message": f"OAuth token validation failed: {error_msg}",
                        "needs_reauth": True,
                    }
                return {"status": "error", "message": f"Connection error: {error_msg}"}
        else:
            # Test service account connection
            if not settings.google_drive_credentials_json:
                logger.warning("Google Drive service account credentials not configured")
                return {
                    "status": "error",
                    "message": "Google Drive service account credentials are not configured",
                }

            try:
                service = get_google_drive_service()
                about = service.about().get(fields="user").execute()

                # For service accounts, try to show the delegated user if available
                user_email = about.get("user", {}).get("emailAddress", "Unknown")
                delegated_user = getattr(settings, "google_drive_delegate_to", None)

                if delegated_user:
                    user_display = f"{user_email} (delegating as {delegated_user})"
                else:
                    user_display = user_email

                logger.info(f"Successfully connected to Google Drive using service account as {user_display}")

                return {
                    "status": "success",
                    "message": f"Service account is valid! Connected as {user_display}",
                    "account": user_email,
                    "auth_type": "service_account",
                }
            except Exception as e:
                error_msg = str(e)
                logger.error(f"Google Drive service account test failed: {error_msg}")
                return {
                    "status": "error",
                    "message": f"Service account validation failed: {error_msg}",
                }

    except Exception as e:
        logger.exception("Unexpected error testing Google Drive token")
        return {"status": "error", "message": f"Unexpected error: {str(e)}"}


@router.get("/google-drive/get-token-info")
@require_login
async def get_google_drive_token_info(request: Request):
    """
    Get information about the current Google Drive token.
    Returns the access token if one exists and is valid.
    Used by the frontend to access the Google Picker API.
    """
    try:
        logger.info("Getting Google Drive token information")

        # Check if OAuth is enabled and configured
        if not getattr(settings, "google_drive_use_oauth", False):
            logger.warning("OAuth is not enabled, using service account instead")
            return {
                "status": "error",
                "message": "OAuth is not enabled. Service accounts don't support user-facing features.",
            }

        if not (
            settings.google_drive_client_id
            and settings.google_drive_client_secret
            and settings.google_drive_refresh_token
        ):
            logger.warning("Google Drive OAuth credentials not fully configured")
            return {
                "status": "error",
                "message": "Google Drive OAuth credentials are not fully configured",
            }

        try:
            # Get credentials and access token
            import google.oauth2.credentials
            from google.auth.transport.requests import Request

            credentials = google.oauth2.credentials.Credentials(
                token=None,
                refresh_token=settings.google_drive_refresh_token,
                token_uri="https://oauth2.googleapis.com/token",
                client_id=settings.google_drive_client_id,
                client_secret=settings.google_drive_client_secret,
            )

            # Force a refresh to get a fresh access token
            if not credentials.valid:
                credentials.refresh(Request())

            # Get token expiration info
            expiration_info = {}
            if hasattr(credentials, "expiry") and credentials.expiry:
                now = datetime.now()
                expiry = credentials.expiry
                time_left = expiry - now
                expiration_info = {
                    "expires_at": expiry.isoformat(),
                    "expires_in_seconds": max(0, int(time_left.total_seconds())),
                    "expires_in_human": format_time_remaining(time_left),
                }

            # Return the token info
            logger.info("Successfully retrieved Google Drive access token")

            return {
                "status": "success",
                "message": "Access token successfully retrieved",
                "access_token": credentials.token,
                "token_info": expiration_info,
            }
        except Exception as e:
            error_msg = str(e)
            logger.error(f"Failed to get Google Drive token: {error_msg}")

            # Check if this is a token-related error
            if "invalid_grant" in error_msg.lower() or "token" in error_msg.lower():
                return {
                    "status": "error",
                    "message": f"OAuth token retrieval failed: {error_msg}",
                    "needs_reauth": True,
                }
            return {"status": "error", "message": f"Token retrieval error: {error_msg}"}

    except Exception as e:
        logger.exception("Unexpected error getting Google Drive token info")
        return {"status": "error", "message": f"Unexpected error: {str(e)}"}


def format_time_remaining(time_delta):
    """Format a timedelta into a human-readable string."""
    if time_delta.total_seconds() <= 0:
        return "Expired"

    days = time_delta.days
    hours, remainder = divmod(time_delta.seconds, 3600)
    minutes, seconds = divmod(remainder, 60)

    parts = []
    if days > 0:
        parts.append(f"{days} day{'s' if days != 1 else ''}")
    if hours > 0:
        parts.append(f"{hours} hour{'s' if hours != 1 else ''}")
    if minutes > 0 and days == 0:  # Only show minutes if less than a day
        parts.append(f"{minutes} minute{'s' if minutes != 1 else ''}")

    return ", ".join(parts)


@router.post("/google-drive/save-settings")
@require_login
async def save_google_drive_settings(
    request: Request,
    refresh_token: Annotated[str, Form(...)],
    client_id: Annotated[Optional[str], Form()] = None,
    client_secret: Annotated[Optional[str], Form()] = None,
    folder_id: Annotated[Optional[str], Form()] = None,
    use_oauth: Annotated[str, Form()] = "true",
    db: Session = Depends(get_db),
):
    """
    Save Google Drive settings to the .env file (best-effort) and persist to database.
    """
    try:
        # Get the path to the .env file
        env_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(__file__))), ".env")

        # Convert use_oauth string to boolean
        use_oauth_bool = use_oauth.lower() in ("true", "1", "yes", "y", "t")

        user = request.session.get("user", {}) if hasattr(request, "session") else {}
        changed_by = (
            user.get("preferred_username") or user.get("username") or user.get("email") or user.get("id") or "wizard"
        )

        # Define settings to update
        drive_settings = {"GOOGLE_DRIVE_USE_OAUTH": str(use_oauth_bool).lower()}

        # Only update these if provided
        if use_oauth_bool:
            if refresh_token:
                drive_settings["GOOGLE_DRIVE_REFRESH_TOKEN"] = refresh_token
            if client_id:
                drive_settings["GOOGLE_DRIVE_CLIENT_ID"] = client_id
            if client_secret:
                drive_settings["GOOGLE_DRIVE_CLIENT_SECRET"] = client_secret

        # Always include folder ID if provided
        if folder_id:
            drive_settings["GOOGLE_DRIVE_FOLDER_ID"] = folder_id

        # Try to update the .env file, but don't fail if it doesn't exist (for Docker containers)
        if os.path.exists(env_path):
            try:
                logger.info(f"Updating Google Drive settings in {env_path}")

                # Read the current .env file
                with open(env_path, "r") as f:
                    env_lines = f.readlines()

                # Process each line and update or add settings
                updated = set()
                new_env_lines = []
                for line in env_lines:
                    stripped_line = line.rstrip()
                    is_updated = False
                    for key, value in drive_settings.items():
                        if stripped_line.startswith(f"{key}=") or stripped_line.startswith(f"# {key}="):
                            # Uncomment if commented out - check the original stripped line
                            new_env_lines.append(f"{key}={value}")
                            updated.add(key)
                            is_updated = True
                            break
                    if not is_updated:
                        new_env_lines.append(stripped_line)

                # Add any settings that weren't updated (they weren't in the file)
                for key, value in drive_settings.items():
                    if key not in updated:
                        new_env_lines.append(f"{key}={value}")

                # Write the updated .env file
                with open(env_path, "w") as f:
                    f.write("\n".join(new_env_lines) + "\n")

                logger.info("Successfully updated Google Drive settings in .env file")
            except Exception as e:
                logger.warning(f"Failed to update .env file: {str(e)}, but will continue with in-memory update")
        else:
            logger.warning(
                f".env file not found at {env_path}, skipping file update but continuing with in-memory update"
            )

        # Update the settings in memory (this always happens)
        if refresh_token:
            settings.google_drive_refresh_token = refresh_token
        if client_id:
            settings.google_drive_client_id = client_id
        if client_secret:
            settings.google_drive_client_secret = client_secret
        if folder_id:
            settings.google_drive_folder_id = folder_id

        # Set OAuth flag
        settings.google_drive_use_oauth = use_oauth_bool

        # Persist to database
        save_setting_to_db(
            db,
            "google_drive_use_oauth",
            str(use_oauth_bool).lower(),
            changed_by=changed_by,
        )
        if refresh_token:
            save_setting_to_db(db, "google_drive_refresh_token", refresh_token, changed_by=changed_by)
        if client_id:
            save_setting_to_db(db, "google_drive_client_id", client_id, changed_by=changed_by)
        if client_secret:
            save_setting_to_db(db, "google_drive_client_secret", client_secret, changed_by=changed_by)
        if folder_id:
            save_setting_to_db(db, "google_drive_folder_id", folder_id, changed_by=changed_by)

        notify_settings_updated()

        logger.info("Successfully updated Google Drive settings in memory and database")

        return {
            "status": "success",
            "message": "Google Drive settings have been saved",
            "in_memory_only": not os.path.exists(env_path),
        }

    except Exception as e:
        logger.exception(f"Unexpected error saving Google Drive settings: {str(e)}")
        raise HTTPException(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            detail=f"Failed to save Google Drive settings: {str(e)}",
        )
