package utils

import (
	stderrors "errors"
	"fmt"
	"strconv"
	"strings"

	"github.com/xuri/excelize/v2"
)

// CardInfo 卡信息(ICCID + MSISDN + VirtualNo)
type CardInfo struct {
	ICCID     string
	MSISDN    string
	VirtualNo string
}

// CSVParseResult Excel/CSV 解析结果
type CSVParseResult struct {
	Cards       []CardInfo
	TotalCount  int
	ParseErrors []CSVParseError
}

// CSVParseError Excel/CSV 解析错误
type CSVParseError struct {
	Line   int
	ICCID  string
	MSISDN string
	Reason string
}

// DeviceParseResult 设备导入 Excel 解析结果
type DeviceParseResult struct {
	Rows        []DeviceRow
	TotalCount  int
	ParseErrors []CSVParseError
}

// DeviceRow 设备导入数据行
type DeviceRow struct {
	Line         int
	VirtualNo    string
	SN           string
	IMEI         string // 设备IMEI，对应 Excel IMEI 列，用于 Gateway API 调用
	DeviceName   string
	DeviceModel  string
	DeviceType   string
	MaxSimSlots  int
	Manufacturer string
	SlotICCIDs   []DeviceSlotICCID
}

// DeviceSlotICCID 设备槽位与 ICCID 的映射
type DeviceSlotICCID struct {
	SlotPosition int
	ICCID        string
}

var (
	// ErrExcelNoSheets Excel文件无工作表
	ErrExcelNoSheets = stderrors.New("Excel文件无工作表")
	// ErrExcelNoData Excel文件无数据行
	ErrExcelNoData = stderrors.New("Excel文件无数据行(至少需要表头+1行数据)")
)

// ParseCardExcel 解析包含 ICCID 和 MSISDN 两列的 Excel 文件
// filePath: Excel文件路径(.xlsx格式)
// 返回: 解析结果 (与CSV解析器返回相同的数据结构)
func ParseCardExcel(filePath string) (*CSVParseResult, error) {
	// 1. 打开Excel文件
	f, err := excelize.OpenFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("打开Excel失败: %w", err)
	}
	defer func() {
		if err := f.Close(); err != nil {
			// 日志记录关闭错误,但不影响解析结果
		}
	}()

	// 2. 选择sheet (优先"导入数据",否则第一个)
	sheetName := selectSheet(f)
	if sheetName == "" {
		return nil, ErrExcelNoSheets
	}

	// 3. 读取所有行
	rows, err := f.GetRows(sheetName)
	if err != nil {
		return nil, fmt.Errorf("读取sheet失败: %w", err)
	}

	if len(rows) < 2 {
		return nil, ErrExcelNoData
	}

	// 4. 解析表头 + 数据行
	return parseCardRows(rows)
}

// ParseDeviceExcel 解析设备导入 Excel 文件
// filePath: Excel文件路径(.xlsx格式)
// 返回: 解析结果（包含有效行和失败行）、错误
func ParseDeviceExcel(filePath string) (*DeviceParseResult, error) {
	f, err := excelize.OpenFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("打开Excel失败: %w", err)
	}
	defer func() {
		if err := f.Close(); err != nil {
		}
	}()

	sheetName := selectSheet(f)
	if sheetName == "" {
		return nil, ErrExcelNoSheets
	}

	rows, err := f.GetRows(sheetName)
	if err != nil {
		return nil, fmt.Errorf("读取sheet失败: %w", err)
	}

	if len(rows) < 2 {
		return nil, ErrExcelNoData
	}

	parseResult := &DeviceParseResult{
		Rows:        make([]DeviceRow, 0),
		ParseErrors: make([]CSVParseError, 0),
	}

	// 按固定列位置读取，第1行为表头跳过：
	// col0=虚拟号 col1=SN col2=设备名称 col3=设备型号 col4=设备类型
	// col5=IMEI col6=制造商 col7=最大SIM槽数 col8~11=卡1~4 ICCID
	for i := 1; i < len(rows); i++ {
		record := rows[i]
		lineNum := i + 1

		row := DeviceRow{Line: lineNum}

		getCol := func(idx int) string {
			if idx < len(record) {
				return strings.TrimSpace(record[idx])
			}
			return ""
		}

		row.VirtualNo = getCol(0)
		row.SN = getCol(1)
		row.DeviceName = getCol(2)
		row.DeviceModel = getCol(3)
		row.DeviceType = getCol(4)
		row.IMEI = getCol(5)
		row.Manufacturer = getCol(6)
		maxSimSlotsStr := getCol(7)

		row.SlotICCIDs = make([]DeviceSlotICCID, 0, 4)
		for j := 8; j <= 11; j++ {
			iccid := getCol(j)
			if iccid != "" {
				row.SlotICCIDs = append(row.SlotICCIDs, DeviceSlotICCID{
					SlotPosition: j - 7,
					ICCID:        iccid,
				})
			}
		}

		// 整行目标列皆为空，视为空行跳过，不计入 total
		if row.VirtualNo == "" && row.SN == "" && row.DeviceName == "" && row.DeviceModel == "" &&
			row.DeviceType == "" && row.IMEI == "" && row.Manufacturer == "" &&
			maxSimSlotsStr == "" && len(row.SlotICCIDs) == 0 {
			continue
		}

		parseResult.TotalCount++

		if row.VirtualNo == "" {
			parseResult.ParseErrors = append(parseResult.ParseErrors, CSVParseError{
				Line:   lineNum,
				Reason: "设备虚拟号(virtual_no)不能为空",
			})
			continue
		}

		// 解析最大SIM槽数
		if maxSimSlotsStr != "" {
			if n, err := strconv.Atoi(maxSimSlotsStr); err == nil {
				row.MaxSimSlots = n
			}
		}

		// 范围校验：不在 [1,4] 则失败；为空/0 时回填默认值 4
		if row.MaxSimSlots == 0 {
			row.MaxSimSlots = 4
		} else if row.MaxSimSlots < 1 || row.MaxSimSlots > 4 {
			parseResult.ParseErrors = append(parseResult.ParseErrors, CSVParseError{
				Line:   lineNum,
				Reason: "最大SIM槽数必须在1-4之间",
			})
			continue
		}

		for _, slotICCID := range row.SlotICCIDs {
			if slotICCID.SlotPosition > row.MaxSimSlots {
				parseResult.ParseErrors = append(parseResult.ParseErrors, CSVParseError{
					Line:   lineNum,
					ICCID:  slotICCID.ICCID,
					Reason: "卡槽填写超出最大SIM槽数",
				})
				goto nextDeviceRow
			}
		}

		parseResult.Rows = append(parseResult.Rows, row)

	nextDeviceRow:
	}

	return parseResult, nil
}

// selectSheet 选择要读取的sheet
// 优先返回名为"导入数据"的sheet,否则返回第一个sheet
func selectSheet(f *excelize.File) string {
	sheets := f.GetSheetList()
	if len(sheets) == 0 {
		return ""
	}

	// 优先查找"导入数据"sheet
	for _, sheet := range sheets {
		if sheet == "导入数据" {
			return sheet
		}
	}

	// 返回第一个sheet
	return sheets[0]
}

// parseCardRows 解析卡数据行
// 第1行固定为表头跳过，按列位置取值：col0=ICCID、col1=MSISDN、col2=VirtualNo
func parseCardRows(rows [][]string) (*CSVParseResult, error) {
	result := &CSVParseResult{
		Cards:       make([]CardInfo, 0),
		ParseErrors: make([]CSVParseError, 0),
	}

	// 从第2行（索引1）开始，跳过第1行表头
	for i := 1; i < len(rows); i++ {
		row := rows[i]
		lineNum := i + 1

		iccid := ""
		msisdn := ""
		virtualNo := ""

		if len(row) >= 1 {
			iccid = strings.TrimSpace(row[0])
		}
		if len(row) >= 2 {
			msisdn = strings.TrimSpace(row[1])
		}
		if len(row) >= 3 {
			virtualNo = strings.TrimSpace(row[2])
		}

		// 三列皆为空视为空行，跳过且不计入 total
		if iccid == "" && msisdn == "" && virtualNo == "" {
			continue
		}

		result.TotalCount++

		if iccid == "" {
			result.ParseErrors = append(result.ParseErrors, CSVParseError{
				Line:   lineNum,
				MSISDN: msisdn,
				Reason: "ICCID 不能为空",
			})
			continue
		}

		if msisdn == "" {
			result.ParseErrors = append(result.ParseErrors, CSVParseError{
				Line:   lineNum,
				ICCID:  iccid,
				Reason: "MSISDN 不能为空",
			})
			continue
		}

		result.Cards = append(result.Cards, CardInfo{
			ICCID:     iccid,
			MSISDN:    msisdn,
			VirtualNo: virtualNo,
		})
	}

	return result, nil
}
