"""
Google Workspace Admin SDK client.

Provides methods for interacting with Google Workspace Directory API to
sync users, groups, and memberships to Django.

Supports two authentication methods:
1. Workload Identity Federation (recommended for AWS ECS production)
2. Service account JSON (fallback for local development)

Required Google Admin SDK scopes:
- https://www.googleapis.com/auth/admin.directory.group.readonly
- https://www.googleapis.com/auth/admin.directory.user.readonly
"""

from __future__ import annotations

import json
import logging
from dataclasses import dataclass
from functools import cached_property
from typing import TYPE_CHECKING, Any

from google.auth import default as google_auth_default
from google.auth.transport.requests import Request
from google.oauth2 import service_account
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

if TYPE_CHECKING:
    from google.oauth2.credentials import Credentials
    from googleapiclient._apis.admin.directory_v1 import DirectoryResource

logger = logging.getLogger(__name__)

# Admin SDK scopes for group/user read access
ADMIN_SCOPES = [
    "https://www.googleapis.com/auth/admin.directory.group.readonly",
    "https://www.googleapis.com/auth/admin.directory.user.readonly",
]


@dataclass
class WorkspaceUser:
    """Represents a Google Workspace user."""

    email: str
    given_name: str
    family_name: str
    full_name: str
    is_admin: bool
    is_suspended: bool
    is_archived: bool
    org_unit_path: str
    thumbnail_url: str | None
    raw_data: dict[str, Any]

    @classmethod
    def from_api_response(cls, data: dict[str, Any]) -> WorkspaceUser:
        """Create WorkspaceUser from Google API response."""
        name = data.get("name", {})
        return cls(
            email=data.get("primaryEmail", ""),
            given_name=name.get("givenName", ""),
            family_name=name.get("familyName", ""),
            full_name=name.get("fullName", ""),
            is_admin=data.get("isAdmin", False),
            is_suspended=data.get("suspended", False),
            is_archived=data.get("archived", False),
            org_unit_path=data.get("orgUnitPath", "/"),
            thumbnail_url=data.get("thumbnailPhotoUrl"),
            raw_data=data,
        )


@dataclass
class WorkspaceGroup:
    """Represents a Google Workspace group."""

    email: str
    name: str
    description: str
    member_count: int
    raw_data: dict[str, Any]

    @classmethod
    def from_api_response(cls, data: dict[str, Any]) -> WorkspaceGroup:
        """Create WorkspaceGroup from Google API response."""
        return cls(
            email=data.get("email", ""),
            name=data.get("name", ""),
            description=data.get("description", ""),
            member_count=int(data.get("directMembersCount", 0)),
            raw_data=data,
        )


@dataclass
class GroupMember:
    """Represents a member of a Google Workspace group."""

    email: str
    role: str  # OWNER, MANAGER, or MEMBER
    member_type: str  # USER, GROUP, or EXTERNAL
    status: str  # ACTIVE, SUSPENDED, etc.
    raw_data: dict[str, Any]

    @classmethod
    def from_api_response(cls, data: dict[str, Any]) -> GroupMember:
        """Create GroupMember from Google API response."""
        return cls(
            email=data.get("email", ""),
            role=data.get("role", "MEMBER"),
            member_type=data.get("type", "USER"),
            status=data.get("status", "ACTIVE"),
            raw_data=data,
        )


class GoogleWorkspaceClient:
    """
    Client for Google Workspace Admin SDK.

    Provides methods to list users, groups, and group memberships for
    synchronizing with Django's user management system.

    Example:
        client = GoogleWorkspaceClient(
            service_account_email="service@project.iam.gserviceaccount.com",
            delegated_admin="admin@company.com",
            domain="company.com",
        )
        users = client.list_users()
        groups = client.list_groups()
    """

    def __init__(
        self,
        *,
        service_account_email: str,
        delegated_admin: str,
        domain: str,
        service_account_json: str | None = None,
        wif_provider: str | None = None,
    ):
        """
        Initialize the Google Workspace client.

        Args:
            service_account_email: Email of the service account to use
            delegated_admin: Admin user to impersonate for domain-wide delegation
            domain: Primary Google Workspace domain
            service_account_json: JSON credentials (optional, for local dev)
            wif_provider: Workload Identity Federation provider (optional, for production)
        """
        self.service_account_email = service_account_email
        self.delegated_admin = delegated_admin
        self.domain = domain
        self._service_account_json = service_account_json
        self._wif_provider = wif_provider
        self._credentials: Credentials | None = None
        self._service: DirectoryResource | None = None

    @cached_property
    def credentials(self) -> Credentials:
        """Get authenticated credentials with domain-wide delegation."""
        if self._service_account_json:
            # Use service account JSON (local development)
            logger.debug("Using service account JSON for authentication")
            service_account_info = json.loads(self._service_account_json)
            credentials = service_account.Credentials.from_service_account_info(
                service_account_info,
                scopes=ADMIN_SCOPES,
                subject=self.delegated_admin,
            )
        elif self._wif_provider:
            # Use Workload Identity Federation (production)
            logger.debug("Using Workload Identity Federation for authentication")
            # Get credentials from WIF
            credentials, _ = google_auth_default(scopes=ADMIN_SCOPES)
            # Create delegated credentials
            credentials = credentials.with_subject(self.delegated_admin)
        else:
            # Try application default credentials
            logger.debug("Using application default credentials")
            credentials, _ = google_auth_default(scopes=ADMIN_SCOPES)
            if hasattr(credentials, "with_subject"):
                credentials = credentials.with_subject(self.delegated_admin)

        # Refresh if needed
        if not credentials.valid:
            credentials.refresh(Request())

        return credentials

    @cached_property
    def service(self) -> DirectoryResource:
        """Get the Directory API service."""
        return build("admin", "directory_v1", credentials=self.credentials)

    def list_users(
        self,
        *,
        query: str | None = None,
        include_suspended: bool = False,
        max_results: int = 500,
    ) -> list[WorkspaceUser]:
        """
        List all users in the Google Workspace domain.

        Args:
            query: Optional query string to filter users
            include_suspended: Whether to include suspended users
            max_results: Maximum number of results per page

        Returns:
            List of WorkspaceUser objects
        """
        users: list[WorkspaceUser] = []
        page_token = None

        try:
            while True:
                request = self.service.users().list(
                    domain=self.domain,
                    query=query,
                    maxResults=min(max_results, 500),
                    pageToken=page_token,
                    orderBy="email",
                )
                response = request.execute()

                for user_data in response.get("users", []):
                    user = WorkspaceUser.from_api_response(user_data)
                    if include_suspended or not user.is_suspended:
                        users.append(user)

                page_token = response.get("nextPageToken")
                if not page_token:
                    break

            logger.info(f"Retrieved {len(users)} users from Google Workspace")
            return users

        except HttpError as e:
            logger.error(f"Failed to list users: {e}")
            raise

    def get_user(self, email: str) -> WorkspaceUser | None:
        """
        Get a specific user by email.

        Args:
            email: The user's email address

        Returns:
            WorkspaceUser or None if not found
        """
        try:
            response = self.service.users().get(userKey=email).execute()
            return WorkspaceUser.from_api_response(response)
        except HttpError as e:
            if e.resp.status == 404:
                return None
            logger.error(f"Failed to get user {email}: {e}")
            raise

    def list_groups(self, *, max_results: int = 200) -> list[WorkspaceGroup]:
        """
        List all groups in the Google Workspace domain.

        Args:
            max_results: Maximum number of results per page

        Returns:
            List of WorkspaceGroup objects
        """
        groups: list[WorkspaceGroup] = []
        page_token = None

        try:
            while True:
                request = self.service.groups().list(
                    customer="my_customer",
                    maxResults=min(max_results, 200),
                    pageToken=page_token,
                )
                response = request.execute()

                for group_data in response.get("groups", []):
                    groups.append(WorkspaceGroup.from_api_response(group_data))

                page_token = response.get("nextPageToken")
                if not page_token:
                    break

            logger.info(f"Retrieved {len(groups)} groups from Google Workspace")
            return groups

        except HttpError as e:
            logger.error(f"Failed to list groups: {e}")
            raise

    def get_group(self, group_email: str) -> WorkspaceGroup | None:
        """
        Get a specific group by email.

        Args:
            group_email: The group's email address

        Returns:
            WorkspaceGroup or None if not found
        """
        try:
            response = self.service.groups().get(groupKey=group_email).execute()
            return WorkspaceGroup.from_api_response(response)
        except HttpError as e:
            if e.resp.status == 404:
                return None
            logger.error(f"Failed to get group {group_email}: {e}")
            raise

    def get_group_members(
        self,
        group_email: str,
        *,
        roles: list[str] | None = None,
        include_external: bool = False,
    ) -> list[GroupMember]:
        """
        Get all members of a specific group.

        Args:
            group_email: The group's email address
            roles: Filter by roles (OWNER, MANAGER, MEMBER)
            include_external: Whether to include external members

        Returns:
            List of GroupMember objects
        """
        members: list[GroupMember] = []
        page_token = None

        try:
            while True:
                request = self.service.members().list(
                    groupKey=group_email,
                    roles=",".join(roles) if roles else None,
                    pageToken=page_token,
                )
                response = request.execute()

                for member_data in response.get("members", []):
                    member = GroupMember.from_api_response(member_data)
                    if include_external or member.member_type != "EXTERNAL":
                        members.append(member)

                page_token = response.get("nextPageToken")
                if not page_token:
                    break

            logger.debug(f"Retrieved {len(members)} members from group {group_email}")
            return members

        except HttpError as e:
            if e.resp.status == 404:
                logger.warning(f"Group not found: {group_email}")
                return []
            logger.error(f"Failed to get members for group {group_email}: {e}")
            raise

    def is_member_of_group(self, user_email: str, group_email: str) -> bool:
        """
        Check if a user is a member of a group.

        Args:
            user_email: The user's email address
            group_email: The group's email address

        Returns:
            True if the user is a member, False otherwise
        """
        try:
            self.service.members().get(
                groupKey=group_email,
                memberKey=user_email,
            ).execute()
            return True
        except HttpError as e:
            if e.resp.status == 404:
                return False
            logger.error(f"Failed to check membership: {e}")
            raise


def get_workspace_client() -> GoogleWorkspaceClient:
    """
    Create a GoogleWorkspaceClient from Django settings.

    Reads configuration from settings:
    - GOOGLE_WORKSPACE_SERVICE_ACCOUNT_EMAIL
    - GOOGLE_WORKSPACE_DELEGATED_ADMIN
    - GOOGLE_WORKSPACE_DOMAIN
    - GOOGLE_WORKSPACE_SERVICE_ACCOUNT_JSON (optional)
    - GOOGLE_WORKSPACE_WIF_PROVIDER (optional)

    Returns:
        Configured GoogleWorkspaceClient instance

    Raises:
        ImproperlyConfigured: If required settings are missing
    """
    from django.conf import settings
    from django.core.exceptions import ImproperlyConfigured

    service_account_email = getattr(settings, "GOOGLE_WORKSPACE_SERVICE_ACCOUNT_EMAIL", "")
    if not service_account_email:
        raise ImproperlyConfigured(
            "GOOGLE_WORKSPACE_SERVICE_ACCOUNT_EMAIL is required for Google Workspace integration"
        )

    delegated_admin = getattr(settings, "GOOGLE_WORKSPACE_DELEGATED_ADMIN", "")
    if not delegated_admin:
        raise ImproperlyConfigured("GOOGLE_WORKSPACE_DELEGATED_ADMIN is required for Google Workspace integration")

    domain = getattr(settings, "GOOGLE_WORKSPACE_DOMAIN", "")
    if not domain:
        raise ImproperlyConfigured("GOOGLE_WORKSPACE_DOMAIN is required for Google Workspace integration")

    return GoogleWorkspaceClient(
        service_account_email=service_account_email,
        delegated_admin=delegated_admin,
        domain=domain,
        service_account_json=getattr(settings, "GOOGLE_WORKSPACE_SERVICE_ACCOUNT_JSON", ""),
        wif_provider=getattr(settings, "GOOGLE_WORKSPACE_WIF_PROVIDER", ""),
    )
