package excel

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"mime/multipart"
	"reflect"
	"strconv"

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

func GetExcelData(file multipart.File, dst interface{}) (err error) {
	// 创建缓冲区
	buf := new(bytes.Buffer)

	// 将文件内容复制到缓冲区
	_, err = io.Copy(buf, file)
	if err != nil {
		fmt.Println(err)
		// c.String(http.StatusInternalServerError, "读取失败")
		err = errors.New("读取失败")
		return
	}
	// 创建Excel文件对象
	f, err := excelize.OpenReader(bytes.NewReader(buf.Bytes()))
	if err != nil {
		fmt.Println(err)
		// c.String(http.StatusInternalServerError, "Excel读取失败")
		err = errors.New("Excel读取失败")
		return
	}
	err = ImportExcel(f, dst, 1, 2)
	// if err != nil {
	// 	fmt.Println(err)
	// }
	return err
}

// ImportExcel 导入数据（单个sheet）
// 需要在传入的结构体中的字段加上tag：excel:"title:列头名称;"
// f 获取到的excel对象、dst 导入目标对象【传指针】
// headIndex 表头的索引，从0开始（用于获取表头名字）
// startRow 头行行数（从第startRow+1行开始扫）
func ImportExcel(f *excelize.File, dst interface{}, headIndex, startRow int) (err error) {
	sheetName := f.GetSheetName(0) // 单个sheet时，默认读取第一个sheet
	err = importData(f, dst, sheetName, headIndex, startRow)
	return
}

// ImportBySheet 导入数据（读取指定sheet）sheetName Sheet名称
func ImportBySheet(f *excelize.File, dst interface{}, sheetName string, headIndex, startRow int) (err error) {
	// 当需要读取多个sheet时，可以通过下面的方式，来调用 ImportBySheet 这个函数
	//sheetList := f.GetSheetList()
	//for _, sheetName := range sheetList {
	//	ImportBySheet(f,dst,sheetName,headIndex,startRow)
	//}
	err = importData(f, dst, sheetName, headIndex, startRow)
	return
}

// 获取在数组中得下标
func GetIndex(items []string, item string) int {
	for i, v := range items {
		if v == item {
			return i
		}
	}
	return -1
}

// 判断数组中是否包含指定元素
// func IsContain(items interface{}, item interface{}) bool {
// 	switch items.(type) {
// 	case []int:
// 		intArr := items.([]int)
// 		for _, value := range intArr {
// 			if value == item.(int) {
// 				return true
// 			}
// 		}
// 	case []string:
// 		strArr := items.([]string)
// 		for _, value := range strArr {
// 			if value == item.(string) {
// 				return true
// 			}
// 		}
// 	default:
// 		return false
// 	}
// 	return false
// }

// 解析数据
func importData(f *excelize.File, dst interface{}, sheetName string, headIndex, startRow int) (err error) {
	rows, err := f.GetRows(sheetName) // 获取所有行
	if err != nil {
		err = errors.New(sheetName + "工作表不存在")
		return
	}
	dataValue := reflect.ValueOf(dst) // 取目标对象的元素类型、字段类型和 tag
	// 判断数据的类型
	if dataValue.Kind() != reflect.Ptr || dataValue.Elem().Kind() != reflect.Slice {
		err = errors.New("invalid data type")
	}
	heads := []string{}                        // 表头
	dataType := dataValue.Elem().Type().Elem() // 获取导入目标对象的类型信息
	// 遍历行，解析数据并填充到目标对象中
	for rowIndex, row := range rows {
		if rowIndex == headIndex {
			heads = row
		}
		if rowIndex < startRow { // 跳过头行
			continue
		}
		newData := reflect.New(dataType).Elem() // 创建新的目标对象
		// 遍历目标对象的字段
		for i := 0; i < dataType.NumField(); i++ {
			// 这里要用构造函数，构造函数里指定了Index默认值为-1，当目标结构体的tag没有指定index的话，那么 excelTag.Index 就一直为0
			// 那么 row[excelizeIndex] 就始终是 row[0]，始终拿的是第一列的数据
			var excelTag = NewExcelTag()
			field := dataType.Field(i) // 获取字段信息和tag
			tag := field.Tag.Get(ExcelTagKey)
			if tag == "" { // 如果tag不存在，则跳过
				continue
			}
			err = excelTag.GetTag(tag)
			if err != nil {
				return
			}
			cellValue := ""
			if excelTag.Index >= 0 { // 当tag里指定了index时，根据这个index来拿数据
				excelizeIndex := excelTag.Index // 解析tag的值
				if excelizeIndex >= len(row) {  // 防止下标越界
					continue
				}
				cellValue = row[excelizeIndex] // 获取单元格的值
			} else { // 否则根据表头名称来拿数据
				var index = GetIndex(heads, excelTag.Name)
				if index != -1 {
					if index >= len(row) { // 防止下标越界
						continue
					}
					cellValue = row[index] // 获取单元格的值
				}
			}
			fmt.Println("Type.Name:", field.Type.Name(), field.Type.Kind())
			// 根据字段类型设置值
			switch field.Type.Kind() {
			case reflect.Int:
				v, _ := strconv.ParseInt(cellValue, 10, 64)
				newData.Field(i).SetInt(v)
			case reflect.Int64:
				v, _ := strconv.ParseInt(cellValue, 10, 64)
				newData.Field(i).SetInt(v)
			case reflect.Uint:
				v, _ := strconv.ParseUint(cellValue, 10, 64)
				newData.Field(i).SetUint(v)
			case reflect.Uint8:
				v, _ := strconv.ParseUint(cellValue, 10, 64)
				newData.Field(i).SetUint(v)
			case reflect.Uint16:
				v, _ := strconv.ParseUint(cellValue, 10, 64)
				newData.Field(i).SetUint(v)
			case reflect.String:
				newData.Field(i).SetString(cellValue)
			}
		}
		// 将新的目标对象添加到导入目标对象的slice中
		dataValue.Elem().Set(reflect.Append(dataValue.Elem(), newData))
	}
	return
}
