// Package alibaba provides the Alibaba Cloud SSO provider implementation.
// This file contains unit tests for the Alibaba Cloud provider.
//
// Requirements: 3.2, 3.3, 3.4, 3.6
package alibaba

import (
	"context"
	"fmt"
	"testing"

	"AutoSSO/internal/config"
	apperrors "AutoSSO/internal/pkg/errors"
	"AutoSSO/internal/provider"
	"github.com/stretchr/testify/assert"
)

func TestNewAlibabaProvider(t *testing.T) {
	t.Run("nil config returns error", func(t *testing.T) {
		_, err := NewAlibabaProvider(nil)
		if err == nil {
			t.Error("NewAlibabaProvider(nil) should return error")
		}
	})

	t.Run("empty DirectoryID returns error", func(t *testing.T) {
		cfg := &config.AlibabaConfig{

			Endpoint: "cloudsso.cn-shanghai.aliyuncs.com",
		}
		_, err := NewAlibabaProvider(cfg)
		if err == nil {
			t.Error("NewAlibabaProvider with empty DirectoryID should return error")
		}
	})

	t.Run("valid config creates provider", func(t *testing.T) {
		cfg := &config.AlibabaConfig{
			// AccessKeyID/Secret removed
			RoleName:    "test-role",
			DirectoryID: "d-00ts1z0rjv32",
			Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
		}
		p, err := NewAlibabaProvider(cfg)
		if err != nil {
			t.Fatalf("NewAlibabaProvider() error = %v", err)
		}
		if p == nil {
			t.Fatal("NewAlibabaProvider() returned nil provider")
		}
		defer p.Close()

		// Verify provider name
		if p.Name() != "Alibaba" {
			t.Errorf("Name() = %v, want Alibaba", p.Name())
		}
	})

	t.Run("default endpoint is used when not specified", func(t *testing.T) {
		cfg := &config.AlibabaConfig{
			RoleName:    "test-role",
			DirectoryID: "d-00ts1z0rjv32",
			Endpoint:    "", // Empty endpoint should use default
		}
		p, err := NewAlibabaProvider(cfg)
		if err != nil {
			t.Fatalf("NewAlibabaProvider() error = %v", err)
		}
		defer p.Close()

		// Provider should be created successfully even with empty endpoint
		if p == nil {
			t.Fatal("NewAlibabaProvider() returned nil provider")
		}
	})
}

func TestAlibabaProvider_Name(t *testing.T) {
	cfg := &config.AlibabaConfig{
		RoleName:    "test-role",
		DirectoryID: "d-00ts1z0rjv32",
		Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
	}
	p, err := NewAlibabaProvider(cfg)
	if err != nil {
		t.Fatalf("NewAlibabaProvider() error = %v", err)
	}
	defer p.Close()

	if p.Name() != string(provider.ProviderAlibaba) {
		t.Errorf("Name() = %v, want %v", p.Name(), provider.ProviderAlibaba)
	}
}

func TestAlibabaProvider_Close(t *testing.T) {
	cfg := &config.AlibabaConfig{
		RoleName:    "test-role",
		DirectoryID: "d-00ts1z0rjv32",
		Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
	}
	p, err := NewAlibabaProvider(cfg)
	if err != nil {
		t.Fatalf("NewAlibabaProvider() error = %v", err)
	}

	// First close should succeed
	err = p.Close()
	if err != nil {
		t.Errorf("Close() error = %v", err)
	}

	// Second close should also succeed (idempotent)
	err = p.Close()
	if err != nil {
		t.Errorf("Second Close() error = %v", err)
	}
}

func TestAlibabaProvider_AddPermission_Validation(t *testing.T) {
	cfg := &config.AlibabaConfig{
		RoleName:    "test-role",
		DirectoryID: "d-00ts1z0rjv32",
		Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
	}
	p, err := NewAlibabaProvider(cfg)
	if err != nil {
		t.Fatalf("NewAlibabaProvider() error = %v", err)
	}
	defer p.Close()

	ctx := context.Background()

	t.Run("nil request returns error", func(t *testing.T) {
		err := p.AddPermission(ctx, nil)
		if err == nil {
			t.Error("AddPermission(nil) should return error")
		}
	})

	t.Run("empty account ID returns error", func(t *testing.T) {
		req := &provider.PermissionRequest{
			AccountID:      "",
			UserEmail:      "user@example.com",
			PermissionSets: []string{"ReadOnly"},
		}
		err := p.AddPermission(ctx, req)
		if err == nil {
			t.Error("AddPermission with empty AccountID should return error")
		}
	})

	t.Run("empty user email returns error", func(t *testing.T) {
		req := &provider.PermissionRequest{
			AccountID:      "5480039859161595",
			UserEmail:      "",
			PermissionSets: []string{"ReadOnly"},
		}
		err := p.AddPermission(ctx, req)
		if err == nil {
			t.Error("AddPermission with empty UserEmail should return error")
		}
	})

	t.Run("empty permission sets returns error", func(t *testing.T) {
		req := &provider.PermissionRequest{
			AccountID:      "5480039859161595",
			UserEmail:      "user@example.com",
			PermissionSets: []string{},
		}
		err := p.AddPermission(ctx, req)
		if err == nil {
			t.Error("AddPermission with empty PermissionSets should return error")
		}
	})
}

func TestAlibabaProvider_RemovePermission_Validation(t *testing.T) {
	cfg := &config.AlibabaConfig{
		RoleName:    "test-role",
		DirectoryID: "d-00ts1z0rjv32",
		Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
	}
	p, err := NewAlibabaProvider(cfg)
	if err != nil {
		t.Fatalf("NewAlibabaProvider() error = %v", err)
	}
	defer p.Close()

	ctx := context.Background()

	t.Run("nil request returns error", func(t *testing.T) {
		err := p.RemovePermission(ctx, nil)
		if err == nil {
			t.Error("RemovePermission(nil) should return error")
		}
	})

	t.Run("empty account ID returns error", func(t *testing.T) {
		req := &provider.PermissionRequest{
			AccountID:      "",
			UserEmail:      "user@example.com",
			PermissionSets: []string{"ReadOnly"},
		}
		err := p.RemovePermission(ctx, req)
		if err == nil {
			t.Error("RemovePermission with empty AccountID should return error")
		}
	})

	t.Run("empty user email returns error", func(t *testing.T) {
		req := &provider.PermissionRequest{
			AccountID:      "5480039859161595",
			UserEmail:      "",
			PermissionSets: []string{"ReadOnly"},
		}
		err := p.RemovePermission(ctx, req)
		if err == nil {
			t.Error("RemovePermission with empty UserEmail should return error")
		}
	})

	t.Run("empty permission sets returns error", func(t *testing.T) {
		req := &provider.PermissionRequest{
			AccountID:      "5480039859161595",
			UserEmail:      "user@example.com",
			PermissionSets: []string{},
		}
		err := p.RemovePermission(ctx, req)
		if err == nil {
			t.Error("RemovePermission with empty PermissionSets should return error")
		}
	})
}

func TestAlibabaProvider_GetUserID_Validation(t *testing.T) {
	cfg := &config.AlibabaConfig{
		RoleName:    "test-role",
		DirectoryID: "d-00ts1z0rjv32",
		Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
	}
	p, err := NewAlibabaProvider(cfg)
	if err != nil {
		t.Fatalf("NewAlibabaProvider() error = %v", err)
	}
	defer p.Close()

	ctx := context.Background()

	t.Run("empty email returns error", func(t *testing.T) {
		_, err := p.GetUserID(ctx, "")
		if err == nil {
			t.Error("GetUserID with empty email should return error")
		}
	})
}

func TestAlibabaProvider_WrapProviderError_InvalidServiceStatus(t *testing.T) {
	cfg := &config.AlibabaConfig{
		RoleName:    "test-role",
		DirectoryID: "d-00ts1z0rjv32",
		Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
	}

	p, err := NewAlibabaProvider(cfg)
	if err != nil {
		t.Fatalf("NewAlibabaProvider() error = %v", err)
	}
	defer p.Close()

	wrapped := p.wrapProviderError("GetUserID", assert.AnError)
	assert.True(t, apperrors.IsProviderError(wrapped))

	providerErr, ok := apperrors.GetProviderError(wrapped)
	assert.True(t, ok)
	assert.Equal(t, string(provider.ProviderAlibaba), providerErr.Provider)
	assert.Equal(t, "GetUserID", providerErr.Op)

	invalidStatus := p.wrapProviderError("GetUserID",
		fmt.Errorf("failed to list users: SDKError: Code: InvalidServiceStatus Message: Cloud SSO is not activated"))
	assert.True(t, apperrors.IsProviderError(invalidStatus))
	assert.Contains(t, invalidStatus.Error(), "Alibaba Cloud SSO is not activated")
	assert.Contains(t, invalidStatus.Error(), cfg.DirectoryID)
	assert.Contains(t, invalidStatus.Error(), "enable Cloud SSO before retrying")
}

func TestAlibabaProvider_ClosedProvider(t *testing.T) {
	cfg := &config.AlibabaConfig{
		RoleName:    "test-role",
		DirectoryID: "d-00ts1z0rjv32",
		Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
	}
	p, err := NewAlibabaProvider(cfg)
	if err != nil {
		t.Fatalf("NewAlibabaProvider() error = %v", err)
	}

	// Close the provider
	p.Close()

	ctx := context.Background()

	t.Run("AddPermission on closed provider returns error", func(t *testing.T) {
		req := &provider.PermissionRequest{
			AccountID:      "5480039859161595",
			UserEmail:      "user@example.com",
			PermissionSets: []string{"ReadOnly"},
		}
		err := p.AddPermission(ctx, req)
		if err == nil {
			t.Error("AddPermission on closed provider should return error")
		}
	})

	t.Run("RemovePermission on closed provider returns error", func(t *testing.T) {
		req := &provider.PermissionRequest{
			AccountID:      "5480039859161595",
			UserEmail:      "user@example.com",
			PermissionSets: []string{"ReadOnly"},
		}
		err := p.RemovePermission(ctx, req)
		if err == nil {
			t.Error("RemovePermission on closed provider should return error")
		}
	})

	t.Run("GetUserID on closed provider returns error", func(t *testing.T) {
		_, err := p.GetUserID(ctx, "user@example.com")
		if err == nil {
			t.Error("GetUserID on closed provider should return error")
		}
	})

	t.Run("ValidateCredentials on closed provider returns error", func(t *testing.T) {
		err := p.ValidateCredentials(ctx)
		if err == nil {
			t.Error("ValidateCredentials on closed provider should return error")
		}
	})
}

func TestAlibabaProvider_ImplementsInterface(t *testing.T) {
	cfg := &config.AlibabaConfig{
		RoleName:    "test-role",
		DirectoryID: "d-00ts1z0rjv32",
		Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
	}
	p, err := NewAlibabaProvider(cfg)
	if err != nil {
		t.Fatalf("NewAlibabaProvider() error = %v", err)
	}
	defer p.Close()

	// Verify that AlibabaProvider implements the Provider interface
	var _ provider.Provider = p
}

func TestAlibabaProvider_GetConfig(t *testing.T) {
	cfg := &config.AlibabaConfig{
		RoleName:    "test-role",
		DirectoryID: "d-00ts1z0rjv32",
		Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
		Accounts: []config.CloudAccount{
			{ID: "5480039859161595", Name: "alibaba-prod-1", Type: "prod"},
			{ID: "5108739859105002", Name: "alibaba-prod-2", Type: "prod"},
		},
	}
	p, err := NewAlibabaProvider(cfg)
	if err != nil {
		t.Fatalf("NewAlibabaProvider() error = %v", err)
	}
	defer p.Close()

	// Verify GetConfig returns the same config
	gotCfg := p.GetConfig()
	if gotCfg != cfg {
		t.Error("GetConfig() should return the same config")
	}

	// Verify config values

	if gotCfg.DirectoryID != "d-00ts1z0rjv32" {
		t.Errorf("GetConfig().DirectoryID = %v, want d-00ts1z0rjv32", gotCfg.DirectoryID)
	}
	if len(gotCfg.Accounts) != 2 {
		t.Errorf("GetConfig().Accounts length = %v, want 2", len(gotCfg.Accounts))
	}
}

func TestAlibabaProvider_DirectMethods_Validation(t *testing.T) {
	cfg := &config.AlibabaConfig{
		RoleName:    "test-role",
		DirectoryID: "d-00ts1z0rjv32",
		Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
	}
	p, err := NewAlibabaProvider(cfg)
	if err != nil {
		t.Fatalf("NewAlibabaProvider() error = %v", err)
	}
	defer p.Close()

	ctx := context.Background()

	t.Run("AssignPermissionDirect with empty accountID returns error", func(t *testing.T) {
		err := p.AssignPermissionDirect(ctx, "", "user@example.com", "ReadOnly")
		if err == nil {
			t.Error("AssignPermissionDirect with empty accountID should return error")
		}
	})

	t.Run("AssignPermissionDirect with empty userEmail returns error", func(t *testing.T) {
		err := p.AssignPermissionDirect(ctx, "5480039859161595", "", "ReadOnly")
		if err == nil {
			t.Error("AssignPermissionDirect with empty userEmail should return error")
		}
	})

	t.Run("AssignPermissionDirect with empty permissionName returns error", func(t *testing.T) {
		err := p.AssignPermissionDirect(ctx, "5480039859161595", "user@example.com", "")
		if err == nil {
			t.Error("AssignPermissionDirect with empty permissionName should return error")
		}
	})

	t.Run("RemovePermissionDirect with empty accountID returns error", func(t *testing.T) {
		err := p.RemovePermissionDirect(ctx, "", "user@example.com", "ReadOnly")
		if err == nil {
			t.Error("RemovePermissionDirect with empty accountID should return error")
		}
	})

	t.Run("RemovePermissionDirect with empty userEmail returns error", func(t *testing.T) {
		err := p.RemovePermissionDirect(ctx, "5480039859161595", "", "ReadOnly")
		if err == nil {
			t.Error("RemovePermissionDirect with empty userEmail should return error")
		}
	})

	t.Run("RemovePermissionDirect with empty permissionName returns error", func(t *testing.T) {
		err := p.RemovePermissionDirect(ctx, "5480039859161595", "user@example.com", "")
		if err == nil {
			t.Error("RemovePermissionDirect with empty permissionName should return error")
		}
	})
}

func TestAlibabaProvider_DirectMethods_ClosedProvider(t *testing.T) {
	cfg := &config.AlibabaConfig{
		RoleName:    "test-role",
		DirectoryID: "d-00ts1z0rjv32",
		Endpoint:    "cloudsso.cn-shanghai.aliyuncs.com",
	}
	p, err := NewAlibabaProvider(cfg)
	if err != nil {
		t.Fatalf("NewAlibabaProvider() error = %v", err)
	}

	// Close the provider
	p.Close()

	ctx := context.Background()

	t.Run("AssignPermissionDirect on closed provider returns error", func(t *testing.T) {
		err := p.AssignPermissionDirect(ctx, "5480039859161595", "user@example.com", "ReadOnly")
		if err == nil {
			t.Error("AssignPermissionDirect on closed provider should return error")
		}
	})

	t.Run("RemovePermissionDirect on closed provider returns error", func(t *testing.T) {
		err := p.RemovePermissionDirect(ctx, "5480039859161595", "user@example.com", "ReadOnly")
		if err == nil {
			t.Error("RemovePermissionDirect on closed provider should return error")
		}
	})
}
