package controller

import (
	"fmt"
	"log"
	"os"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/gofiber/fiber/v3"
	"github.com/xuri/excelize/v2"

	"fafa-crawler/src/beans"
	"fafa-crawler/src/models"
	"fafa-crawler/src/util"
)

const (
	// 处理文件的总文件夹
	processFilePath = "/Users/panjunjie/Downloads/电商商品图片库/"
)

var (
	resultMu sync.Mutex
	wg       sync.WaitGroup
)

// SyncMeiTuanData 处理Excel文件上传请求，获取商品信息并处理图片
func (c *Controller) SyncMeiTuanData(ctx fiber.Ctx) error {
	start := time.Now()
	// 直接解析请求参数（Fiber v3自动处理JSON/表单）
	var params struct {
		Folders []string `json:"folders" form:"folders"` // 图片源目录路径
	}

	if err := ctx.Bind().JSON(&params); err != nil {
		return ctx.Status(fiber.StatusBadRequest).JSON(Error(fiber.StatusBadRequest, "无效的请求参数"))
	}
	if len(params.Folders) == 0 {
		return ctx.Status(fiber.StatusBadRequest).JSON(Error(fiber.StatusBadRequest, "请提供图片源目录路径"))
	}

	// 处理的图片保存路径
	saveImgRoot := "/Users/panjunjie/Downloads/meituan_img/"

	successCnt := 0

	for _, folder := range params.Folders {

		path := processFilePath + folder

		// 读取并合并商品数据
		productList, err := ReadProductsFromExcel(path)
		if err != nil {
			return ctx.JSON(Error(1001, "读取Excel文件失败: "+err.Error()))
		}

		productList2, err := ReadProductActivitiesFromExcel(path, productList)
		if err != nil {
			return ctx.JSON(Error(1002, "处理商品图片失败: "+err.Error()))
		}
		productList = append(productList, productList2...)

		// 批量处理商品图片
		productBeanList, err := batchCopyProductImages(productList, path, saveImgRoot)
		if err != nil {
			return ctx.JSON(Error(1003, "批量处理商品图片失败: "+err.Error()))
		}

		// 批量保存数据
		productService.SaveMeiTuanProducts(productBeanList)

		successCnt += len(productBeanList)
	}

	elapsed := time.Since(start)

	return ctx.JSON(Success(fiber.Map{
		"success_count": successCnt,
		"folder_count":  len(params.Folders),
		"elapsed_time":  elapsed.String(),
	}))
}

// readExcelData 读取Excel文件数据并返回指定工作表的行数据
// 参数: filePath - Excel文件路径, sheetIndex - 工作表索引
// 返回: 行数据列表和可能的错误
func readExcelData(filePath string, sheetIndex int) ([][]string, error) {
	excelFile, err := excelize.OpenFile(filePath)
	if err != nil {
		return nil, fmt.Errorf("打开excel文件失败: %w", err)
	}
	defer excelFile.Close()

	sheetList := excelFile.GetSheetList()
	if len(sheetList) <= sheetIndex {
		return nil, fmt.Errorf("工作表索引 %d 不存在，文件中共有 %d 个工作表", sheetIndex, len(sheetList))
	}

	sheetName := sheetList[sheetIndex]
	log.Printf("处理工作表: %q", sheetName)

	rows, err := excelFile.GetRows(sheetName)
	if err != nil {
		return nil, fmt.Errorf("获取工作表行数据失败: %w", err)
	}

	return rows, nil
}

// 常量定义
const (
	headerRowCount = 3 // 表头行数
)

// MeiTuanController 美团数据控制器
type MeiTuanController struct {
}

func NewMeiTuanController() *MeiTuanController {
	return &MeiTuanController{}
}

// 读取商品 Excel 文件填充结构体
// ReadProductsFromExcel 从Excel文件读取产品数据并转换为模型列表
// 参数: filePath - Excel文件路径
// 返回: 产品模型列表和可能的错误
func ReadProductsFromExcel(filePath string) ([]*models.LibraryProduct, error) {
	startTime := time.Now()
	// 打开Excel文件
	excelFileName := filePath + "/美团闪购.xlsx"
	rows, err := readExcelData(excelFileName, 0) // 读取第二个工作表(索引1)
	if err != nil {
		return nil, fmt.Errorf("读取Excel文件 %s失败: %w", excelFileName, err)
	}

	// 预分配空间，排除标题行（确保容量非负）
	capacity := len(rows) - headerRowCount
	if capacity < 0 {
		capacity = 0
	}
	productList := make([]*models.LibraryProduct, 0, capacity)

	titleClear := util.NewTitleCleaner()

	// 遍历每一行数据（跳过前3行标题和备注行）
	for rowIdx, row := range rows {
		// 跳过标题行和备注行（前3行）
		if rowIdx < headerRowCount {
			continue
		}

		// 跳过空行
		if isEmptyRow(row) {
			log.Printf("跳过空行: 第%d行", rowIdx+1)
			continue
		}

		// 创建产品模型
		product := &models.LibraryProduct{}

		// 解析单元格数据
		if err := parseRowToProduct(row, product, titleClear); err != nil {
			log.Printf("解析行数据失败(第%d行): %v, 已跳过", rowIdx+1, err)
			continue
		}

		// 验证产品名称
		if product.Name == "" {
			log.Printf("产品名称为空，跳过第%d行", rowIdx+1)
			continue
		}

		product.Source = "meituan"
		productList = append(productList, product)
	}

	elapsed := time.Since(startTime)
	log.Printf("excel解析完成，共处理 %d 个产品，耗时 %v", len(productList), elapsed)
	return productList, nil
}

// isEmptyRow 判断是否为空行
func isEmptyRow(row []string) bool {
	for _, cell := range row {
		if strings.TrimSpace(cell) != "" {
			return false
		}
	}
	return true
}

// parseRowToProduct 将Excel行数据解析到产品模型
func parseRowToProduct(row []string, product *models.LibraryProduct, titleClear *util.TitleCleaner) error {
	// 列索引到处理函数的映射，仅包含需要处理的列
	cellHandlers := map[int]func(string, *models.LibraryProduct) error{
		0: func(v string, p *models.LibraryProduct) error { // 条形码
			p.Barcode = emptyIf(v, "无条形码")
			return nil
		},
		1: func(v string, p *models.LibraryProduct) error { // 产品名称
			p.Title = v
			p.Name = titleClear.ExtractProductName(v)
			p.Sname = p.Name
			p.Thumb = titleClear.CleanForFileName(v)
			return nil
		},
		2: func(v string, p *models.LibraryProduct) error { // 规格
			// 处理规格字符串：替换分隔符并提取关键规格信息
			p.Spec = strings.ReplaceAll(v, "#", ";")
			newSpec := titleClear.ExtractSpec(v)
			if p.Spec == "" && newSpec != "" {
				p.Spec = newSpec
			}
			return nil
		},
		3: func(v string, p *models.LibraryProduct) error { // 价格
			if v == "" {
				p.Price = 0
				return nil
			}
			price, err := strconv.ParseFloat(v, 64)
			if err != nil {
				// return fmt.Errorf("第%d行价格格式错误: %s", rowNumber, v)
				price = 0
			}
			p.Price = price
			return nil
		},
		8: func(v string, p *models.LibraryProduct) error { // 分类
			p.Category = v
			return nil
		},
		9: func(v string, p *models.LibraryProduct) error { // 品牌
			p.Brand = emptyIf(v, "无品牌")
			return nil
		},
		10: func(v string, p *models.LibraryProduct) error { // 产地
			p.MadeIn = v
			return nil
		},
		11: func(v string, p *models.LibraryProduct) error { // 重量
			p.Weight = v
			return nil
		},
		12: func(v string, p *models.LibraryProduct) error { // 重量单位
			p.WeightUnit = v
			return nil
		},
		14: func(v string, p *models.LibraryProduct) error { // 一级分类
			p.Category1St = v
			return nil
		},
		15: func(v string, p *models.LibraryProduct) error { // 二级分类
			p.Category2Nd = v
			return nil
		},
		19: func(v string, p *models.LibraryProduct) error { // 卖点
			p.SellingPoint = v
			return nil
		},
		21: func(v string, p *models.LibraryProduct) error { // 介绍
			p.Intro = v
			return nil
		},
	}

	for i, cellValue := range row {
		if handler, ok := cellHandlers[i]; ok {
			if err := handler(strings.TrimSpace(cellValue), product); err != nil {
				return err
			}
		}
	}
	return nil
}

// parseRowToProductActivity 将Excel行数据解析到产品模型
func parseRowToProductActivity(row []string, product *models.LibraryProduct, titleClear *util.TitleCleaner) error {
	// 列索引到处理函数的映射，仅包含需要处理的列
	cellHandlers := map[int]func(string, *models.LibraryProduct) error{
		1: func(v string, p *models.LibraryProduct) error { // 条形码
			p.Barcode = emptyIf(v, "无条形码")
			return nil

		},
		3: func(v string, p *models.LibraryProduct) error { // 产品名称
			p.Title = v
			p.Name = titleClear.ExtractProductName(v)
			p.Sname = p.Name
			p.Thumb = titleClear.CleanForFileName(v)

			if p.Spec == "" {
				p.Spec = titleClear.ExtractSpec(v)
			}

			return nil
		},
		5: func(v string, p *models.LibraryProduct) error { // 价格
			price, err := strconv.ParseFloat(v, 64)
			if err != nil {
				price = 0
			}
			p.Price = price
			return nil
		},
	}

	for i, cellValue := range row {
		if handler, ok := cellHandlers[i]; ok {
			if err := handler(strings.TrimSpace(cellValue), product); err != nil {
				return err
			}
		}
	}
	return nil
}

func emptyIf(value, match string) string {
	if value == match {
		return ""
	}
	return value
}

// ReadProductActivitiesFromExcel 从Excel文件读取产品活动数据
// 参数: filePath - Excel文件路径, oldProductList - 已有产品列表用于去重
// 返回: 产品模型列表和可能的错误
func ReadProductActivitiesFromExcel(filePath string, oldProductList []*models.LibraryProduct) ([]*models.LibraryProduct, error) {
	startTime := time.Now()
	// 创建产品索引映射用于快速去重
	productIndex := make(map[string]bool)
	for _, p := range oldProductList {
		productIndex[p.Barcode] = true
		productIndex[p.Name] = true
	}

	// 读取Excel数据
	excelFileName := filePath + "/美团闪购折扣.xlsx"
	rows, err := readExcelData(excelFileName, 1) // 读取第二个工作表(索引1)
	if err != nil {
		return nil, fmt.Errorf("读取Excel文件 %s失败: %w", excelFileName, err)
	}

	titleClear := util.NewTitleCleaner()

	// 预分配空间，排除标题行（确保容量非负）
	capacity := len(rows) - headerRowCount
	if capacity < 0 {
		capacity = 0
	}
	productList := make([]*models.LibraryProduct, 0, capacity)

	// 遍历行数据并解析
	for rowIdx, row := range rows {
		if rowIdx < headerRowCount || isEmptyRow(row) {
			continue
		}

		product := &models.LibraryProduct{}
		if err := parseRowToProductActivity(row, product, titleClear); err != nil {
			log.Printf("解析行数据失败(第%d行): %v, 已跳过", rowIdx+1, err)
			continue
		}

		// 验证并检查重复
		if product.Name == "" {
			//log.Printf("产品名称为空，跳过第%d行", rowIdx+1)
			continue
		}

		if productIndex[product.Barcode] || productIndex[product.Name] {
			// log.Printf("产品已存在(条形码: %s, 名称: %s)，跳过第%d行", product.Barcode, product.Name, rowIdx+1)
			continue
		}

		productList = append(productList, product)
		productIndex[product.Barcode] = true
		productIndex[product.Name] = true
	}

	elapsed := time.Since(startTime)
	log.Printf("活动excel解析完成，共处理 %d 个产品，耗时 %v", len(productList), elapsed)

	return productList, nil
}

// batchCopyProductImages 批量复制商品图片，并将匹配的图片路径赋值给商品信息
// 参数:
//
//	productList - 商品列表
//	srcPath - 图片源目录路径
//	saveImgRoot - 图片保存目录路径
//
// 返回:
//
//	包含商品和图片信息的列表，以及可能的错误
func batchCopyProductImages(productList []*models.LibraryProduct, srcPath, saveImgRoot string) ([]*beans.ProductMeiTuanBean, error) {
	startTime := time.Now()
	result := make([]*beans.ProductMeiTuanBean, 0, len(productList))
	if len(productList) == 0 {
		return result, nil
	}

	// 1. 验证图片源目录
	if _, err := os.Stat(srcPath); os.IsNotExist(err) {
		return nil, fmt.Errorf("目录不存在: %w", err)
	}

	// 2. 合并分散的图片文件夹
	processor := util.NewImageProcessor(srcPath)
	mergePath, err := processor.Merge()
	if err != nil {
		return nil, fmt.Errorf("图片合并失败: %w", err)
	}

	// 3. 构建图片索引 (优化查询性能: O(N+M)替代O(N*M))
	imageFiles, err := os.ReadDir(mergePath)
	if err != nil {
		return nil, fmt.Errorf("读取图片目录失败: %w", err)
	}

	// 图片索引: key=处理后的标题前缀, value=匹配的图片列表
	imageIndex := make(map[string][]string)
	for _, file := range imageFiles {
		if file.IsDir() {
			continue
		}

		imageName := file.Name()
		imageNameExt := filepath.Ext(imageName)
		imageNameWithoutExt := strings.TrimSuffix(imageName, imageNameExt)

		// 移除末尾的-数字后缀 (如"abc-1.jpg" → "abc")
		processedName := regexp.MustCompile(`-\d+$`).ReplaceAllString(imageNameWithoutExt, "")
		// 限制前缀长度，避免索引键过长
		prefix := processedName
		if len(prefix) > 250 {
			prefix = prefix[:250]
		}

		imageIndex[prefix] = append(imageIndex[prefix], imageName)
	}

	// log.Printf("图片字典长度:%v", imageIndex)

	// 4. 准备保存目录
	nowFolder := time.Now().Format("20060102")
	saveRoot := filepath.Join(saveImgRoot, nowFolder)
	virtualSaveRoot := filepath.Join("/", "media", "images", "goods_library", nowFolder)
	if err := os.MkdirAll(saveRoot, 0755); err != nil {
		return nil, fmt.Errorf("创建目录失败: %w", err)
	}

	// 5. 并发处理商品图片
	for _, product := range productList {
		if product.Title == "" {
			continue
		}

		wg.Add(1)

		go func(p *models.LibraryProduct) {
			defer wg.Done()

			// 5.1 查找匹配的图片: 优先条形码匹配，再标题匹配
			matchedImages := imageIndex[p.Barcode]

			// p.Thumb 临时符合规范的图片名称
			matchKey := p.Thumb
			if len(matchKey) > 250 {
				matchKey = matchKey[:250]
			}
			matchedImages = append(matchedImages, imageIndex[matchKey]...)

			// 5.2 去重处理: 使用map确保图片唯一
			uniqueImages := make([]string, 0, len(matchedImages))
			seenImages := make(map[string]bool)
			for _, img := range matchedImages {
				if !seenImages[img] {
					seenImages[img] = true
					uniqueImages = append(uniqueImages, img)
				}
			}
			matchedImages = uniqueImages

			// 如果商品匹配不到图片，则跳过
			if len(matchedImages) == 0 {
				return
			}

			// 5.3 确定主图: 优先匹配条形码/标题，无则取第一张
			var mainImage string
			for _, imgName := range matchedImages {
				fileExt := filepath.Ext(imgName)
				nameWithoutExt := util.CleanProductForFilePath(strings.TrimSuffix(imgName, fileExt))
				if mainImage == "" && (nameWithoutExt == p.Barcode || nameWithoutExt == matchKey) {
					mainImage = imgName
					break
				}
			}

			if mainImage == "" {
				mainImage = matchedImages[0]
			}

			// 5.5 并发复制图片: 并行处理图片复制并生成ProductImage
			var (
				productImages []*models.LibraryProductImage
				copyWg        sync.WaitGroup
				copyMu        sync.Mutex
			)

			thumbNewName := ""

			for idx, imgName := range matchedImages {
				copyWg.Add(1)
				go func(i int, img string) {
					defer copyWg.Done()
					fileExt := filepath.Ext(img)
					imgNewName := util.GenerateUUID2FileName(fileExt, false)
					absSavePath := filepath.Join(saveRoot, imgNewName)
					if err := util.CopyFile(filepath.Join(mergePath, img), absSavePath); err != nil {
						log.Printf("图片复制失败 %s: %v", img, err)
						return
					}

					var isMainImg int32 = 2
					if img == mainImage {
						isMainImg = 1
					}
					if isMainImg == 1 {
						thumbNewName = imgNewName
					}

					// 创建图片信息并添加到列表
					image := &models.LibraryProductImage{
						ProductID: p.ID,
						ImageURL:  fmt.Sprintf("%s/%s", virtualSaveRoot, imgNewName),
						IsMain:    isMainImg,
						Seq:       int32(i + 1),
					}

					copyMu.Lock()
					productImages = append(productImages, image)
					copyMu.Unlock()
				}(idx, imgName)
			}

			copyWg.Wait()

			// 5.4 处理主图URL: 生成随机文件名并更新商品缩略图
			p.Thumb = fmt.Sprintf("%s/%s", virtualSaveRoot, thumbNewName)

			// 5.6 收集结果: 加锁保护结果切片
			resultMu.Lock()
			result = append(result, &beans.ProductMeiTuanBean{
				Product:   p,
				ImageList: productImages,
			})
			resultMu.Unlock()

		}(product)
	}
	wg.Wait()

	// 6. 输出处理结果: 记录耗时和处理数量
	elapsed := time.Since(startTime)
	log.Printf("填充数据，并上传图片，实际共处理 %d 个产品，耗时 %v", len(result), elapsed)
	return result, nil
}
