package utils

import (
	"path/filepath"
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"github.com/xuri/excelize/v2"
)

// createTestCardExcel 创建测试用的 ICCID+MSISDN Excel 文件
func createTestCardExcel(t *testing.T, filename string, headers []string, rows [][]string) string {
	t.Helper()

	tmpDir := t.TempDir()
	filePath := filepath.Join(tmpDir, filename)

	f := excelize.NewFile()
	defer func() {
		if err := f.Close(); err != nil {
			t.Logf("关闭Excel文件失败: %v", err)
		}
	}()

	sheetName := "Sheet1"

	// 写入表头
	if len(headers) > 0 {
		for i, header := range headers {
			cell, _ := excelize.CoordinatesToCellName(i+1, 1)
			f.SetCellValue(sheetName, cell, header)
		}
	}

	// 写入数据行
	for rowIdx, row := range rows {
		for colIdx, value := range row {
			cell, _ := excelize.CoordinatesToCellName(colIdx+1, rowIdx+2)
			f.SetCellValue(sheetName, cell, value)
		}
	}

	err := f.SaveAs(filePath)
	require.NoError(t, err, "保存Excel文件失败")

	return filePath
}

// createTestDeviceExcel 创建测试用的设备导入 Excel 文件
func createTestDeviceExcel(t *testing.T, filename string, headers []string, rows [][]string) string {
	t.Helper()

	tmpDir := t.TempDir()
	filePath := filepath.Join(tmpDir, filename)

	f := excelize.NewFile()
	defer func() {
		if err := f.Close(); err != nil {
			t.Logf("关闭Excel文件失败: %v", err)
		}
	}()

	sheetName := "Sheet1"

	// 写入表头
	for i, header := range headers {
		cell, _ := excelize.CoordinatesToCellName(i+1, 1)
		f.SetCellValue(sheetName, cell, header)
	}

	// 写入数据行
	for rowIdx, row := range rows {
		for colIdx, value := range row {
			cell, _ := excelize.CoordinatesToCellName(colIdx+1, rowIdx+2)
			f.SetCellValue(sheetName, cell, value)
		}
	}

	err := f.SaveAs(filePath)
	require.NoError(t, err, "保存Excel文件失败")

	return filePath
}

func TestParseCardExcel(t *testing.T) {
	tests := []struct {
		name           string
		headers        []string
		rows           [][]string
		wantCardCount  int
		wantErrorCount int
		wantError      bool
		errorContains  string
	}{
		{
			name:    "标准双列格式-英文表头",
			headers: []string{"ICCID", "MSISDN"},
			rows: [][]string{
				{"89860012345678901234", "13800000001"},
				{"89860012345678901235", "13800000002"},
			},
			wantCardCount:  2,
			wantErrorCount: 0,
			wantError:      false,
		},
		{
			name:    "中文表头",
			headers: []string{"卡号", "接入号"},
			rows: [][]string{
				{"89860012345678901234", "13800000001"},
				{"89860012345678901235", "13800000002"},
			},
			wantCardCount:  2,
			wantErrorCount: 0,
			wantError:      false,
		},
		{
			name:    "混合中英文表头",
			headers: []string{"ICCID", "手机号"},
			rows: [][]string{
				{"89860012345678901234", "13800000001"},
			},
			wantCardCount:  1,
			wantErrorCount: 0,
			wantError:      false,
		},
		{
			name:    "ICCID为空-应记录错误",
			headers: []string{"ICCID", "MSISDN"},
			rows: [][]string{
				{"89860012345678901234", "13800000001"},
				{"", "13800000002"},
			},
			wantCardCount:  1,
			wantErrorCount: 1,
			wantError:      false,
		},
		{
			name:    "MSISDN为空-应记录错误",
			headers: []string{"ICCID", "MSISDN"},
			rows: [][]string{
				{"89860012345678901234", "13800000001"},
				{"89860012345678901235", ""},
			},
			wantCardCount:  1,
			wantErrorCount: 1,
			wantError:      false,
		},
		{
			name:    "跳过空行",
			headers: []string{"ICCID", "MSISDN"},
			rows: [][]string{
				{"89860012345678901234", "13800000001"},
				{"", ""},
				{"89860012345678901235", "13800000002"},
			},
			wantCardCount:  2,
			wantErrorCount: 0,
			wantError:      false,
		},
		{
			name:    "无表头-直接解析数据",
			headers: nil,
			rows: [][]string{
				{"89860012345678901234", "13800000001"},
				{"89860012345678901235", "13800000002"},
			},
			wantCardCount:  2,
			wantErrorCount: 0,
			wantError:      false,
		},
		{
			name:    "20位长数字无损",
			headers: []string{"ICCID", "MSISDN"},
			rows: [][]string{
				{"12345678901234567890", "13800000001"},
			},
			wantCardCount:  1,
			wantErrorCount: 0,
			wantError:      false,
		},
		{
			name:    "首尾空格自动去除",
			headers: []string{"ICCID", "MSISDN"},
			rows: [][]string{
				{"  89860012345678901234  ", "  13800000001  "},
			},
			wantCardCount:  1,
			wantErrorCount: 0,
			wantError:      false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 创建测试Excel文件
			filePath := createTestCardExcel(t, "test_cards.xlsx", tt.headers, tt.rows)

			// 解析Excel
			result, err := ParseCardExcel(filePath)

			// 验证错误
			if tt.wantError {
				require.Error(t, err)
				if tt.errorContains != "" {
					assert.Contains(t, err.Error(), tt.errorContains)
				}
				return
			}

			require.NoError(t, err)
			require.NotNil(t, result)

			// 验证结果
			assert.Equal(t, tt.wantCardCount, len(result.Cards), "卡数量不匹配")
			assert.Equal(t, tt.wantErrorCount, len(result.ParseErrors), "错误数量不匹配")

			// 验证首尾空格被去除
			if tt.name == "首尾空格自动去除" && len(result.Cards) > 0 {
				assert.Equal(t, "89860012345678901234", result.Cards[0].ICCID)
				assert.Equal(t, "13800000001", result.Cards[0].MSISDN)
			}
		})
	}
}

func TestParseCardExcel_ErrorScenarios(t *testing.T) {
	tests := []struct {
		name          string
		setupFunc     func(t *testing.T) string
		wantError     bool
		errorContains string
	}{
		{
			name: "文件不存在",
			setupFunc: func(t *testing.T) string {
				return "/nonexistent/file.xlsx"
			},
			wantError:     true,
			errorContains: "打开Excel失败",
		},
		{
			name: "Excel无数据行",
			setupFunc: func(t *testing.T) string {
				tmpDir := t.TempDir()
				filePath := filepath.Join(tmpDir, "empty.xlsx")
				f := excelize.NewFile()
				defer f.Close()

				// 只写入表头，无数据行
				f.SetCellValue("Sheet1", "A1", "ICCID")
				f.SetCellValue("Sheet1", "B1", "MSISDN")

				f.SaveAs(filePath)
				return filePath
			},
			wantError:     true,
			errorContains: "Excel文件无数据行",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			filePath := tt.setupFunc(t)

			result, err := ParseCardExcel(filePath)

			if tt.wantError {
				require.Error(t, err)
				if tt.errorContains != "" {
					assert.Contains(t, err.Error(), tt.errorContains)
				}
				assert.Nil(t, result)
			} else {
				require.NoError(t, err)
			}
		})
	}
}

func TestParseDeviceExcel(t *testing.T) {
	tests := []struct {
		name          string
		headers       []string
		rows          [][]string
		wantCount     int
		wantError     bool
		errorContains string
		validateFunc  func(t *testing.T, rows []DeviceRow)
	}{
		{
			name: "标准10列格式",
			headers: []string{
				"device_no", "device_name", "device_model", "device_type",
				"max_sim_slots", "manufacturer", "iccid_1", "iccid_2", "iccid_3", "iccid_4",
			},
			rows: [][]string{
				{"DEV-001", "GPS追踪器A", "GT06N", "GPS Tracker", "4", "Concox", "89860012345678901234", "89860012345678901235", "", ""},
				{"DEV-002", "GPS追踪器B", "GT06N", "GPS Tracker", "4", "Concox", "89860012345678901236", "", "", ""},
			},
			wantCount: 2,
			wantError: false,
			validateFunc: func(t *testing.T, rows []DeviceRow) {
				assert.Equal(t, "DEV-001", rows[0].DeviceNo)
				assert.Equal(t, "GPS追踪器A", rows[0].DeviceName)
				assert.Equal(t, 4, rows[0].MaxSimSlots)
				assert.Equal(t, 2, len(rows[0].ICCIDs))

				assert.Equal(t, "DEV-002", rows[1].DeviceNo)
				assert.Equal(t, 1, len(rows[1].ICCIDs))
			},
		},
		{
			name: "可选列缺失-应使用默认值",
			headers: []string{
				"device_no", "iccid_1",
			},
			rows: [][]string{
				{"DEV-003", "89860012345678901234"},
			},
			wantCount: 1,
			wantError: false,
			validateFunc: func(t *testing.T, rows []DeviceRow) {
				assert.Equal(t, "DEV-003", rows[0].DeviceNo)
				assert.Equal(t, 4, rows[0].MaxSimSlots, "max_sim_slots应默认为4")
				assert.Equal(t, "", rows[0].DeviceName)
			},
		},
		{
			name: "ICCID列解析-全部4个插槽",
			headers: []string{
				"device_no", "iccid_1", "iccid_2", "iccid_3", "iccid_4",
			},
			rows: [][]string{
				{"DEV-004", "89860012345678901234", "89860012345678901235", "89860012345678901236", "89860012345678901237"},
			},
			wantCount: 1,
			wantError: false,
			validateFunc: func(t *testing.T, rows []DeviceRow) {
				assert.Equal(t, 4, len(rows[0].ICCIDs))
			},
		},
		{
			name: "跳过device_no为空的行",
			headers: []string{
				"device_no", "iccid_1",
			},
			rows: [][]string{
				{"DEV-005", "89860012345678901234"},
				{"", "89860012345678901235"},
				{"DEV-006", "89860012345678901236"},
			},
			wantCount: 2,
			wantError: false,
			validateFunc: func(t *testing.T, rows []DeviceRow) {
				assert.Equal(t, "DEV-005", rows[0].DeviceNo)
				assert.Equal(t, "DEV-006", rows[1].DeviceNo)
			},
		},
		{
			name: "max_sim_slots字符串转整数",
			headers: []string{
				"device_no", "max_sim_slots", "iccid_1",
			},
			rows: [][]string{
				{"DEV-007", "2", "89860012345678901234"},
			},
			wantCount: 1,
			wantError: false,
			validateFunc: func(t *testing.T, rows []DeviceRow) {
				assert.Equal(t, 2, rows[0].MaxSimSlots)
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// 创建测试Excel文件
			filePath := createTestDeviceExcel(t, "test_devices.xlsx", tt.headers, tt.rows)

			// 解析Excel
			rows, count, err := ParseDeviceExcel(filePath)

			// 验证错误
			if tt.wantError {
				require.Error(t, err)
				if tt.errorContains != "" {
					assert.Contains(t, err.Error(), tt.errorContains)
				}
				return
			}

			require.NoError(t, err)
			assert.Equal(t, tt.wantCount, count, "设备数量不匹配")
			assert.Equal(t, tt.wantCount, len(rows), "返回的行数不匹配")

			// 执行自定义验证
			if tt.validateFunc != nil {
				tt.validateFunc(t, rows)
			}
		})
	}
}

func TestParseDeviceExcel_ErrorScenarios(t *testing.T) {
	tests := []struct {
		name          string
		setupFunc     func(t *testing.T) string
		wantError     bool
		errorContains string
	}{
		{
			name: "文件不存在",
			setupFunc: func(t *testing.T) string {
				return "/nonexistent/device.xlsx"
			},
			wantError:     true,
			errorContains: "打开Excel失败",
		},
		{
			name: "Excel无数据行",
			setupFunc: func(t *testing.T) string {
				tmpDir := t.TempDir()
				filePath := filepath.Join(tmpDir, "empty_device.xlsx")
				f := excelize.NewFile()
				defer f.Close()

				// 只写入表头，无数据行
				f.SetCellValue("Sheet1", "A1", "device_no")

				f.SaveAs(filePath)
				return filePath
			},
			wantError:     true,
			errorContains: "Excel文件无数据行",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			filePath := tt.setupFunc(t)

			rows, count, err := ParseDeviceExcel(filePath)

			if tt.wantError {
				require.Error(t, err)
				if tt.errorContains != "" {
					assert.Contains(t, err.Error(), tt.errorContains)
				}
				assert.Nil(t, rows)
				assert.Equal(t, 0, count)
			} else {
				require.NoError(t, err)
			}
		})
	}
}

func TestSelectSheet(t *testing.T) {
	tests := []struct {
		name          string
		setupFunc     func() *excelize.File
		expectedSheet string
	}{
		{
			name: "优先选择'导入数据'sheet",
			setupFunc: func() *excelize.File {
				f := excelize.NewFile()
				f.NewSheet("Sheet1")
				f.NewSheet("导入数据")
				f.NewSheet("Sheet2")
				return f
			},
			expectedSheet: "导入数据",
		},
		{
			name: "无'导入数据'sheet-返回第一个",
			setupFunc: func() *excelize.File {
				f := excelize.NewFile()
				return f
			},
			expectedSheet: "Sheet1",
		},
		{
			name: "删除默认sheet后-返回空字符串",
			setupFunc: func() *excelize.File {
				f := excelize.NewFile()
				// excelize创建新文件时会有默认的Sheet1，删除后仍会返回Sheet1
				// 这是库的行为，我们只验证没有崩溃
				f.DeleteSheet("Sheet1")
				return f
			},
			expectedSheet: "Sheet1", // excelize的默认行为
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			f := tt.setupFunc()
			defer f.Close()

			result := selectSheet(f)
			assert.Equal(t, tt.expectedSheet, result)
		})
	}
}

func TestFindCardColumns(t *testing.T) {
	tests := []struct {
		name          string
		header        []string
		wantICCIDCol  int
		wantMSISDNCol int
	}{
		{
			name:          "标准英文表头",
			header:        []string{"ICCID", "MSISDN"},
			wantICCIDCol:  0,
			wantMSISDNCol: 1,
		},
		{
			name:          "小写英文表头",
			header:        []string{"iccid", "msisdn"},
			wantICCIDCol:  0,
			wantMSISDNCol: 1,
		},
		{
			name:          "中文表头",
			header:        []string{"卡号", "接入号"},
			wantICCIDCol:  0,
			wantMSISDNCol: 1,
		},
		{
			name:          "混合表头",
			header:        []string{"ICCID", "手机号"},
			wantICCIDCol:  0,
			wantMSISDNCol: 1,
		},
		{
			name:          "表头顺序颠倒",
			header:        []string{"MSISDN", "ICCID"},
			wantICCIDCol:  1,
			wantMSISDNCol: 0,
		},
		{
			name:          "表头包含空格",
			header:        []string{"  ICCID  ", "  MSISDN  "},
			wantICCIDCol:  0,
			wantMSISDNCol: 1,
		},
		{
			name:          "无法识别的表头",
			header:        []string{"unknown1", "unknown2"},
			wantICCIDCol:  -1,
			wantMSISDNCol: -1,
		},
		{
			name:          "只有ICCID列",
			header:        []string{"ICCID", "其他"},
			wantICCIDCol:  0,
			wantMSISDNCol: -1,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			iccidCol, msisdnCol := findCardColumns(tt.header)
			assert.Equal(t, tt.wantICCIDCol, iccidCol, "ICCID列索引不匹配")
			assert.Equal(t, tt.wantMSISDNCol, msisdnCol, "MSISDN列索引不匹配")
		})
	}
}

func TestBuildDeviceColumnIndex(t *testing.T) {
	tests := []struct {
		name          string
		header        []string
		expectedIndex map[string]int
	}{
		{
			name: "标准10列表头",
			header: []string{
				"device_no", "device_name", "device_model", "device_type",
				"max_sim_slots", "manufacturer", "iccid_1", "iccid_2", "iccid_3", "iccid_4",
			},
			expectedIndex: map[string]int{
				"device_no":     0,
				"device_name":   1,
				"device_model":  2,
				"device_type":   3,
				"max_sim_slots": 4,
				"manufacturer":  5,
				"iccid_1":       6,
				"iccid_2":       7,
				"iccid_3":       8,
				"iccid_4":       9,
			},
		},
		{
			name:   "顺序颠倒",
			header: []string{"iccid_1", "device_no"},
			expectedIndex: map[string]int{
				"iccid_1":       0,
				"device_no":     1,
				"device_name":   -1,
				"device_model":  -1,
				"device_type":   -1,
				"max_sim_slots": -1,
				"manufacturer":  -1,
				"iccid_2":       -1,
				"iccid_3":       -1,
				"iccid_4":       -1,
			},
		},
		{
			name:   "大写表头-能识别",
			header: []string{"DEVICE_NO", "DEVICE_NAME"},
			expectedIndex: map[string]int{
				"device_no":     0,
				"device_name":   1,
				"device_model":  -1,
				"device_type":   -1,
				"max_sim_slots": -1,
				"manufacturer":  -1,
				"iccid_1":       -1,
				"iccid_2":       -1,
				"iccid_3":       -1,
				"iccid_4":       -1,
			},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			result := buildDeviceColumnIndex(tt.header)
			assert.Equal(t, tt.expectedIndex, result)
		})
	}
}

// TestParseCardExcel_RealWorldScenario 测试真实场景
func TestParseCardExcel_RealWorldScenario(t *testing.T) {
	t.Run("100行数据性能测试", func(t *testing.T) {
		// 生成100行测试数据
		headers := []string{"ICCID", "MSISDN"}
		rows := make([][]string, 100)
		for i := 0; i < 100; i++ {
			iccid := "8986001234567890" + padLeft(i, 4)
			msisdn := "1380000" + padLeft(i, 4)
			rows[i] = []string{iccid, msisdn}
		}

		filePath := createTestCardExcel(t, "large_cards.xlsx", headers, rows)

		result, err := ParseCardExcel(filePath)
		require.NoError(t, err)
		assert.Equal(t, 100, len(result.Cards))
		assert.Equal(t, 0, len(result.ParseErrors))
	})
}

// padLeft 左侧填充0
func padLeft(num int, width int) string {
	s := ""
	for i := 0; i < width; i++ {
		s += "0"
	}
	s += string(rune('0' + num%10))
	if num >= 10 {
		s = s[:width-2] + string(rune('0'+num/10%10)) + string(rune('0'+num%10))
	}
	if num >= 100 {
		s = s[:width-3] + string(rune('0'+num/100%10)) + string(rune('0'+num/10%10)) + string(rune('0'+num%10))
	}
	if num >= 1000 {
		s = string(rune('0'+num/1000%10)) + string(rune('0'+num/100%10)) + string(rune('0'+num/10%10)) + string(rune('0'+num%10))
	}
	return s
}
