package permission

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"AutoSSO/internal/config"
	"AutoSSO/internal/feishu"
	"AutoSSO/internal/pkg/httpclient"
	"AutoSSO/internal/provider"

	"github.com/stretchr/testify/assert"
)

// mockMonitorTestProvider implements provider.Provider interface for testing
type mockMonitorTestProvider struct{}

func (m *mockMonitorTestProvider) Name() string { return "Alibaba" }
func (m *mockMonitorTestProvider) AddPermission(ctx context.Context, req *provider.PermissionRequest) error { return nil }
func (m *mockMonitorTestProvider) RemovePermission(ctx context.Context, req *provider.PermissionRequest) error { return nil }
func (m *mockMonitorTestProvider) GetUserID(ctx context.Context, email string) (string, error) { return "test-user-id", nil }
func (m *mockMonitorTestProvider) ValidateCredentials(ctx context.Context) error { return nil }
func (m *mockMonitorTestProvider) Close() error { return nil }

func TestMonitor_ProcessPendingApprovals_Integration(t *testing.T) {
	cfg := createMonitorTestConfig()
	// Disable historical check for easier testing
	cfg.Runtime.SkipHistoricalApprovals = true
	
	// Configure test account
	cfg.Providers.Alibaba.Accounts = []config.CloudAccount{
		{ID: "123456789", Name: "Test Account"},
	}

	// Create registry and register mock provider
	registry, err := provider.NewRegistry(cfg)
	assert.NoError(t, err)

	err = registry.RegisterProvider(provider.ProviderAlibaba, &mockMonitorTestProvider{})
	assert.NoError(t, err)
	
	log := createMonitorTestLogger()
	stateStore := newMockMonitorStateStore()

	// Control parsing behavior via server response
	var callCount int
	
	// Create a mock Feishu server
	server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")

		if strings.Contains(r.URL.Path, "/auth/v3/tenant_access_token") {
			json.NewEncoder(w).Encode(map[string]interface{}{
				"code":                0,
				"msg":                 "success",
				"tenant_access_token": "test-token-12345",
				"expire":              7200,
			})
			return
		}

		if strings.Contains(r.URL.Path, "/approval/v4/instances") {
			// List instances
			if r.Method == "GET" && !strings.Contains(r.URL.Path, "/test-instance-pending") {
				// Return our test instance ID
				json.NewEncoder(w).Encode(map[string]interface{}{
					"code": 0,
					"msg":  "success",
					"data": map[string]interface{}{
						"instance_code_list": []string{"test-instance-pending"},
					},
				})
				return
			}
			
			// Get Instance Detail
			if strings.Contains(r.URL.Path, "test-instance-pending") {
				callCount++
				
				isApproved := false
				if callCount > 1 {
					isApproved = true // Approve on second call
				}
				
				// Using helper from monitor_test.go
				resp := createMockApprovalResponse(
					"test@example.com",
					"123456789",
					"Aliyun",
					[]string{"ReadOnly"},
					isApproved,
					time.Now(),
					time.Now().Add(24*time.Hour),
				)
				w.Write(resp)
				return
			}
		}

		w.WriteHeader(http.StatusNotFound)
	}))
	defer server.Close()

	cfg.Feishu.BaseURL = server.URL
	httpClient := httpclient.New(&httpclient.Config{Timeout: 5 * time.Second})
	feishuClient := feishu.NewClient(&cfg.Feishu, httpClient)
	
	manager := &Manager{
		state:     stateStore,
		config:    cfg,
		logger:    log.WithModule("permission-manager"),
		providers: registry,
	}
	
	monitor := NewMonitor(manager, feishuClient, stateStore, cfg, log)

	ctx := context.Background()
	
	// 1. First Pass: Process New Approvals
	// This should fetch the instance, see it as PENDING (callCount=1), and add to pending list.
	err = monitor.ProcessNewApprovals(ctx)
	assert.NoError(t, err)
	
	// Verify it is in pending list
	assert.Equal(t, 1, monitor.GetPendingInstanceCount())
	pendingIDs := monitor.GetPendingInstances()
	assert.Contains(t, pendingIDs, "test-instance-pending")
	
	// 2. Second Pass: Process Pending Approvals
	// This should re-fetch (callCount=2 -> APPROVED).
	// Then it attempts AddPermission. Since manager has our mock provider, it should succeed.
	// processInstance checks "if !approvalData.IsApproved" (it is approved now).
	// Then "if isPending { ... delete ... }". So it IS deleted.
	
	err = monitor.ProcessPendingApprovals(ctx)
	assert.NoError(t, err)
	
	// Verify it is REMOVED from pending list
	assert.Equal(t, 0, monitor.GetPendingInstanceCount())
	
	// Verify call count (should be 2: 1 from ProcessNewApprovals, 1 from ProcessPendingApprovals)
	assert.GreaterOrEqual(t, callCount, 2)
}
