package provider

import (
	"context"
	"errors"
	"testing"

	"AutoSSO/internal/config"
)

// mockProvider is a mock implementation of the Provider interface for testing.
type mockProvider struct {
	name   string
	closed bool
}

func newMockProvider(name string) *mockProvider {
	return &mockProvider{name: name}
}

func (m *mockProvider) Name() string {
	return m.name
}

func (m *mockProvider) AddPermission(ctx context.Context, req *PermissionRequest) error {
	return nil
}

func (m *mockProvider) RemovePermission(ctx context.Context, req *PermissionRequest) error {
	return nil
}

func (m *mockProvider) GetUserID(ctx context.Context, email string) (string, error) {
	return "user-123", nil
}

func (m *mockProvider) ValidateCredentials(ctx context.Context) error {
	return nil
}

func (m *mockProvider) Close() error {
	m.closed = true
	return nil
}

// errorProvider is a mock provider that returns errors on Close.
type errorProvider struct {
	mockProvider
}

func (e *errorProvider) Close() error {
	return errors.New("close error")
}

func TestProviderType_String(t *testing.T) {
	tests := []struct {
		pt       ProviderType
		expected string
	}{
		{ProviderAWS, "AWS"},
		{ProviderGCP, "GCP"},
		{ProviderAzure, "Azure"},
		{ProviderAlibaba, "Alibaba"},
	}

	for _, tt := range tests {
		t.Run(string(tt.pt), func(t *testing.T) {
			if got := tt.pt.String(); got != tt.expected {
				t.Errorf("ProviderType.String() = %v, want %v", got, tt.expected)
			}
		})
	}
}

func TestProviderType_IsValid(t *testing.T) {
	tests := []struct {
		pt       ProviderType
		expected bool
	}{
		{ProviderAWS, true},
		{ProviderGCP, true},
		{ProviderAzure, true},
		{ProviderAlibaba, true},
		{ProviderType("Unknown"), false},
		{ProviderType(""), false},
	}

	for _, tt := range tests {
		t.Run(string(tt.pt), func(t *testing.T) {
			if got := tt.pt.IsValid(); got != tt.expected {
				t.Errorf("ProviderType.IsValid() = %v, want %v", got, tt.expected)
			}
		})
	}
}

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

	t.Run("empty config creates empty registry", func(t *testing.T) {
		cfg := &config.Config{}
		r, err := NewRegistry(cfg)
		if err != nil {
			t.Fatalf("NewRegistry() error = %v", err)
		}
		if r.AccountCount() != 0 {
			t.Errorf("AccountCount() = %v, want 0", r.AccountCount())
		}
	})

	t.Run("config with accounts creates mappings", func(t *testing.T) {
		cfg := &config.Config{
			Providers: config.ProvidersConfig{
				AWS: config.AWSConfig{
					Accounts: []config.CloudAccount{
						{ID: "aws-account-1", Name: "AWS Account 1"},
						{ID: "aws-account-2", Name: "AWS Account 2"},
					},
				},
				GCP: config.GCPConfig{
					Projects: []config.CloudAccount{
						{ID: "gcp-project-1", Name: "GCP Project 1"},
					},
				},
				Azure: config.AzureConfig{
					Subscriptions: []config.CloudAccount{
						{ID: "azure-sub-1", Name: "azure-subscription-1"},
					},
				},
				Alibaba: config.AlibabaConfig{
					Accounts: []config.CloudAccount{
						{ID: "alibaba-account-1", Name: "Alibaba Account 1"},
					},
				},
			},
		}

		r, err := NewRegistry(cfg)
		if err != nil {
			t.Fatalf("NewRegistry() error = %v", err)
		}

		// AWS accounts: 2
		// GCP projects: 1
		// Azure subscriptions: 1 (ID) + 1 (Name) = 2 mappings
		// Alibaba accounts: 1
		// Total: 6 mappings
		expectedCount := 6
		if r.AccountCount() != expectedCount {
			t.Errorf("AccountCount() = %v, want %v", r.AccountCount(), expectedCount)
		}

		// Verify AWS account mapping
		pt, ok := r.GetProviderType("aws-account-1")
		if !ok || pt != ProviderAWS {
			t.Errorf("GetProviderType(aws-account-1) = %v, %v, want AWS, true", pt, ok)
		}

		// Verify GCP project mapping
		pt, ok = r.GetProviderType("gcp-project-1")
		if !ok || pt != ProviderGCP {
			t.Errorf("GetProviderType(gcp-project-1) = %v, %v, want GCP, true", pt, ok)
		}

		// Verify Azure subscription mapping by ID
		pt, ok = r.GetProviderType("azure-sub-1")
		if !ok || pt != ProviderAzure {
			t.Errorf("GetProviderType(azure-sub-1) = %v, %v, want Azure, true", pt, ok)
		}

		// Verify Azure subscription mapping by Name
		pt, ok = r.GetProviderType("azure-subscription-1")
		if !ok || pt != ProviderAzure {
			t.Errorf("GetProviderType(azure-subscription-1) = %v, %v, want Azure, true", pt, ok)
		}

		// Verify Alibaba account mapping
		pt, ok = r.GetProviderType("alibaba-account-1")
		if !ok || pt != ProviderAlibaba {
			t.Errorf("GetProviderType(alibaba-account-1) = %v, %v, want Alibaba, true", pt, ok)
		}
	})

	t.Run("empty account IDs are skipped", func(t *testing.T) {
		cfg := &config.Config{
			Providers: config.ProvidersConfig{
				AWS: config.AWSConfig{
					Accounts: []config.CloudAccount{
						{ID: "", Name: "Empty ID Account"},
						{ID: "valid-account", Name: "Valid Account"},
					},
				},
			},
		}

		r, err := NewRegistry(cfg)
		if err != nil {
			t.Fatalf("NewRegistry() error = %v", err)
		}

		if r.AccountCount() != 1 {
			t.Errorf("AccountCount() = %v, want 1", r.AccountCount())
		}

		_, ok := r.GetProviderType("")
		if ok {
			t.Error("Empty account ID should not be registered")
		}
	})
}

func TestRegistry_RegisterProvider(t *testing.T) {
	cfg := &config.Config{}
	r, _ := NewRegistry(cfg)

	t.Run("register valid provider", func(t *testing.T) {
		p := newMockProvider("AWS")
		err := r.RegisterProvider(ProviderAWS, p)
		if err != nil {
			t.Errorf("RegisterProvider() error = %v", err)
		}
		if r.ProviderCount() != 1 {
			t.Errorf("ProviderCount() = %v, want 1", r.ProviderCount())
		}
	})

	t.Run("register duplicate provider returns error", func(t *testing.T) {
		p := newMockProvider("AWS")
		err := r.RegisterProvider(ProviderAWS, p)
		if err == nil {
			t.Error("RegisterProvider() should return error for duplicate")
		}
	})

	t.Run("register invalid provider type returns error", func(t *testing.T) {
		p := newMockProvider("Unknown")
		err := r.RegisterProvider(ProviderType("Unknown"), p)
		if err == nil {
			t.Error("RegisterProvider() should return error for invalid type")
		}
	})

	t.Run("register nil provider returns error", func(t *testing.T) {
		err := r.RegisterProvider(ProviderGCP, nil)
		if err == nil {
			t.Error("RegisterProvider() should return error for nil provider")
		}
	})
}

func TestRegistry_GetProvider(t *testing.T) {
	cfg := &config.Config{
		Providers: config.ProvidersConfig{
			AWS: config.AWSConfig{
				Accounts: []config.CloudAccount{
					{ID: "aws-account-1"},
				},
			},
		},
	}
	r, _ := NewRegistry(cfg)

	awsProvider := newMockProvider("AWS")
	_ = r.RegisterProvider(ProviderAWS, awsProvider)

	t.Run("get provider for known account", func(t *testing.T) {
		p, err := r.GetProvider("aws-account-1")
		if err != nil {
			t.Errorf("GetProvider() error = %v", err)
		}
		if p != awsProvider {
			t.Error("GetProvider() returned wrong provider")
		}
	})

	t.Run("get provider for unknown account returns error", func(t *testing.T) {
		_, err := r.GetProvider("unknown-account")
		if err == nil {
			t.Error("GetProvider() should return error for unknown account")
		}
	})

	t.Run("get provider when provider not registered returns error", func(t *testing.T) {
		cfg2 := &config.Config{
			Providers: config.ProvidersConfig{
				GCP: config.GCPConfig{
					Projects: []config.CloudAccount{
						{ID: "gcp-project-1"},
					},
				},
			},
		}
		r2, _ := NewRegistry(cfg2)
		// GCP provider not registered
		_, err := r2.GetProvider("gcp-project-1")
		if err == nil {
			t.Error("GetProvider() should return error when provider not registered")
		}
	})
}

func TestRegistry_GetProviderByType(t *testing.T) {
	cfg := &config.Config{}
	r, _ := NewRegistry(cfg)

	awsProvider := newMockProvider("AWS")
	_ = r.RegisterProvider(ProviderAWS, awsProvider)

	t.Run("get registered provider by type", func(t *testing.T) {
		p, err := r.GetProviderByType(ProviderAWS)
		if err != nil {
			t.Errorf("GetProviderByType() error = %v", err)
		}
		if p != awsProvider {
			t.Error("GetProviderByType() returned wrong provider")
		}
	})

	t.Run("get unregistered provider by type returns error", func(t *testing.T) {
		_, err := r.GetProviderByType(ProviderGCP)
		if err == nil {
			t.Error("GetProviderByType() should return error for unregistered type")
		}
	})
}

func TestRegistry_GetProviderType(t *testing.T) {
	cfg := &config.Config{
		Providers: config.ProvidersConfig{
			AWS: config.AWSConfig{
				Accounts: []config.CloudAccount{
					{ID: "aws-account-1"},
				},
			},
		},
	}
	r, _ := NewRegistry(cfg)

	t.Run("get provider type for known account", func(t *testing.T) {
		pt, ok := r.GetProviderType("aws-account-1")
		if !ok {
			t.Error("GetProviderType() should return true for known account")
		}
		if pt != ProviderAWS {
			t.Errorf("GetProviderType() = %v, want AWS", pt)
		}
	})

	t.Run("get provider type for unknown account", func(t *testing.T) {
		_, ok := r.GetProviderType("unknown-account")
		if ok {
			t.Error("GetProviderType() should return false for unknown account")
		}
	})
}

func TestRegistry_ListAccounts(t *testing.T) {
	cfg := &config.Config{
		Providers: config.ProvidersConfig{
			AWS: config.AWSConfig{
				Accounts: []config.CloudAccount{
					{ID: "aws-account-1"},
					{ID: "aws-account-2"},
				},
			},
			GCP: config.GCPConfig{
				Projects: []config.CloudAccount{
					{ID: "gcp-project-1"},
				},
			},
		},
	}
	r, _ := NewRegistry(cfg)

	t.Run("list AWS accounts", func(t *testing.T) {
		accounts := r.ListAccounts(ProviderAWS)
		if len(accounts) != 2 {
			t.Errorf("ListAccounts(AWS) returned %d accounts, want 2", len(accounts))
		}
	})

	t.Run("list GCP accounts", func(t *testing.T) {
		accounts := r.ListAccounts(ProviderGCP)
		if len(accounts) != 1 {
			t.Errorf("ListAccounts(GCP) returned %d accounts, want 1", len(accounts))
		}
	})

	t.Run("list accounts for unregistered type", func(t *testing.T) {
		accounts := r.ListAccounts(ProviderAzure)
		if len(accounts) != 0 {
			t.Errorf("ListAccounts(Azure) returned %d accounts, want 0", len(accounts))
		}
	})
}

func TestRegistry_ListProviderTypes(t *testing.T) {
	cfg := &config.Config{}
	r, _ := NewRegistry(cfg)

	t.Run("empty registry", func(t *testing.T) {
		types := r.ListProviderTypes()
		if len(types) != 0 {
			t.Errorf("ListProviderTypes() returned %d types, want 0", len(types))
		}
	})

	t.Run("with registered providers", func(t *testing.T) {
		_ = r.RegisterProvider(ProviderAWS, newMockProvider("AWS"))
		_ = r.RegisterProvider(ProviderGCP, newMockProvider("GCP"))

		types := r.ListProviderTypes()
		if len(types) != 2 {
			t.Errorf("ListProviderTypes() returned %d types, want 2", len(types))
		}
	})
}

func TestRegistry_Close(t *testing.T) {
	t.Run("close all providers", func(t *testing.T) {
		cfg := &config.Config{}
		r, _ := NewRegistry(cfg)

		awsProvider := newMockProvider("AWS")
		gcpProvider := newMockProvider("GCP")
		_ = r.RegisterProvider(ProviderAWS, awsProvider)
		_ = r.RegisterProvider(ProviderGCP, gcpProvider)

		err := r.Close()
		if err != nil {
			t.Errorf("Close() error = %v", err)
		}

		if !awsProvider.closed {
			t.Error("AWS provider should be closed")
		}
		if !gcpProvider.closed {
			t.Error("GCP provider should be closed")
		}

		// Registry should be cleared
		if r.ProviderCount() != 0 {
			t.Errorf("ProviderCount() after Close() = %v, want 0", r.ProviderCount())
		}
		if r.AccountCount() != 0 {
			t.Errorf("AccountCount() after Close() = %v, want 0", r.AccountCount())
		}
	})

	t.Run("close returns first error", func(t *testing.T) {
		cfg := &config.Config{}
		r, _ := NewRegistry(cfg)

		_ = r.RegisterProvider(ProviderAWS, &errorProvider{mockProvider: mockProvider{name: "AWS"}})

		err := r.Close()
		if err == nil {
			t.Error("Close() should return error when provider close fails")
		}
	})
}

func TestRegistry_Counts(t *testing.T) {
	cfg := &config.Config{
		Providers: config.ProvidersConfig{
			AWS: config.AWSConfig{
				Accounts: []config.CloudAccount{
					{ID: "aws-1"},
					{ID: "aws-2"},
				},
			},
		},
	}
	r, _ := NewRegistry(cfg)

	if r.AccountCount() != 2 {
		t.Errorf("AccountCount() = %v, want 2", r.AccountCount())
	}

	if r.ProviderCount() != 0 {
		t.Errorf("ProviderCount() = %v, want 0", r.ProviderCount())
	}

	_ = r.RegisterProvider(ProviderAWS, newMockProvider("AWS"))

	if r.ProviderCount() != 1 {
		t.Errorf("ProviderCount() = %v, want 1", r.ProviderCount())
	}
}

func TestPermissionRequest(t *testing.T) {
	req := &PermissionRequest{
		AccountID:      "account-123",
		UserEmail:      "user@example.com",
		PermissionSets: []string{"ReadOnly", "Admin"},
	}

	if req.AccountID != "account-123" {
		t.Errorf("AccountID = %v, want account-123", req.AccountID)
	}
	if req.UserEmail != "user@example.com" {
		t.Errorf("UserEmail = %v, want user@example.com", req.UserEmail)
	}
	if len(req.PermissionSets) != 2 {
		t.Errorf("PermissionSets length = %v, want 2", len(req.PermissionSets))
	}
}

func TestPermissionResult(t *testing.T) {
	t.Run("successful result", func(t *testing.T) {
		result := &PermissionResult{
			Success:       true,
			PermissionSet: "ReadOnly",
			Error:         nil,
		}

		if !result.Success {
			t.Error("Success should be true")
		}
		if result.Error != nil {
			t.Error("Error should be nil for successful result")
		}
	})

	t.Run("failed result", func(t *testing.T) {
		err := errors.New("permission denied")
		result := &PermissionResult{
			Success:       false,
			PermissionSet: "Admin",
			Error:         err,
		}

		if result.Success {
			t.Error("Success should be false")
		}
		if result.Error != err {
			t.Error("Error should be set for failed result")
		}
	})
}
