// Package aws provides the AWS SSO (IAM Identity Center) provider implementation.
// This file contains the permission management methods.
//
// Requirements: 3.2, 3.3, 3.4, 3.6
package aws

import (
	"context"
	"fmt"
	"strings"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/identitystore"
	"github.com/aws/aws-sdk-go/service/ssoadmin"

	"AutoSSO/internal/pkg/logger"
	"AutoSSO/internal/provider"
)

// AddPermission adds permission for a user in AWS SSO.
// It creates an account assignment for each permission set in the request.
//
// Parameters:
//   - ctx: Context for cancellation and timeout
//   - req: Permission request containing account ID, user email, and permission sets
//
// Returns:
//   - error: Any error that occurred during the operation
//
// Requirements: 3.2
func (p *AWSProvider) AddPermission(ctx context.Context, req *provider.PermissionRequest) error {
	p.closeMu.RLock()
	if p.closed {
		p.closeMu.RUnlock()
		return fmt.Errorf("provider is closed")
	}
	p.closeMu.RUnlock()

	if req == nil {
		return fmt.Errorf("permission request cannot be nil")
	}

	if req.AccountID == "" {
		return fmt.Errorf("account ID is required")
	}

	if req.UserEmail == "" {
		return fmt.Errorf("user email is required")
	}

	if len(req.PermissionSets) == 0 {
		return fmt.Errorf("at least one permission set is required")
	}

	// Get the user ID from email
	userID, err := p.GetUserID(ctx, req.UserEmail)
	if err != nil {
		return fmt.Errorf("failed to get user ID for %s: %w", req.UserEmail, err)
	}

	if userID == "" {
		return fmt.Errorf("user not found: %s", req.UserEmail)
	}

	// Add each permission set
	var errs []string
	for _, permissionSetName := range req.PermissionSets {
		// Wait before operation to avoid AWS SDK conflicts
		if err := p.waitBeforeOperation(ctx); err != nil {
			return err
		}

		// Get the permission set ARN
		permissionSetARN, err := p.getPermissionSetARN(ctx, permissionSetName)
		if err != nil {
			errs = append(errs, fmt.Sprintf("failed to get permission set ARN for %s: %v", permissionSetName, err))
			continue
		}

		// Create the account assignment
		err = p.createAccountAssignment(ctx, req.AccountID, userID, permissionSetARN)
		if err != nil {
			errs = append(errs, fmt.Sprintf("failed to add permission %s: %v", permissionSetName, err))
			continue
		}
	}

	if len(errs) > 0 {
		return fmt.Errorf("failed to add permissions: %s", strings.Join(errs, "; "))
	}

	return nil
}

// RemovePermission removes permission from a user in AWS SSO.
// It deletes the account assignment for each permission set in the request.
//
// Parameters:
//   - ctx: Context for cancellation and timeout
//   - req: Permission request containing account ID, user email, and permission sets
//
// Returns:
//   - error: Any error that occurred during the operation
//
// Requirements: 3.3
func (p *AWSProvider) RemovePermission(ctx context.Context, req *provider.PermissionRequest) error {
	p.closeMu.RLock()
	if p.closed {
		p.closeMu.RUnlock()
		return fmt.Errorf("provider is closed")
	}
	p.closeMu.RUnlock()

	if req == nil {
		return fmt.Errorf("permission request cannot be nil")
	}

	if req.AccountID == "" {
		return fmt.Errorf("account ID is required")
	}

	if req.UserEmail == "" {
		return fmt.Errorf("user email is required")
	}

	if len(req.PermissionSets) == 0 {
		return fmt.Errorf("at least one permission set is required")
	}

	// Get the user ID from email
	userID, err := p.GetUserID(ctx, req.UserEmail)
	if err != nil {
		return fmt.Errorf("failed to get user ID for %s: %w", req.UserEmail, err)
	}

	if userID == "" {
		return fmt.Errorf("user not found: %s", req.UserEmail)
	}

	// Remove each permission set
	var lastErr error
	for _, permissionSetName := range req.PermissionSets {
		// Wait before operation to avoid AWS SDK conflicts
		if err := p.waitBeforeOperation(ctx); err != nil {
			return err
		}

		// Get the permission set ARN
		permissionSetARN, err := p.getPermissionSetARN(ctx, permissionSetName)
		if err != nil {
			lastErr = fmt.Errorf("failed to get permission set ARN for %s: %w", permissionSetName, err)
			continue
		}

		// Delete the account assignment
		err = p.deleteAccountAssignment(ctx, req.AccountID, userID, permissionSetARN)
		if err != nil {
			lastErr = fmt.Errorf("failed to remove permission %s: %w", permissionSetName, err)
			continue
		}
	}

	return lastErr
}

// GetUserID retrieves the AWS SSO user ID for a given email address.
// It searches through the Identity Store to find the user.
//
// Parameters:
//   - ctx: Context for cancellation and timeout
//   - email: User's email address
//
// Returns:
//   - string: The AWS SSO user ID
//   - error: Any error that occurred during the lookup
func (p *AWSProvider) GetUserID(ctx context.Context, email string) (string, error) {
	p.closeMu.RLock()
	if p.closed {
		p.closeMu.RUnlock()
		return "", fmt.Errorf("provider is closed")
	}
	p.closeMu.RUnlock()

	if email == "" {
		return "", fmt.Errorf("email is required")
	}

	// Normalize the email
	email = strings.TrimSpace(email)

	// List users and find the matching one
	input := &identitystore.ListUsersInput{
		IdentityStoreId: aws.String(p.config.IdentityStoreID),
		MaxResults:      aws.Int64(100),
	}

	for {
		resp, err := p.identityStoreClient.ListUsersWithContext(ctx, input)
		if err != nil {
			return "", fmt.Errorf("failed to list users: %w", err)
		}

		for _, user := range resp.Users {
			if user.UserName != nil && strings.TrimSpace(*user.UserName) == email {
				if user.UserId != nil {
					return strings.TrimSpace(*user.UserId), nil
				}
			}
		}

		// Check for more results
		if resp.NextToken != nil {
			input.NextToken = resp.NextToken
		} else {
			break
		}
	}

	return "", nil
}

// createAccountAssignment creates an account assignment for a user.
// This grants the user access to the specified account with the specified permission set.
//
// Parameters:
//   - ctx: Context for cancellation and timeout
//   - accountID: The AWS account ID
//   - userID: The AWS SSO user ID
//   - permissionSetARN: The ARN of the permission set
//
// Returns:
//   - error: Any error that occurred during the operation
func (p *AWSProvider) createAccountAssignment(ctx context.Context, accountID, userID, permissionSetARN string) error {
	input := &ssoadmin.CreateAccountAssignmentInput{
		InstanceArn:      aws.String(p.config.IdentityCenterARN),
		TargetId:         aws.String(accountID),
		TargetType:       aws.String("AWS_ACCOUNT"),
		PermissionSetArn: aws.String(permissionSetARN),
		PrincipalType:    aws.String("USER"),
		PrincipalId:      aws.String(userID),
	}

	if p.logger != nil {
		p.logger.Info("Calling CreateAccountAssignment",
			logger.String("instance_arn", *input.InstanceArn),
			logger.String("target_id", *input.TargetId),
			logger.String("target_type", *input.TargetType),
			logger.String("permission_set_arn", *input.PermissionSetArn),
			logger.String("principal_type", *input.PrincipalType),
			logger.String("principal_id", *input.PrincipalId),
		)
	}

	_, err := p.ssoAdminClient.CreateAccountAssignmentWithContext(ctx, input)
	if err != nil {
		return fmt.Errorf("failed to create account assignment: %w", err)
	}

	return nil
}

// deleteAccountAssignment deletes an account assignment for a user.
// This revokes the user's access to the specified account with the specified permission set.
//
// Parameters:
//   - ctx: Context for cancellation and timeout
//   - accountID: The AWS account ID
//   - userID: The AWS SSO user ID
//   - permissionSetARN: The ARN of the permission set
//
// Returns:
//   - error: Any error that occurred during the operation
func (p *AWSProvider) deleteAccountAssignment(ctx context.Context, accountID, userID, permissionSetARN string) error {
	input := &ssoadmin.DeleteAccountAssignmentInput{
		InstanceArn:      aws.String(p.config.IdentityCenterARN),
		TargetId:         aws.String(accountID),
		TargetType:       aws.String("AWS_ACCOUNT"),
		PermissionSetArn: aws.String(permissionSetARN),
		PrincipalType:    aws.String("USER"),
		PrincipalId:      aws.String(userID),
	}

	if p.logger != nil {
		p.logger.Info("Calling DeleteAccountAssignment",
			logger.String("instance_arn", *input.InstanceArn),
			logger.String("target_id", *input.TargetId),
			logger.String("target_type", *input.TargetType),
			logger.String("permission_set_arn", *input.PermissionSetArn),
			logger.String("principal_type", *input.PrincipalType),
			logger.String("principal_id", *input.PrincipalId),
		)
	}

	_, err := p.ssoAdminClient.DeleteAccountAssignmentWithContext(ctx, input)
	if err != nil {
		return fmt.Errorf("failed to delete account assignment: %w", err)
	}

	return nil
}
