package service_test

import (
	"context"
	"testing"
	"time"

	"github.com/user-management-system/internal/domain"
	"github.com/user-management-system/internal/repository"
	"github.com/user-management-system/internal/service"
	gormsqlite "gorm.io/driver/sqlite"
	"gorm.io/gorm"
	"gorm.io/gorm/logger"
)

// =============================================================================
// Login Log Service Tests
// =============================================================================

func setupLoginLogTestEnv(t *testing.T) (*service.LoginLogService, *gorm.DB) {
	t.Helper()

	db, err := gorm.Open(gormsqlite.New(gormsqlite.Config{
		DriverName: "sqlite",
		DSN:        "file:loginlog_test?mode=memory&cache=shared",
	}), &gorm.Config{
		Logger: logger.Default.LogMode(logger.Silent),
	})
	if err != nil {
		t.Fatalf("failed to connect database: %v", err)
	}

	if err := db.AutoMigrate(&domain.LoginLog{}); err != nil {
		t.Fatalf("failed to migrate: %v", err)
	}

	loginLogRepo := repository.NewLoginLogRepository(db)
	logSvc := service.NewLoginLogService(loginLogRepo)

	return logSvc, db
}

func TestLoginLogService_RecordLogin(t *testing.T) {
	svc, _ := setupLoginLogTestEnv(t)
	ctx := context.Background()

	t.Run("Record login success", func(t *testing.T) {
		req := &service.RecordLoginRequest{
			UserID:    1,
			LoginType: 1,
			DeviceID:  "device001",
			IP:        "192.168.1.1",
			Location:  "Beijing",
			Status:    1,
		}
		err := svc.RecordLogin(ctx, req)
		if err != nil {
			t.Fatalf("RecordLogin failed: %v", err)
		}
	})

	t.Run("Record login failure", func(t *testing.T) {
		req := &service.RecordLoginRequest{
			UserID:     2,
			LoginType:  1,
			DeviceID:   "device002",
			IP:         "192.168.1.2",
			Status:     0,
			FailReason: "密码错误",
		}
		err := svc.RecordLogin(ctx, req)
		if err != nil {
			t.Fatalf("RecordLogin failed: %v", err)
		}
	})

	t.Run("Record login without user ID", func(t *testing.T) {
		req := &service.RecordLoginRequest{
			LoginType: 1,
			DeviceID:  "device003",
			IP:        "192.168.1.3",
			Status:    0,
		}
		err := svc.RecordLogin(ctx, req)
		if err != nil {
			t.Fatalf("RecordLogin failed: %v", err)
		}
	})
}

func TestLoginLogService_GetLoginLogs(t *testing.T) {
	svc, _ := setupLoginLogTestEnv(t)
	ctx := context.Background()

	// Create test logs
	for i := 0; i < 5; i++ {
		req := &service.RecordLoginRequest{
			UserID:    1,
			LoginType: 1,
			DeviceID:  string(rune('a' + i)),
			IP:        "192.168.1.1",
			Status:    1,
		}
		svc.RecordLogin(ctx, req)
	}

	t.Run("Get login logs with pagination", func(t *testing.T) {
		req := &service.ListLoginLogRequest{
			Page:     1,
			PageSize: 3,
		}
		logs, total, err := svc.GetLoginLogs(ctx, req)
		if err != nil {
			t.Fatalf("GetLoginLogs failed: %v", err)
		}
		if len(logs) > 3 {
			t.Errorf("Expected max 3 logs, got %d", len(logs))
		}
		if total < 5 {
			t.Errorf("Expected total >= 5, got %d", total)
		}
	})

	t.Run("Get login logs by user ID", func(t *testing.T) {
		req := &service.ListLoginLogRequest{
			UserID:   1,
			Page:     1,
			PageSize: 10,
		}
		logs, _, err := svc.GetLoginLogs(ctx, req)
		if err != nil {
			t.Fatalf("GetLoginLogs failed: %v", err)
		}
		if len(logs) < 5 {
			t.Errorf("Expected at least 5 logs, got %d", len(logs))
		}
	})

	t.Run("Get login logs with default pagination", func(t *testing.T) {
		req := &service.ListLoginLogRequest{}
		_, _, err := svc.GetLoginLogs(ctx, req)
		if err != nil {
			t.Fatalf("GetLoginLogs failed: %v", err)
		}
	})

	t.Run("Get login logs by status", func(t *testing.T) {
		status := 1
		req := &service.ListLoginLogRequest{
			Status:   &status,
			Page:     1,
			PageSize: 10,
		}
		_, _, err := svc.GetLoginLogs(ctx, req)
		if err != nil {
			t.Fatalf("GetLoginLogs failed: %v", err)
		}
	})

	t.Run("Get login logs by time range", func(t *testing.T) {
		req := &service.ListLoginLogRequest{
			StartAt:  time.Now().Add(-24 * time.Hour).Format(time.RFC3339),
			EndAt:    time.Now().Add(24 * time.Hour).Format(time.RFC3339),
			Page:     1,
			PageSize: 10,
		}
		_, _, err := svc.GetLoginLogs(ctx, req)
		if err != nil {
			t.Fatalf("GetLoginLogs failed: %v", err)
		}
	})
}

func TestLoginLogService_GetMyLoginLogs(t *testing.T) {
	svc, _ := setupLoginLogTestEnv(t)
	ctx := context.Background()

	// Create test logs
	for i := 0; i < 3; i++ {
		req := &service.RecordLoginRequest{
			UserID:    1,
			LoginType: 1,
			DeviceID:  string(rune('x' + i)),
			IP:        "192.168.1.1",
			Status:    1,
		}
		svc.RecordLogin(ctx, req)
	}

	t.Run("Get my login logs", func(t *testing.T) {
		logs, total, err := svc.GetMyLoginLogs(ctx, 1, 1, 10)
		if err != nil {
			t.Fatalf("GetMyLoginLogs failed: %v", err)
		}
		if total < 3 {
			t.Errorf("Expected total >= 3, got %d", total)
		}
		_ = logs
	})

	t.Run("Get my login logs with default pagination", func(t *testing.T) {
		_, _, err := svc.GetMyLoginLogs(ctx, 1, 0, 0)
		if err != nil {
			t.Fatalf("GetMyLoginLogs failed: %v", err)
		}
	})
}

func TestLoginLogService_GetLoginLogsCursor(t *testing.T) {
	svc, _ := setupLoginLogTestEnv(t)
	ctx := context.Background()

	// Create test logs
	for i := 0; i < 5; i++ {
		req := &service.RecordLoginRequest{
			UserID:    1,
			LoginType: 1,
			DeviceID:  string(rune('m' + i)),
			IP:        "192.168.1.1",
			Status:    1,
		}
		svc.RecordLogin(ctx, req)
	}

	t.Run("Get login logs with cursor", func(t *testing.T) {
		req := &service.ListLoginLogRequest{
			UserID: 1,
			Size:   3,
		}
		result, err := svc.GetLoginLogsCursor(ctx, req)
		if err != nil {
			t.Fatalf("GetLoginLogsCursor failed: %v", err)
		}
		if result.PageSize != 3 {
			t.Errorf("Expected page size 3, got %d", result.PageSize)
		}
	})

	t.Run("Get login logs with status filter cursor", func(t *testing.T) {
		status := 1
		req := &service.ListLoginLogRequest{
			Status: &status,
			Size:   10,
		}
		result, err := svc.GetLoginLogsCursor(ctx, req)
		if err != nil {
			t.Fatalf("GetLoginLogsCursor failed: %v", err)
		}
		_ = result
	})

	t.Run("Get login logs with time range cursor", func(t *testing.T) {
		req := &service.ListLoginLogRequest{
			StartAt: time.Now().Add(-24 * time.Hour).Format(time.RFC3339),
			EndAt:   time.Now().Add(24 * time.Hour).Format(time.RFC3339),
			Size:    10,
		}
		result, err := svc.GetLoginLogsCursor(ctx, req)
		if err != nil {
			t.Fatalf("GetLoginLogsCursor failed: %v", err)
		}
		_ = result
	})

	t.Run("Get login logs with invalid cursor", func(t *testing.T) {
		req := &service.ListLoginLogRequest{
			Cursor: "invalid-cursor",
		}
		_, err := svc.GetLoginLogsCursor(ctx, req)
		if err == nil {
			t.Error("Expected error for invalid cursor")
		}
	})
}

func TestLoginLogService_ExportLoginLogs(t *testing.T) {
	svc, _ := setupLoginLogTestEnv(t)
	ctx := context.Background()

	// Create test logs
	for i := 0; i < 3; i++ {
		req := &service.RecordLoginRequest{
			UserID:    1,
			LoginType: 1,
			DeviceID:  string(rune('p' + i)),
			IP:        "192.168.1.1",
			Status:    1,
		}
		svc.RecordLogin(ctx, req)
	}

	t.Run("Export login logs as CSV", func(t *testing.T) {
		req := &service.ExportLoginLogRequest{
			Format: "csv",
		}
		data, filename, contentType, err := svc.ExportLoginLogs(ctx, req)
		if err != nil {
			t.Fatalf("ExportLoginLogs failed: %v", err)
		}
		if len(data) == 0 {
			t.Error("Expected CSV data")
		}
		if filename == "" {
			t.Error("Expected filename")
		}
		if contentType == "" {
			t.Error("Expected content type")
		}
	})

	t.Run("Export login logs as XLSX", func(t *testing.T) {
		req := &service.ExportLoginLogRequest{
			Format: "xlsx",
		}
		data, filename, contentType, err := svc.ExportLoginLogs(ctx, req)
		if err != nil {
			t.Fatalf("ExportLoginLogs failed: %v", err)
		}
		if len(data) == 0 {
			t.Error("Expected XLSX data")
		}
		if filename == "" {
			t.Error("Expected filename")
		}
		_ = contentType
	})

	t.Run("Export login logs with time range", func(t *testing.T) {
		req := &service.ExportLoginLogRequest{
			Format:  "csv",
			StartAt: time.Now().Add(-24 * time.Hour).Format(time.RFC3339),
			EndAt:   time.Now().Add(24 * time.Hour).Format(time.RFC3339),
		}
		data, _, _, err := svc.ExportLoginLogs(ctx, req)
		if err != nil {
			t.Fatalf("ExportLoginLogs failed: %v", err)
		}
		_ = data
	})
}

func TestLoginLogService_CleanupOldLogs(t *testing.T) {
	svc, _ := setupLoginLogTestEnv(t)
	ctx := context.Background()

	t.Run("Cleanup old logs", func(t *testing.T) {
		err := svc.CleanupOldLogs(ctx, 30)
		if err != nil {
			t.Fatalf("CleanupOldLogs failed: %v", err)
		}
	})
}
