package feishu

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

	"AutoSSO/internal/config"
	"AutoSSO/internal/pkg/httpclient"

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

// mockFeishuServer creates a mock Feishu API server for testing.
func mockFeishuServer(t *testing.T) *httptest.Server {
	return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-Type", "application/json")

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

		case strings.Contains(r.URL.Path, "/contact/v3/users/batch_get_id"):
			// User ID lookup endpoint
			json.NewEncoder(w).Encode(map[string]interface{}{
				"code": 0,
				"msg":  "success",
				"data": map[string]interface{}{
					"user_list": []map[string]interface{}{
						{"user_id": "ou_test_user_id_12345"},
					},
				},
			})

		case strings.Contains(r.URL.Path, "/im/v1/messages"):
			// Message sending endpoint
			json.NewEncoder(w).Encode(map[string]interface{}{
				"code": 0,
				"msg":  "success",
			})

		default:
			w.WriteHeader(http.StatusNotFound)
			json.NewEncoder(w).Encode(map[string]interface{}{
				"code": 404,
				"msg":  "not found",
			})
		}
	}))
}

// createTestNotificationService creates a NotificationService for testing.
func createTestNotificationService(t *testing.T, server *httptest.Server) *NotificationService {
	cfg := &config.FeishuConfig{
		AppID:        "test-app-id",
		AppSecret:    "test-app-secret",
		ApprovalCode: "test-approval-code",
		ChatID:       "oc_test_chat_id",
		BaseURL:      server.URL,
	}

	httpClientCfg := httpclient.Config{
		Timeout: 10 * time.Second,
	}
	httpClient := httpclient.New(&httpClientCfg)

	client := NewClient(cfg, httpClient)
	return NewNotificationService(client, cfg)
}

func TestNewNotificationService(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)

	assert.NotNil(t, service)
	assert.NotNil(t, service.client)
	assert.NotNil(t, service.config)
}

func TestNotifyPermissionAdded(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)
	ctx := context.Background()

	tests := []struct {
		name        string
		data        *ApprovalData
		expectError bool
	}{
		{
			name: "valid AWS permission added",
			data: &ApprovalData{
				Email:          "test@example.com",
				CloudType:      "AWS",
				AccountID:      "123456789",
				PermissionSets: []string{"DBaaS-Prod-ViewOnly-Role"},
			},
			expectError: false,
		},
		{
			name: "valid GCP permission added",
			data: &ApprovalData{
				Email:          "test@example.com",
				CloudType:      "GCP",
				AccountID:      "tidbcloud-prod-000",
				PermissionSets: []string{"DBaaS-Prod-DevUser-Role"},
			},
			expectError: false,
		},
		{
			name: "valid Azure permission added",
			data: &ApprovalData{
				Email:          "test@example.com",
				CloudType:      "Azure",
				AccountID:      "subscription-id",
				PermissionSets: []string{"DBaaS-Prod-Admin-Role"},
			},
			expectError: false,
		},
		{
			name: "valid Alibaba permission added",
			data: &ApprovalData{
				Email:          "test@example.com",
				CloudType:      "Alibaba",
				AccountID:      "5480039859161595",
				PermissionSets: []string{"DBaaS-Prod-Security-Role"},
			},
			expectError: false,
		},
		{
			name: "multiple permissions added",
			data: &ApprovalData{
				Email:          "test@example.com",
				CloudType:      "AWS",
				AccountID:      "123456789",
				PermissionSets: []string{"DBaaS-Prod-ViewOnly-Role", "DBaaS-Prod-OnCall-Role"},
			},
			expectError: false,
		},
		{
			name:        "nil data",
			data:        nil,
			expectError: true,
		},
		{
			name: "empty email",
			data: &ApprovalData{
				Email:          "",
				CloudType:      "AWS",
				PermissionSets: []string{"DBaaS-Prod-ViewOnly-Role"},
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := service.NotifyPermissionAdded(ctx, tt.data)
			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestNotifyPermissionExpiring(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)
	ctx := context.Background()

	tests := []struct {
		name          string
		data          *ApprovalData
		daysRemaining int
		expectError   bool
	}{
		{
			name: "valid expiring notification with 3 days",
			data: &ApprovalData{
				Email:          "test@example.com",
				CloudType:      "AWS",
				AccountID:      "123456789",
				PermissionSets: []string{"DBaaS-Prod-ViewOnly-Role"},
			},
			daysRemaining: 3,
			expectError:   false,
		},
		{
			name: "valid expiring notification with 1 day",
			data: &ApprovalData{
				Email:          "test@example.com",
				CloudType:      "GCP",
				AccountID:      "tidbcloud-prod-000",
				PermissionSets: []string{"DBaaS-Prod-DevUser-Role"},
			},
			daysRemaining: 1,
			expectError:   false,
		},
		{
			name:          "nil data",
			data:          nil,
			daysRemaining: 3,
			expectError:   true,
		},
		{
			name: "empty email",
			data: &ApprovalData{
				Email:          "",
				CloudType:      "AWS",
				PermissionSets: []string{"DBaaS-Prod-ViewOnly-Role"},
			},
			daysRemaining: 3,
			expectError:   true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := service.NotifyPermissionExpiring(ctx, tt.data, tt.daysRemaining)
			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestNotifyPermissionRemoved(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)
	ctx := context.Background()

	tests := []struct {
		name        string
		data        *ApprovalData
		expectError bool
	}{
		{
			name: "valid AWS permission removed",
			data: &ApprovalData{
				Email:          "test@example.com",
				CloudType:      "AWS",
				AccountID:      "123456789",
				PermissionSets: []string{"DBaaS-Prod-ViewOnly-Role"},
			},
			expectError: false,
		},
		{
			name: "valid GCP permission removed",
			data: &ApprovalData{
				Email:          "test@example.com",
				CloudType:      "GCP",
				AccountID:      "tidbcloud-prod-000",
				PermissionSets: []string{"DBaaS-Prod-DevUser-Role"},
			},
			expectError: false,
		},
		{
			name:        "nil data",
			data:        nil,
			expectError: true,
		},
		{
			name: "empty email",
			data: &ApprovalData{
				Email:          "",
				CloudType:      "AWS",
				PermissionSets: []string{"DBaaS-Prod-ViewOnly-Role"},
			},
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := service.NotifyPermissionRemoved(ctx, tt.data)
			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestSendDailyReport(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)
	ctx := context.Background()

	tests := []struct {
		name        string
		stats       *DailyStats
		expectError bool
	}{
		{
			name: "valid stats with AWS data",
			stats: &DailyStats{
				ReportTime: time.Now(),
				AWS: CloudStats{
					TotalAdded:   5,
					TotalRemoved: 2,
					AddedByPermission: map[string]int{
						"DBaaS-Prod-ViewOnly-Role": 3,
						"DBaaS-Prod-OnCall-Role":   2,
					},
					RemovedByPermission: map[string]int{
						"DBaaS-Prod-ViewOnly-Role": 2,
					},
				},
			},
			expectError: false,
		},
		{
			name: "valid stats with all cloud platforms",
			stats: &DailyStats{
				ReportTime: time.Now(),
				AWS: CloudStats{
					TotalAdded:          3,
					TotalRemoved:        1,
					AddedByPermission:   map[string]int{"DBaaS-Prod-ViewOnly-Role": 3},
					RemovedByPermission: map[string]int{"DBaaS-Prod-ViewOnly-Role": 1},
				},
				GCP: CloudStats{
					TotalAdded:          2,
					TotalRemoved:        1,
					AddedByPermission:   map[string]int{"DBaaS-Prod-DevUser-Role": 2},
					RemovedByPermission: map[string]int{"DBaaS-Prod-DevUser-Role": 1},
				},
				Azure: CloudStats{
					TotalAdded:          1,
					TotalRemoved:        0,
					AddedByPermission:   map[string]int{"DBaaS-Prod-Admin-Role": 1},
					RemovedByPermission: map[string]int{},
				},
				Alibaba: CloudStats{
					TotalAdded:          1,
					TotalRemoved:        1,
					AddedByPermission:   map[string]int{"DBaaS-Prod-Security-Role": 1},
					RemovedByPermission: map[string]int{"DBaaS-Prod-Security-Role": 1},
				},
			},
			expectError: false,
		},
		{
			name: "empty stats - no reports sent",
			stats: &DailyStats{
				ReportTime: time.Now(),
				AWS:        CloudStats{AddedByPermission: map[string]int{}, RemovedByPermission: map[string]int{}},
				GCP:        CloudStats{AddedByPermission: map[string]int{}, RemovedByPermission: map[string]int{}},
				Azure:      CloudStats{AddedByPermission: map[string]int{}, RemovedByPermission: map[string]int{}},
				Alibaba:    CloudStats{AddedByPermission: map[string]int{}, RemovedByPermission: map[string]int{}},
			},
			expectError: false,
		},
		{
			name:        "nil stats",
			stats:       nil,
			expectError: true,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := service.SendDailyReport(ctx, tt.stats)
			if tt.expectError {
				assert.Error(t, err)
			} else {
				assert.NoError(t, err)
			}
		})
	}
}

func TestSendDailyReport_NoChatID(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	cfg := &config.FeishuConfig{
		AppID:        "test-app-id",
		AppSecret:    "test-app-secret",
		ApprovalCode: "test-approval-code",
		ChatID:       "", // Empty chat ID
		BaseURL:      server.URL,
	}

	httpClientCfg := httpclient.Config{
		Timeout: 10 * time.Second,
	}
	httpClient := httpclient.New(&httpClientCfg)

	client := NewClient(cfg, httpClient)
	service := NewNotificationService(client, cfg)

	stats := &DailyStats{
		ReportTime: time.Now(),
		AWS: CloudStats{
			TotalAdded:        1,
			AddedByPermission: map[string]int{"DBaaS-Prod-ViewOnly-Role": 1},
		},
	}

	err := service.SendDailyReport(context.Background(), stats)
	assert.Error(t, err)
	assert.Contains(t, err.Error(), "chat ID not configured")
}

func TestNewDailyStats(t *testing.T) {
	stats := NewDailyStats()

	require.NotNil(t, stats)
	assert.False(t, stats.ReportTime.IsZero())
	assert.NotNil(t, stats.AWS.AddedByPermission)
	assert.NotNil(t, stats.AWS.RemovedByPermission)
	assert.NotNil(t, stats.GCP.AddedByPermission)
	assert.NotNil(t, stats.GCP.RemovedByPermission)
	assert.NotNil(t, stats.Azure.AddedByPermission)
	assert.NotNil(t, stats.Azure.RemovedByPermission)
	assert.NotNil(t, stats.Alibaba.AddedByPermission)
	assert.NotNil(t, stats.Alibaba.RemovedByPermission)
}

func TestDailyStats_RecordPermissionAdded(t *testing.T) {
	stats := NewDailyStats()

	// Record AWS permissions
	stats.RecordPermissionAdded("AWS", "DBaaS-Prod-ViewOnly-Role")
	stats.RecordPermissionAdded("AWS", "DBaaS-Prod-ViewOnly-Role")
	stats.RecordPermissionAdded("AWS", "DBaaS-Prod-OnCall-Role")

	assert.Equal(t, 3, stats.AWS.TotalAdded)
	assert.Equal(t, 2, stats.AWS.AddedByPermission["DBaaS-Prod-ViewOnly-Role"])
	assert.Equal(t, 1, stats.AWS.AddedByPermission["DBaaS-Prod-OnCall-Role"])

	// Record GCP permissions
	stats.RecordPermissionAdded("GCP", "DBaaS-Prod-DevUser-Role")
	assert.Equal(t, 1, stats.GCP.TotalAdded)
	assert.Equal(t, 1, stats.GCP.AddedByPermission["DBaaS-Prod-DevUser-Role"])

	// Record Azure permissions
	stats.RecordPermissionAdded("Azure", "DBaaS-Prod-Admin-Role")
	assert.Equal(t, 1, stats.Azure.TotalAdded)
	assert.Equal(t, 1, stats.Azure.AddedByPermission["DBaaS-Prod-Admin-Role"])

	// Record Alibaba permissions
	stats.RecordPermissionAdded("Alibaba", "DBaaS-Prod-Security-Role")
	assert.Equal(t, 1, stats.Alibaba.TotalAdded)
	assert.Equal(t, 1, stats.Alibaba.AddedByPermission["DBaaS-Prod-Security-Role"])
}

func TestDailyStats_RecordPermissionRemoved(t *testing.T) {
	stats := NewDailyStats()

	// Record AWS general permission removal
	stats.RecordPermissionRemoved("AWS", "DBaaS-Prod-ViewOnly-Role", false)
	stats.RecordPermissionRemoved("AWS", "DBaaS-Prod-ViewOnly-Role", false)

	assert.Equal(t, 2, stats.AWS.TotalRemoved)
	assert.Equal(t, 2, stats.AWS.RemovedByPermission["DBaaS-Prod-ViewOnly-Role"])
	assert.Equal(t, 0, stats.AWS.NonGeneralRemoved)

	// Record AWS non-general permission removal
	stats.RecordPermissionRemoved("AWS", "custom-role", true)
	assert.Equal(t, 3, stats.AWS.TotalRemoved)
	assert.Equal(t, 1, stats.AWS.NonGeneralRemoved)

	// Record GCP permissions
	stats.RecordPermissionRemoved("GCP", "DBaaS-Prod-DevUser-Role", false)
	assert.Equal(t, 1, stats.GCP.TotalRemoved)
	assert.Equal(t, 1, stats.GCP.RemovedByPermission["DBaaS-Prod-DevUser-Role"])

	// Record Azure permissions
	stats.RecordPermissionRemoved("Azure", "DBaaS-Prod-Admin-Role", false)
	assert.Equal(t, 1, stats.Azure.TotalRemoved)
	assert.Equal(t, 1, stats.Azure.RemovedByPermission["DBaaS-Prod-Admin-Role"])

	// Record Alibaba permissions
	stats.RecordPermissionRemoved("Alibaba", "DBaaS-Prod-Security-Role", false)
	assert.Equal(t, 1, stats.Alibaba.TotalRemoved)
	assert.Equal(t, 1, stats.Alibaba.RemovedByPermission["DBaaS-Prod-Security-Role"])
}

func TestNotificationTitles(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)

	// Test added notification titles
	assert.Equal(t, "AWS SSO 权限添加通知", service.getAddedNotificationTitle("AWS"))
	assert.Equal(t, "GCP SSO 权限添加通知", service.getAddedNotificationTitle("GCP"))
	assert.Equal(t, "Azure SSO 权限添加通知", service.getAddedNotificationTitle("Azure"))
	assert.Equal(t, "Alibaba SSO 权限添加通知", service.getAddedNotificationTitle("Alibaba"))
	assert.Equal(t, "SSO 权限添加通知", service.getAddedNotificationTitle("Unknown"))

	// Test expiring notification titles
	assert.Equal(t, "AWS SSO 权限过期提醒", service.getExpiringNotificationTitle("AWS"))
	assert.Equal(t, "GCP SSO 权限过期提醒", service.getExpiringNotificationTitle("GCP"))
	assert.Equal(t, "Azure SSO 权限过期提醒", service.getExpiringNotificationTitle("Azure"))
	assert.Equal(t, "Alibaba SSO 权限过期提醒", service.getExpiringNotificationTitle("Alibaba"))
	assert.Equal(t, "SSO 权限过期提醒", service.getExpiringNotificationTitle("Unknown"))

	// Test removed notification titles
	assert.Equal(t, "AWS SSO 权限回收通知", service.getRemovedNotificationTitle("AWS"))
	assert.Equal(t, "GCP SSO 权限回收通知", service.getRemovedNotificationTitle("GCP"))
	assert.Equal(t, "Azure SSO 权限回收通知", service.getRemovedNotificationTitle("Azure"))
	assert.Equal(t, "Alibaba SSO 权限回收通知", service.getRemovedNotificationTitle("Alibaba"))
	assert.Equal(t, "SSO 权限回收通知", service.getRemovedNotificationTitle("Unknown"))
}

func TestFormatPermissionAddedMessage(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)

	data := &ApprovalData{
		Email:          "test@example.com",
		CloudType:      "AWS",
		PermissionSets: []string{"DBaaS-Prod-ViewOnly-Role", "DBaaS-Prod-OnCall-Role"},
	}

	message := service.formatPermissionAddedMessage(data)

	assert.Contains(t, message, "AWS SSO Permission")
	assert.Contains(t, message, "DBaaS-Prod-ViewOnly-Role, DBaaS-Prod-OnCall-Role")
	assert.Contains(t, message, "已经授予完成")
}

func TestFormatPermissionExpiringMessage(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)

	data := &ApprovalData{
		Email:          "test@example.com",
		CloudType:      "GCP",
		PermissionSets: []string{"DBaaS-Prod-DevUser-Role"},
	}

	message := service.formatPermissionExpiringMessage(data, 3)

	assert.Contains(t, message, "GCP SSO Permission")
	assert.Contains(t, message, "DBaaS-Prod-DevUser-Role")
	assert.Contains(t, message, "3 天后自动回收")
}

func TestFormatPermissionRemovedMessage(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)

	data := &ApprovalData{
		Email:          "test@example.com",
		CloudType:      "Azure",
		PermissionSets: []string{"DBaaS-Prod-Admin-Role"},
	}

	message := service.formatPermissionRemovedMessage(data)

	assert.Contains(t, message, "Azure SSO Permission")
	assert.Contains(t, message, "DBaaS-Prod-Admin-Role")
	assert.Contains(t, message, "已经被系统自动回收")
}

func TestFormatAWSReport(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)

	stats := &DailyStats{
		ReportTime: time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC),
		AWS: CloudStats{
			TotalAdded:   5,
			TotalRemoved: 3,
			AddedByPermission: map[string]int{
				"DBaaS-Prod-ViewOnly-Role": 3,
				"DBaaS-Prod-OnCall-Role":   2,
			},
			RemovedByPermission: map[string]int{
				"DBaaS-Prod-ViewOnly-Role": 2,
				"DBaaS-Prod-OnCall-Role":   1,
			},
			NonGeneralRemoved: 1,
		},
	}

	report := service.formatAWSReport(stats)

	assert.Contains(t, report, "2024-01-15 10:30")
	assert.Contains(t, report, "AWS IAM SSO")
	assert.Contains(t, report, "添加授予权限总数:5")
	assert.Contains(t, report, "回收过期权限总数:3")
	assert.Contains(t, report, "非通用权限集回收数：1")
}

func TestFormatGCPReport(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)

	stats := &DailyStats{
		ReportTime: time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC),
		GCP: CloudStats{
			TotalAdded:   3,
			TotalRemoved: 2,
			AddedByPermission: map[string]int{
				"DBaaS-Prod-DevUser-Role": 3,
			},
			RemovedByPermission: map[string]int{
				"DBaaS-Prod-DevUser-Role": 2,
			},
		},
	}

	report := service.formatGCPReport(stats)

	assert.Contains(t, report, "2024-01-15 10:30")
	assert.Contains(t, report, "GCP IAM SSO")
	assert.Contains(t, report, "添加授予权限总数:3")
	assert.Contains(t, report, "回收过期权限总数:2")
}

func TestFormatAzureReport(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)

	stats := &DailyStats{
		ReportTime: time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC),
		Azure: CloudStats{
			TotalAdded:   2,
			TotalRemoved: 1,
			AddedByPermission: map[string]int{
				"DBaaS-Prod-Admin-Role": 2,
			},
			RemovedByPermission: map[string]int{
				"DBaaS-Prod-Admin-Role": 1,
			},
		},
	}

	report := service.formatAzureReport(stats)

	assert.Contains(t, report, "2024-01-15 10:30")
	assert.Contains(t, report, "Azure IAM SSO")
	assert.Contains(t, report, "添加授予权限总数:2")
	assert.Contains(t, report, "回收过期权限总数:1")
}

func TestFormatAlibabaReport(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)

	stats := &DailyStats{
		ReportTime: time.Date(2024, 1, 15, 10, 30, 0, 0, time.UTC),
		Alibaba: CloudStats{
			TotalAdded:   1,
			TotalRemoved: 1,
			AddedByPermission: map[string]int{
				"DBaaS-Prod-Security-Role": 1,
			},
			RemovedByPermission: map[string]int{
				"DBaaS-Prod-Security-Role": 1,
			},
		},
	}

	report := service.formatAlibabaReport(stats)

	assert.Contains(t, report, "2024-01-15 10:30")
	assert.Contains(t, report, "Alibaba IAM SSO")
	assert.Contains(t, report, "添加授予权限总数:1")
	assert.Contains(t, report, "回收过期权限总数:1")
}

func TestContextCancellation(t *testing.T) {
	server := mockFeishuServer(t)
	defer server.Close()

	service := createTestNotificationService(t, server)

	// Create a cancelled context
	ctx, cancel := context.WithCancel(context.Background())
	cancel()

	data := &ApprovalData{
		Email:          "test@example.com",
		CloudType:      "AWS",
		PermissionSets: []string{"DBaaS-Prod-ViewOnly-Role"},
	}

	// The operation should fail due to cancelled context
	err := service.NotifyPermissionAdded(ctx, data)
	assert.Error(t, err)
}
