"""IAM access management utilities for GCP."""

from gcp_platforms_auto.rcache import redis_instance
from redis import Redis
import json
import logging
import os
import google.cloud.logging
from google.cloud import asset_v1
from typing import Optional, Callable

# Initialize Google Cloud Logging
client = google.cloud.logging.Client()
client.setup_logging()

# Configure the logger
logger = logging.getLogger("uvicorn")
logger.setLevel(logging.INFO)

CACHE_EXPIRATION_TIME_SECONDS = 600 # 10 minutes

def check_user_has_role_in_project(
    project_id: str,
    user_email: str,
    organization_id: str,
    role: str = "roles/owner",
    expand_groups: bool = True
) -> bool:
    """
    Check if a user has a specific role in a GCP project.
    
    Args:
        project_id: GCP project ID (e.g., 'sky-starfi-mam-res-gcpro-1')
        user_email: Email of the user to check (e.g., 'oshasha10@gcporg.com')
        organization_id: GCP organization ID (e.g., '111111111111')
        role: Role to check (e.g., 'roles/owner', 'roles/editor')
        expand_groups: Whether to expand group memberships (default: True)
        
    Returns:
        bool: True if the user has the role in the project, False otherwise
        
    Example:
        >>> has_access = check_user_has_role_in_project(
        ...     project_id='sky-starfi-mam-res-gcpro-1',
        ...     user_email='oshasha10@gcporg.com',
        ...     organization_id='111111111111',
        ...     role='roles/owner'
        ... )
    """
    client = asset_v1.AssetServiceClient()
    
    # Construct the full resource name
    scope = f"organizations/{organization_id}"
    full_resource_name = f"//cloudresourcemanager.googleapis.com/projects/{project_id}"
    identity = f"user:{user_email}"
    
    # Build the request
    request = asset_v1.AnalyzeIamPolicyRequest(
        analysis_query=asset_v1.IamPolicyAnalysisQuery(
            scope=scope,
            resource_selector=asset_v1.IamPolicyAnalysisQuery.ResourceSelector(
                full_resource_name=full_resource_name
            ),
            identity_selector=asset_v1.IamPolicyAnalysisQuery.IdentitySelector(
                identity=identity
            ),
            access_selector=asset_v1.IamPolicyAnalysisQuery.AccessSelector(
                roles=[role]
            ),
            options=asset_v1.IamPolicyAnalysisQuery.Options(
                expand_groups=expand_groups,
                expand_roles=True,
            )
        )
    )
    
    try:
        # Execute the analysis
        logger.info(f"Checking if user {user_email} has role {role} in project {project_id}")
        response = client.analyze_iam_policy(request=request)
        
        # Check if any results were returned
        if response.main_analysis and response.main_analysis.analysis_results:
            logger.info(f"User {user_email} has role {role} in project {project_id}")
            return True
        
        logger.info(f"User {user_email} does not have role {role} in project {project_id}")
        return False
        
    except Exception as e:
        logger.exception(f"Error analyzing IAM policy: {e}")
        raise


def check_service_account_has_role_in_project(
    project_id: str,
    service_account_email: str,
    organization_id: str,
    role: str = "roles/owner",
    expand_groups: bool = True
) -> bool:
    """
    Check if a service account has a specific role in a GCP project.
    
    Args:
        project_id: GCP project ID (e.g., 'sky-starfi-mam-res-gcpro-1')
        service_account_email: Email of the service account to check
                              (e.g., 'my-sa@project.iam.gserviceaccount.com')
        organization_id: GCP organization ID (e.g., '111111111111')
        role: Role to check (e.g., 'roles/owner', 'roles/editor')
        expand_groups: Whether to expand group memberships (default: True)
        
    Returns:
        bool: True if the service account has the role in the project, False otherwise
        
    Example:
        >>> has_access = check_service_account_has_role_in_project(
        ...     project_id='sky-starfi-mam-res-gcpro-1',
        ...     service_account_email='my-sa@project.iam.gserviceaccount.com',
        ...     organization_id='111111111111',
        ...     role='roles/owner'
        ... )
    """
    client = asset_v1.AssetServiceClient()
    
    # Construct the full resource name
    scope = f"organizations/{organization_id}"
    full_resource_name = f"//cloudresourcemanager.googleapis.com/projects/{project_id}"
    identity = f"serviceAccount:{service_account_email}"
    
    # Build the request
    request = asset_v1.AnalyzeIamPolicyRequest(
        analysis_query=asset_v1.IamPolicyAnalysisQuery(
            scope=scope,
            resource_selector=asset_v1.IamPolicyAnalysisQuery.ResourceSelector(
                full_resource_name=full_resource_name
            ),
            identity_selector=asset_v1.IamPolicyAnalysisQuery.IdentitySelector(
                identity=identity
            ),
            access_selector=asset_v1.IamPolicyAnalysisQuery.AccessSelector(
                roles=[role]
            ),
            options=asset_v1.IamPolicyAnalysisQuery.Options(
                expand_groups=expand_groups,
                expand_roles=True,
            )
        )
    )
    
    try:
        # Execute the analysis
        logger.info(f"Checking if service account {service_account_email} has role {role} in project {project_id}")
        response = client.analyze_iam_policy(request=request)
        
        # Check if any results were returned
        if response.main_analysis and response.main_analysis.analysis_results:
            logger.info(f"Service account {service_account_email} has role {role} in project {project_id}")
            return True
        
        logger.info(f"Service account {service_account_email} does not have role {role} in project {project_id}")
        return False
        
    except Exception as e:
        logger.exception(f"Error analyzing IAM policy: {e}")
        raise


def check_group_has_role_in_project(
    project_id: str,
    group_email: str,
    organization_id: str,
    role: str = "roles/owner",
    expand_groups: bool = True
) -> bool:
    """
    Check if a group has a specific role in a GCP project.
    
    Args:
        project_id: GCP project ID (e.g., 'sky-starfi-mam-res-gcpro-1')
        group_email: Email of the group to check (e.g., 'dev-team@gcporg.com')
        organization_id: GCP organization ID (e.g., '111111111111')
        role: Role to check (e.g., 'roles/owner', 'roles/editor')
        expand_groups: Whether to expand group memberships (default: True)
        
    Returns:
        bool: True if the group has the role in the project, False otherwise
        
    Example:
        >>> has_access = check_group_has_role_in_project(
        ...     project_id='sky-starfi-mam-res-gcpro-1',
        ...     group_email='dev-team@gcporg.com',
        ...     organization_id='111111111111',
        ...     role='roles/owner'
        ... )
    """
    client = asset_v1.AssetServiceClient()
    
    # Construct the full resource name
    scope = f"organizations/{organization_id}"
    full_resource_name = f"//cloudresourcemanager.googleapis.com/projects/{project_id}"
    identity = f"group:{group_email}"
    
    # Build the request
    request = asset_v1.AnalyzeIamPolicyRequest(
        analysis_query=asset_v1.IamPolicyAnalysisQuery(
            scope=scope,
            resource_selector=asset_v1.IamPolicyAnalysisQuery.ResourceSelector(
                full_resource_name=full_resource_name
            ),
            identity_selector=asset_v1.IamPolicyAnalysisQuery.IdentitySelector(
                identity=identity
            ),
            access_selector=asset_v1.IamPolicyAnalysisQuery.AccessSelector(
                roles=[role]
            ),
            options=asset_v1.IamPolicyAnalysisQuery.Options(
                expand_groups=expand_groups,
                expand_roles=True,
            )
        )
    )
    
    try:
        # Execute the analysis
        logger.info(f"Checking if group {group_email} has role {role} in project {project_id}")
        response = client.analyze_iam_policy(request=request)
        
        # Check if any results were returned
        if response.main_analysis and response.main_analysis.analysis_results:
            logger.info(f"Group {group_email} has role {role} in project {project_id}")
            return True
        
        logger.info(f"Group {group_email} does not have role {role} in project {project_id}")
        return False
        
    except Exception as e:
        logger.exception(f"Error analyzing IAM policy: {e}")
        raise


def _get_all_service_projects(base_paths, prefix):
    service_projects = []

    for path in base_paths:
        logger.info(f"[INFO] Searching for projects under: {path}")

        for root, _, _ in os.walk(path):
            if root == path:
                continue

            for _, _, projects in os.walk(root):
                for project in projects:
                    if not project.endswith(".yaml"):
                        continue
                    if project.startswith(prefix):
                        service_projects.append(project.split('.')[0])
                    else:
                        service_projects.append(f"{prefix}-{project.split('.')[0]}")

    return list(set(service_projects))


def get_projects_with_role(
    user_email: str,
    organization_id: str,
    role: str = "roles/owner",
    expand_groups: bool = True,
    project_prefix: Optional[str] = None,
    projects_base_paths: Optional[list] = [],
    use_cache: bool = True,
) -> list:
    """
    Get all projects where a user or service account has a specific role.
    Searches for all service projects in the organization.
    
    Args:
        user_email: Email of the user or service account to check
                   (e.g., 'oshasha10@gcporg.com' or 'my-sa@project.iam.gserviceaccount.com')
        organization_id: GCP organization ID (e.g., '111111111111')
        role: Role to check (e.g., 'roles/owner', 'roles/editor')
        expand_groups: Whether to expand group memberships (default: True)
        
    Returns:
        list: List of project IDs where the user/service account has the specified role
        
    Example:
        >>> projects = get_projects_with_role(
        ...     user_email='oshasha10@gcporg.com',
        ...     organization_id='111111111111',
        ...     role='roles/owner'
        ... )
    """
    logger.info(f"Fetching all projects in organization {organization_id} (including folders)")

    def _fetch():
        all_projects = _get_all_service_projects(
            base_paths=projects_base_paths,
            prefix=project_prefix
        )

        logger.info(f"Found {len(all_projects)} total service project(s) across organization {organization_id}")

        matching_projects = []
        for project_id in all_projects:
            if check_user_has_role_in_project(
                project_id=project_id,
                user_email=user_email,
                organization_id=organization_id,
                role=role,
                expand_groups=expand_groups
            ):
                matching_projects.append(project_id)

        logger.info(f"Checked {len(all_projects)} projects, found {len(matching_projects)} where {user_email} has role {role}")
        return sorted(matching_projects)

    try:
        return redis_instance.compute_cache_aside(
            key=f"{role}:{user_email}",
            compute_fn=_fetch,
            use_cache=use_cache,
            ex=CACHE_EXPIRATION_TIME_SECONDS
        )
    except Exception as e:
        logger.exception(f"Error fetching projects: {e}")
        raise
