package spider

import (
	"encoding/json"
	"io"
	"log"
	"net/http"
	"regexp"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/tidwall/gjson"
	"gorm.io/gorm"

	"my-spiders/internal/model"
)

const (
	VogueBaseURL  = "https://www.vogue.com"
	VoguePlatform = "vogue"
)

type VogueSpider struct {
	Client       *http.Client
	DB           *gorm.DB
	MaxCo        int  // 最大并发数
	BrandID      uint // 客户端指定的品牌ID
	OnlyPlatform bool // 是否仅抓取该平台关联的品牌
	Debug        bool // 供 s.Debug 使用
	ForceUpdate  bool // 供 s.ForceUpdate 使用
}

func NewVogueSpider(db *gorm.DB, maxCo int, brandID uint, onlyPlatform bool) *VogueSpider {
	if maxCo <= 0 {
		maxCo = 5 // 默认限制 5 个并发，防止打满带宽
	}
	return &VogueSpider{
		DB:           db,
		MaxCo:        maxCo,
		BrandID:      brandID,
		OnlyPlatform: onlyPlatform,
		Client: &http.Client{
			Timeout: 30 * time.Second,
		},
	}
}

// Run 爬虫启动总入口
func (s *VogueSpider) Run() {
	tasks, err := s.getTask()
	if err != nil {
		log.Printf("[错误] 获取任务失败: %v", err)
		return
	}

	log.Printf("[Info] 成功获取 %d 个品牌任务，开始执行...", len(tasks))

	// 品牌级别的并发控制
	brandSem := make(chan struct{}, s.MaxCo)
	var wg sync.WaitGroup

	for _, task := range tasks {
		brandSem <- struct{}{}
		wg.Add(1)

		go func(t model.AppBrand) {
			defer func() {
				<-brandSem
				wg.Done()
			}()
			s.SpiderStart(t)
		}(task)
	}

	wg.Wait()
	log.Println("[Info] Vogue 所有抓取任务已完成！")
}

// getTask 获取需要抓取的品牌任务
func (s *VogueSpider) getTask() ([]model.AppBrand, error) {
	// 1. 本地无数据库时的 Mock 调试场景
	if s.DB == nil {
		log.Println("[Debug] 当前未连接数据库，启动 Mock 任务数据模式")
		brandID := s.BrandID
		if brandID == 0 {
			brandID = 108 // 默认给一个 Chanel 示例 ID
		}

		return []model.AppBrand{
			{ID: int64(brandID), Name: "Alexander McQueen", SpiderOrigin: VoguePlatform},
		}, nil
	}

	// 2. 有数据库时的查询逻辑
	var brands []model.AppBrand
	query := s.DB.Model(&model.AppBrand{})

	if s.BrandID > 0 {
		query = query.Where("id = ?", s.BrandID)
	} else {
		query = query.Where("id > ?", 1)
		if s.OnlyPlatform {
			query = query.Where("spider_origin = ?", VoguePlatform)
		}
		query = query.Order("id asc")
	}

	err := query.Find(&brands).Error
	return brands, err
}

// SpiderStart 针对单个品牌开启抓取
func (s *VogueSpider) SpiderStart(task model.AppBrand) {
	brandName := s.getTaskName(task.Name)
	url := VogueBaseURL + "/fashion-shows/designer/" + brandName
	log.Printf("[Command] brandName: %s; spiderUrl: %s", brandName, url)

	showsList := s.getShowsList(url)
	if len(showsList) == 0 {
		log.Printf("[Warning] 未找到品牌 [%s] 的发布会列表", brandName)
		return
	}

	// 核心修复：限制该品牌下发布会详情页的抓取并发数
	detailSem := make(chan struct{}, s.MaxCo)
	var wg sync.WaitGroup

	for _, list := range showsList {
		detailSem <- struct{}{} // 超过 MaxCo 时会自动卡住等待
		wg.Add(1)

		go func(info gjson.Result) {
			defer func() {
				<-detailSem // 释放槽位
				wg.Done()
			}()

			s.getDetail(task.ID, info)

			// 频控：每次下载后稍作停顿，保护带宽并防封 IP
			time.Sleep(200 * time.Millisecond)
		}(list)
	}

	wg.Wait()
}

// getShowsList 获取品牌的发布会列表 JSON 数据
func (s *VogueSpider) getShowsList(url string) []gjson.Result {
	body, httpCode := s.request(url)

	if httpCode == 200 && body != "" {
		re := regexp.MustCompile(`(?s)window\.__PRELOADED_STATE__\s*=\s*(.*?);</script>`)
		matches := re.FindStringSubmatch(body)
		if len(matches) > 1 {
			collections := gjson.Get(matches[1], "transformed.runwayDesignerContent.designerCollections")
			return collections.Array()
		}
	}

	log.Printf("[Info] %s 未找到数据.", url)
	return nil
}

// getDetail 获取并解析单个发布会详情
func (s *VogueSpider) getDetail(brandID int64, info gjson.Result) {
	hed := info.Get("hed").String()

	var exist model.BrandRunway

	// 如果处于非 Debug 模式，进行数据库查询判重
	if !s.Debug && s.DB != nil {
		s.DB.Where("brand_id = ? AND title = ?", brandID, hed).First(&exist)
		if exist.ID > 0 && !s.ForceUpdate {
			return
		}
	}

	// 如果非 forceUpdate 且记录已存在，跳过更新
	if exist.ID > 0 && !s.ForceUpdate {
		return
	}

	// 获取图片（保持你原本的 URL 拼接与请求逻辑）
	pageURI := info.Get("url").String()
	requestURL := VogueBaseURL + pageURI + "/slideshow/collection"
	log.Printf("正在匹配发布会详情 %s", requestURL)

	body, httpCode := s.request(requestURL)
	if httpCode != 200 || body == "" {
		log.Printf("[Warning] %s 请求失败.", requestURL)
		return
	}

	re := regexp.MustCompile(`(?s)window\.__PRELOADED_STATE__\s*=\s*(.*?);<`)
	matches := re.FindStringSubmatch(body)

	if len(matches) <= 1 {
		return
	}

	imagesResult := gjson.Get(matches[1], "transformed.runwayGalleries.galleries.0.items")

	if !imagesResult.Exists() {
		log.Printf("[Warning] %s 获取图片失败.", requestURL)
		return
	}

	type ImageItem struct {
		Src string `json:"src"`
	}

	var saveURL []ImageItem
	var detailURL []ImageItem

	// 保持你原有的图片与细节图提取逻辑
	for _, img := range imagesResult.Array() {
		xxlURL := img.Get("image.sources.xxl.url").String()
		if xxlURL != "" {
			saveURL = append(saveURL, ImageItem{Src: xxlURL})
			log.Printf(xxlURL)
		}

		// 详情图片提取
		for _, detail := range img.Get("details").Array() {
			detailXxlURL := detail.Get("image.sources.xxl.url").String()
			if detailXxlURL != "" {
				detailURL = append(detailURL, ImageItem{Src: detailXxlURL})
			}
		}
	}

	// ---------------- 下面对接你的双表数据库结构 ----------------

	// 1. 整理图片与封面
	cover := ""
	if len(saveURL) > 0 {
		cover = saveURL[0].Src
	}

	// 汇总所有图片（主图 + 细节图）准备存入 brand_runway_images 表
	var allImages []string
	for _, item := range saveURL {
		allImages = append(allImages, item.Src)
	}
	for _, item := range detailURL {
		allImages = append(allImages, item.Src)
	}

	now := time.Now().Unix()

	// 2. 构建主表 BrandRunway 结构体
	runway := model.BrandRunway{
		Title:      hed,
		BrandID:    uint(brandID),
		Year:       uint16(s.parseYear(hed)),
		Cover:      cover,
		SourceURL:  requestURL,
		ImageCount: uint16(len(allImages)),
		CreatedAt:  now,
		UpdatedAt:  now,
	}

	// 3. Debug 模式或无数据库连接时控制台输出
	if s.Debug || s.DB == nil {
		out, _ := json.MarshalIndent(runway, "", "  ")
		log.Printf("\n================ [DEBUG OUTPUT] ================\n%s\n提取图片总数: %d 张\n================================================", string(out), len(allImages))
		return
	}

	// 4. 执行数据库事务保存（主表 brand_runway + 从表 brand_runway_images）
	err := s.DB.Transaction(func(tx *gorm.DB) error {
		// 判断是更新还是新增
		if exist.ID > 0 {
			runway.ID = exist.ID
			if err := tx.Save(&runway).Error; err != nil {
				return err
			}
			// 删除旧关联图片
			if err := tx.Where("runway_id = ?", exist.ID).Delete(&model.BrandRunwayImage{}).Error; err != nil {
				return err
			}
		} else {
			if err := tx.Create(&runway).Error; err != nil {
				return err
			}
		}

		// 批量插入图片从表
		var runwayImages []model.BrandRunwayImage
		for idx, imgURL := range allImages {
			runwayImages = append(runwayImages, model.BrandRunwayImage{
				RunwayID:  runway.ID, // 获取上面生成的自增 ID
				BrandID:   uint(brandID),
				Image:     imgURL,
				SortOrder: uint(idx + 1),
				CreatedAt: now,
				UpdatedAt: now,
			})
		}

		if len(runwayImages) > 0 {
			if err := tx.Create(&runwayImages).Error; err != nil {
				return err
			}
		}

		return nil
	})

	if err != nil {
		log.Printf("[错误] 数据入库失败 [%s]: %v", hed, err)
	} else {
		log.Printf("[成功] 保存发布会: %s (共 %d 张图片)", hed, len(allImages))
	}
}

// getTaskName 品牌名称格式化（对应 getTaskName）
func (s *VogueSpider) getTaskName(name string) string {
	r := strings.NewReplacer(".", "-", " ", "-", "&", "")
	log.Printf("[Debug] 原始品牌名: %s; 格式化后: %s", name, strings.ToLower(r.Replace(name)))
	return strings.ToLower(r.Replace(name))
}

// HTTP 请求封装
func (s *VogueSpider) request(url string) (string, int) {
	req, err := http.NewRequest("GET", url, nil)
	if err != nil {
		return "", 0
	}

	req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36")

	resp, err := s.Client.Do(req)
	if err != nil {
		return "", 0
	}
	defer resp.Body.Close()

	bodyBytes, err := io.ReadAll(resp.Body)
	if err != nil {
		return "", resp.StatusCode
	}

	return string(bodyBytes), resp.StatusCode
}

// parseYear 从标题中提取 4 位年份数字（如 Fall 2024 Ready-to-Wear -> 2024）
func (s *VogueSpider) parseYear(title string) int {
	re := regexp.MustCompile(`\b(19|20)\d{2}\b`)
	match := re.FindString(title)
	if match != "" {
		y, _ := strconv.Atoi(match)
		return y
	}
	return time.Now().Year()
}
