package service

import (
	"context"
	"database/sql"
	"errors"
	"fmt"
	"hm-server/app/main/api/internal/config"
	"hm-server/app/main/model"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"github.com/Masterminds/squirrel"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/mock"
	"github.com/zeromicro/go-zero/core/stores/sqlx"
)

// mockResult 模拟sql.Result
type mockResult struct {
	lastInsertId int64
	rowsAffected int64
}

func (m *mockResult) LastInsertId() (int64, error) {
	return m.lastInsertId, nil
}

func (m *mockResult) RowsAffected() (int64, error) {
	return m.rowsAffected, nil
}

// MockAuthorizationDocumentModel 模拟授权书模型
type MockAuthorizationDocumentModel struct {
	mock.Mock
}

func (m *MockAuthorizationDocumentModel) Insert(ctx context.Context, session sqlx.Session, data *model.AuthorizationDocument) (sql.Result, error) {
	args := m.Called(ctx, session, data)
	return args.Get(0).(sql.Result), args.Error(1)
}

func (m *MockAuthorizationDocumentModel) FindOne(ctx context.Context, id int64) (*model.AuthorizationDocument, error) {
	args := m.Called(ctx, id)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).(*model.AuthorizationDocument), args.Error(1)
}

func (m *MockAuthorizationDocumentModel) Update(ctx context.Context, session sqlx.Session, data *model.AuthorizationDocument) (sql.Result, error) {
	args := m.Called(ctx, session, data)
	return args.Get(0).(sql.Result), args.Error(1)
}

func (m *MockAuthorizationDocumentModel) UpdateWithVersion(ctx context.Context, session sqlx.Session, data *model.AuthorizationDocument) error {
	args := m.Called(ctx, session, data)
	return args.Error(0)
}

func (m *MockAuthorizationDocumentModel) Trans(ctx context.Context, fn func(context.Context, sqlx.Session) error) error {
	args := m.Called(ctx, fn)
	return args.Error(0)
}

func (m *MockAuthorizationDocumentModel) SelectBuilder() squirrel.SelectBuilder {
	args := m.Called()
	return args.Get(0).(squirrel.SelectBuilder)
}

func (m *MockAuthorizationDocumentModel) DeleteSoft(ctx context.Context, session sqlx.Session, data *model.AuthorizationDocument) error {
	args := m.Called(ctx, session, data)
	return args.Error(0)
}

func (m *MockAuthorizationDocumentModel) FindSum(ctx context.Context, sumBuilder squirrel.SelectBuilder, field string) (float64, error) {
	args := m.Called(ctx, sumBuilder, field)
	return args.Get(0).(float64), args.Error(1)
}

func (m *MockAuthorizationDocumentModel) FindCount(ctx context.Context, countBuilder squirrel.SelectBuilder, field string) (int64, error) {
	args := m.Called(ctx, countBuilder, field)
	return args.Get(0).(int64), args.Error(1)
}

func (m *MockAuthorizationDocumentModel) FindAll(ctx context.Context, rowBuilder squirrel.SelectBuilder, orderBy string) ([]*model.AuthorizationDocument, error) {
	args := m.Called(ctx, rowBuilder, orderBy)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]*model.AuthorizationDocument), args.Error(1)
}

func (m *MockAuthorizationDocumentModel) FindPageListByPage(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*model.AuthorizationDocument, error) {
	args := m.Called(ctx, rowBuilder, page, pageSize, orderBy)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]*model.AuthorizationDocument), args.Error(1)
}

func (m *MockAuthorizationDocumentModel) FindPageListByPageWithTotal(ctx context.Context, rowBuilder squirrel.SelectBuilder, page, pageSize int64, orderBy string) ([]*model.AuthorizationDocument, int64, error) {
	args := m.Called(ctx, rowBuilder, page, pageSize, orderBy)
	if args.Get(0) == nil {
		return nil, args.Get(1).(int64), args.Error(2)
	}
	return args.Get(0).([]*model.AuthorizationDocument), args.Get(1).(int64), args.Error(2)
}

func (m *MockAuthorizationDocumentModel) FindPageListByIdDESC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMinId, pageSize int64) ([]*model.AuthorizationDocument, error) {
	args := m.Called(ctx, rowBuilder, preMinId, pageSize)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]*model.AuthorizationDocument), args.Error(1)
}

func (m *MockAuthorizationDocumentModel) FindPageListByIdASC(ctx context.Context, rowBuilder squirrel.SelectBuilder, preMaxId, pageSize int64) ([]*model.AuthorizationDocument, error) {
	args := m.Called(ctx, rowBuilder, preMaxId, pageSize)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]*model.AuthorizationDocument), args.Error(1)
}

func (m *MockAuthorizationDocumentModel) Delete(ctx context.Context, session sqlx.Session, id int64) error {
	args := m.Called(ctx, session, id)
	return args.Error(0)
}

func (m *MockAuthorizationDocumentModel) FindByOrderId(ctx context.Context, orderId int64) ([]*model.AuthorizationDocument, error) {
	args := m.Called(ctx, orderId)
	if args.Get(0) == nil {
		return nil, args.Error(1)
	}
	return args.Get(0).([]*model.AuthorizationDocument), args.Error(1)
}

// MockResult 模拟数据库结果
type MockResult struct {
	lastInsertId int64
}

func (m *MockResult) LastInsertId() (int64, error) {
	return m.lastInsertId, nil
}

func (m *MockResult) RowsAffected() (int64, error) {
	return 1, nil
}

// TestNewAuthorizationService 测试创建授权书服务
func TestNewAuthorizationService(t *testing.T) {
	config := config.Config{
		Authorization: config.AuthorizationConfig{
			FileBaseURL: "https://test.com/api/v1/auth-docs",
		},
	}
	mockModel := &MockAuthorizationDocumentModel{}

	service := NewAuthorizationService(config, mockModel)

	assert.NotNil(t, service)
	assert.Equal(t, "data/authorization_docs", service.fileStoragePath)
	assert.Equal(t, "https://test.com/api/v1/auth-docs", service.fileBaseURL)
}

// TestGetFullFileURL 测试获取完整文件URL
func TestGetFullFileURL(t *testing.T) {
	config := config.Config{
		Authorization: config.AuthorizationConfig{
			FileBaseURL: "https://test.com/api/v1/auth-docs",
		},
	}
	mockModel := &MockAuthorizationDocumentModel{}
	service := NewAuthorizationService(config, mockModel)

	tests := []struct {
		name         string
		relativePath string
		expected     string
	}{
		{
			name:         "正常相对路径",
			relativePath: "2025/09/auth_123_456_20250913_160800.pdf",
			expected:     "https://test.com/api/v1/auth-docs/2025/09/auth_123_456_20250913_160800.pdf",
		},
		{
			name:         "空路径",
			relativePath: "",
			expected:     "",
		},
		{
			name:         "只有文件名",
			relativePath: "test.pdf",
			expected:     "https://test.com/api/v1/auth-docs/test.pdf",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := service.GetFullFileURL(tt.relativePath)
			assert.Equal(t, tt.expected, result)
		})
	}
}

// TestGenerateAuthorizationDocument 测试生成授权书
func TestGenerateAuthorizationDocument(t *testing.T) {
	// 创建测试配置
	config := config.Config{
		Authorization: config.AuthorizationConfig{
			FileBaseURL: "https://test.example.com/api/v1/auth-docs",
		},
	}

	// 创建模拟的数据库模型
	mockModel := &MockAuthorizationDocumentModel{}

	// 创建授权书服务
	service := NewAuthorizationService(config, mockModel)

	// 准备测试数据
	userInfo := map[string]interface{}{
		"name":    "张三",
		"id_card": "110101199001011234",
		"mobile":  "13800138000",
	}

	// 模拟数据库插入成功
	mockModel.On("Insert", mock.Anything, mock.Anything, mock.Anything).Return(
		&mockResult{lastInsertId: 1, rowsAffected: 1}, nil)

	// 执行测试
	authDoc, err := service.GenerateAuthorizationDocument(
		context.Background(),
		1, // userID
		2, // orderID
		3, // queryID
		userInfo,
	)

	// 验证结果
	assert.NoError(t, err)
	assert.NotNil(t, authDoc)
	assert.Equal(t, int64(1), authDoc.UserId)
	assert.Equal(t, int64(2), authDoc.OrderId)
	assert.Equal(t, int64(3), authDoc.QueryId)
	assert.Equal(t, "pdf", authDoc.FileType)
	assert.Equal(t, "active", authDoc.Status)
	assert.False(t, authDoc.ExpireTime.Valid) // 永久保留，不设置过期时间

	// 验证文件路径格式（兼容Windows和Unix路径分隔符）
	assert.True(t, strings.Contains(authDoc.FilePath, "data/authorization_docs") || 
		strings.Contains(authDoc.FilePath, "data\\authorization_docs"))
	assert.Contains(t, authDoc.FileName, "auth_")
	assert.Contains(t, authDoc.FileName, ".pdf")

	// 验证相对路径格式
	assert.Regexp(t, `^\d{4}/\d{2}/auth_\d+_\d+_\d{8}_\d{6}\.pdf$`, authDoc.FileUrl)

	// 验证文件大小
	assert.Greater(t, authDoc.FileSize, int64(0))

	// 验证数据库调用
	mockModel.AssertExpectations(t)

	// 验证文件是否真的被创建
	if _, err := os.Stat(authDoc.FilePath); err == nil {
		t.Logf("✅ 授权书文件已创建: %s", authDoc.FilePath)
		t.Logf("📊 文件大小: %d 字节", authDoc.FileSize)
	} else {
		t.Logf("⚠️ 文件未找到: %s", authDoc.FilePath)
	}
}

// TestGenerateAuthorizationDocument_DatabaseError 测试数据库错误
func TestGenerateAuthorizationDocument_DatabaseError(t *testing.T) {
	// 创建测试配置
	config := config.Config{
		Authorization: config.AuthorizationConfig{
			FileBaseURL: "https://test.example.com/api/v1/auth-docs",
		},
	}

	// 创建模拟的数据库模型
	mockModel := &MockAuthorizationDocumentModel{}

	// 创建授权书服务
	service := NewAuthorizationService(config, mockModel)

	// 准备测试数据
	userInfo := map[string]interface{}{
		"name":    "李四",
		"id_card": "110101199001011235",
		"mobile":  "13800138001",
	}

	// 模拟数据库插入失败
	mockModel.On("Insert", mock.Anything, mock.Anything, mock.Anything).Return(
		(*mockResult)(nil), errors.New("数据库连接失败"))

	// 执行测试
	authDoc, err := service.GenerateAuthorizationDocument(
		context.Background(),
		1, // userID
		2, // orderID
		3, // queryID
		userInfo,
	)

	// 验证结果
	assert.Error(t, err)
	assert.Nil(t, authDoc)
	assert.Contains(t, err.Error(), "数据库连接失败")

	// 验证数据库调用
	mockModel.AssertExpectations(t)
}

// TestGeneratePDFContent 测试生成PDF内容
func TestGeneratePDFContent(t *testing.T) {
	config := config.Config{
		Authorization: config.AuthorizationConfig{
			FileBaseURL: "https://test.com/api/v1/auth-docs",
		},
	}
	mockModel := &MockAuthorizationDocumentModel{}
	service := NewAuthorizationService(config, mockModel)

	userInfo := map[string]interface{}{
		"name":    "张三",
		"id_card": "110101199001011234",
	}

	pdfBytes, err := service.generatePDFContent(userInfo)

	// 验证结果
	assert.NoError(t, err)
	assert.NotNil(t, pdfBytes)
	assert.Greater(t, len(pdfBytes), 0)

	// 验证PDF内容（PDF是二进制格式，只验证基本结构）
	assert.Contains(t, string(pdfBytes), "%PDF") // PDF文件头

	// 按照 GenerateAuthorizationDocument 的方式保存文件到本地
	// 1. 创建文件存储目录
	year := time.Now().Format("2006")
	month := time.Now().Format("01")
	dirPath := filepath.Join("data", "authorization_docs", year, month)
	if err := os.MkdirAll(dirPath, 0755); err != nil {
		t.Fatalf("创建存储目录失败: %v", err)
	}

	// 2. 生成文件名和路径
	fileName := fmt.Sprintf("test_auth_%s.pdf", time.Now().Format("20060102_150405"))
	filePath := filepath.Join(dirPath, fileName)
	relativePath := fmt.Sprintf("%s/%s/%s", year, month, fileName)

	// 3. 保存PDF文件
	if err := os.WriteFile(filePath, pdfBytes, 0644); err != nil {
		t.Fatalf("保存PDF文件失败: %v", err)
	}

	// 4. 验证文件是否保存成功
	if _, err := os.Stat(filePath); err == nil {
		t.Logf("✅ PDF文件已保存到本地")
		t.Logf("📄 文件名: %s", fileName)
		t.Logf("📁 文件路径: %s", filePath)
		t.Logf("🔗 相对路径: %s", relativePath)
		t.Logf("📊 文件大小: %d 字节", len(pdfBytes))
		
		// 获取绝对路径
		absPath, _ := filepath.Abs(filePath)
		t.Logf("📍 绝对路径: %s", absPath)
	} else {
		t.Errorf("❌ PDF文件保存失败: %v", err)
	}
}

// TestSavePDFToLocal 专门测试保存PDF文件到本地
func TestSavePDFToLocal(t *testing.T) {
	config := config.Config{
		Authorization: config.AuthorizationConfig{
			FileBaseURL: "https://test.com/api/v1/auth-docs",
		},
	}
	mockModel := &MockAuthorizationDocumentModel{}
	service := NewAuthorizationService(config, mockModel)

	// 准备测试数据
	userInfo := map[string]interface{}{
		"name":    "何志勇",
		"id_card": "452528197907133014",
		"mobile":  "18276151590",
	}

	// 生成PDF内容
	pdfBytes, err := service.generatePDFContent(userInfo)
	assert.NoError(t, err)
	assert.NotNil(t, pdfBytes)
	assert.Greater(t, len(pdfBytes), 0)

	// 按照 GenerateAuthorizationDocument 的方式保存文件到本地
	// 1. 创建文件存储目录
	year := time.Now().Format("2006")
	month := time.Now().Format("01")
	dirPath := filepath.Join("data", "authorization_docs", year, month)
	if err := os.MkdirAll(dirPath, 0755); err != nil {
		t.Fatalf("创建存储目录失败: %v", err)
	}

	// 2. 生成文件名和路径
	fileName := fmt.Sprintf("local_test_auth_%s.pdf", time.Now().Format("20060102_150405"))
	filePath := filepath.Join(dirPath, fileName)
	relativePath := fmt.Sprintf("%s/%s/%s", year, month, fileName)

	// 3. 保存PDF文件
	if err := os.WriteFile(filePath, pdfBytes, 0644); err != nil {
		t.Fatalf("保存PDF文件失败: %v", err)
	}

	// 4. 验证文件是否保存成功
	if _, err := os.Stat(filePath); err == nil {
		t.Logf("✅ PDF文件已保存到本地")
		t.Logf("📄 文件名: %s", fileName)
		t.Logf("📁 文件路径: %s", filePath)
		t.Logf("🔗 相对路径: %s", relativePath)
		t.Logf("📊 文件大小: %d 字节", len(pdfBytes))
		
		// 获取绝对路径
		absPath, _ := filepath.Abs(filePath)
		t.Logf("📍 绝对路径: %s", absPath)
		
		// 验证文件内容
		fileInfo, err := os.Stat(filePath)
		assert.NoError(t, err)
		assert.Greater(t, fileInfo.Size(), int64(1000)) // 文件应该大于1KB
		
		t.Logf("🎉 文件保存验证通过！")
	} else {
		t.Errorf("❌ PDF文件保存失败: %v", err)
	}
}

// TestGeneratePDFContent_EmptyUserInfo 测试空用户信息
func TestGeneratePDFContent_EmptyUserInfo(t *testing.T) {
	config := config.Config{
		Authorization: config.AuthorizationConfig{
			FileBaseURL: "https://test.com/api/v1/auth-docs",
		},
	}
	mockModel := &MockAuthorizationDocumentModel{}
	service := NewAuthorizationService(config, mockModel)

	userInfo := map[string]interface{}{}

	pdfBytes, err := service.generatePDFContent(userInfo)

	// 验证结果
	assert.NoError(t, err)
	assert.NotNil(t, pdfBytes)
	assert.Greater(t, len(pdfBytes), 0)

	// 验证PDF内容（PDF是二进制格式，只验证基本结构）
	assert.Contains(t, string(pdfBytes), "%PDF") // PDF文件头
}

// TestGetUserInfoString 测试获取用户信息字符串
func TestGetUserInfoString(t *testing.T) {
	userInfo := map[string]interface{}{
		"name":    "张三",
		"id_card": "110101199001011234",
		"age":     30,
		"empty":   "",
	}

	tests := []struct {
		name     string
		key      string
		expected string
	}{
		{
			name:     "正常字符串",
			key:      "name",
			expected: "张三",
		},
		{
			name:     "身份证号",
			key:      "id_card",
			expected: "110101199001011234",
		},
		{
			name:     "空字符串",
			key:      "empty",
			expected: "",
		},
		{
			name:     "不存在的键",
			key:      "not_exist",
			expected: "",
		},
		{
			name:     "非字符串类型",
			key:      "age",
			expected: "",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := getUserInfoString(userInfo, tt.key)
			assert.Equal(t, tt.expected, result)
		})
	}
}

// BenchmarkGeneratePDFContent 性能测试
func BenchmarkGeneratePDFContent(b *testing.B) {
	config := config.Config{
		Authorization: config.AuthorizationConfig{
			FileBaseURL: "https://test.com/api/v1/auth-docs",
		},
	}
	mockModel := &MockAuthorizationDocumentModel{}
	service := NewAuthorizationService(config, mockModel)

	userInfo := map[string]interface{}{
		"name":    "张三",
		"id_card": "110101199001011234",
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, err := service.generatePDFContent(userInfo)
		if err != nil {
			b.Fatal(err)
		}
	}
}

// TestAuthorizationService_Integration 集成测试（需要真实文件系统）
func TestAuthorizationService_Integration(t *testing.T) {
	// 创建测试配置
	config := config.Config{
		Authorization: config.AuthorizationConfig{
			FileBaseURL: "https://test.example.com/api/v1/auth-docs",
		},
	}

	// 创建模拟的数据库模型
	mockModel := &MockAuthorizationDocumentModel{}

	// 创建授权书服务
	service := NewAuthorizationService(config, mockModel)

	// 准备测试数据
	userInfo := map[string]interface{}{
		"name":    "王五",
		"id_card": "110101199001011236",
		"mobile":  "13800138002",
	}

	// 模拟数据库插入成功
	mockModel.On("Insert", mock.Anything, mock.Anything, mock.Anything).Return(
		&mockResult{lastInsertId: 1, rowsAffected: 1}, nil)

	// 执行测试
	authDoc, err := service.GenerateAuthorizationDocument(
		context.Background(),
		1, // userID
		2, // orderID
		3, // queryID
		userInfo,
	)

	// 验证结果
	assert.NoError(t, err)
	assert.NotNil(t, authDoc)

	// 测试GetFullFileURL方法
	fullURL := service.GetFullFileURL(authDoc.FileUrl)
	expectedURL := fmt.Sprintf("%s/%s", config.Authorization.FileBaseURL, authDoc.FileUrl)
	assert.Equal(t, expectedURL, fullURL)

	// 验证文件是否真的被创建
	if _, err := os.Stat(authDoc.FilePath); err == nil {
		t.Logf("✅ 集成测试成功 - 授权书文件已创建")
		t.Logf("📄 文件名: %s", authDoc.FileName)
		t.Logf("📁 文件路径: %s", authDoc.FilePath)
		t.Logf("🔗 相对路径: %s", authDoc.FileUrl)
		t.Logf("🌐 完整URL: %s", fullURL)
		t.Logf("📊 文件大小: %d 字节", authDoc.FileSize)
	} else {
		t.Logf("⚠️ 集成测试 - 文件未找到: %s", authDoc.FilePath)
	}

	// 验证数据库调用
	mockModel.AssertExpectations(t)
}

// TestGeneratePDFFile 专门测试PDF文件生成
func TestGeneratePDFFile(t *testing.T) {
	// 创建测试配置
	config := config.Config{
		Authorization: config.AuthorizationConfig{
			FileBaseURL: "https://test.example.com/api/v1/auth-docs",
		},
	}

	// 创建模拟的数据库模型
	mockModel := &MockAuthorizationDocumentModel{}

	// 创建授权书服务
	service := NewAuthorizationService(config, mockModel)

	// 准备测试数据
	userInfo := map[string]interface{}{
		"name":    "测试用户",
		"id_card": "110101199001011237",
		"mobile":  "13800138003",
	}

	// 模拟数据库插入成功
	mockModel.On("Insert", mock.Anything, mock.Anything, mock.Anything).Return(
		&mockResult{lastInsertId: 1, rowsAffected: 1}, nil)

	// 执行测试
	authDoc, err := service.GenerateAuthorizationDocument(
		context.Background(),
		999, // userID
		888, // orderID
		777, // queryID
		userInfo,
	)

	// 验证结果
	assert.NoError(t, err)
	assert.NotNil(t, authDoc)

	// 验证文件是否真的被创建
	if _, err := os.Stat(authDoc.FilePath); err == nil {
		t.Logf("✅ PDF文件生成成功！")
		t.Logf("📄 文件名: %s", authDoc.FileName)
		t.Logf("📁 文件路径: %s", authDoc.FilePath)
		t.Logf("🔗 相对路径: %s", authDoc.FileUrl)
		t.Logf("📊 文件大小: %d 字节", authDoc.FileSize)
		
		// 验证文件内容
		fileInfo, err := os.Stat(authDoc.FilePath)
		assert.NoError(t, err)
		assert.Greater(t, fileInfo.Size(), int64(1000)) // 文件应该大于1KB
		
		// 验证文件名格式
		assert.Regexp(t, `^auth_999_888_\d{8}_\d{6}\.pdf$`, authDoc.FileName)
		
		// 验证路径格式
		assert.Regexp(t, `^\d{4}/\d{2}/auth_999_888_\d{8}_\d{6}\.pdf$`, authDoc.FileUrl)
		
		t.Logf("🎉 所有验证通过！")
	} else {
		t.Errorf("❌ PDF文件未创建: %s", authDoc.FilePath)
	}

	// 验证数据库调用
	mockModel.AssertExpectations(t)
}
