package plant

import (
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"strings"
	"sundynix-go/global"
	"sundynix-go/model/commom/request"
	"sundynix-go/model/plant"
	"sundynix-go/model/plant/response"
	"sundynix-go/pkg/httpclient"

	"go.uber.org/zap"
)

type OcrService struct{}

// ClassifyPlant 植物识别
func (s *OcrService) ClassifyPlant(file multipart.File, header *multipart.FileHeader, userId string) (response.PlantRecognitionResponse, error) {
	reqUrl := "https://aip.baidubce.com/rest/2.0/image-classify/v1/plant?access_token=" + getAccessToken()
	// 3. 读取文件的全部字节
	fileBytes, err := io.ReadAll(file)
	if err != nil {
		global.Logger.Error("读取文件失败!", zap.Error(err))
	}
	// 4. 将字节流编码为 Base64 字符串（可选 URLEncoding，根据场景选择）
	// 去掉编码头进行urlencode
	base64Str := base64.StdEncoding.EncodeToString(fileBytes)
	escapedBase64 := url.QueryEscape(base64Str)
	payload := strings.NewReader("image=" + escapedBase64 + "&baike_num=1")
	myHttpClient := httpclient.GetClient()
	req, err := http.NewRequest("POST", reqUrl, payload)
	if err != nil {
		global.Logger.Error("创建请求失败!", zap.Error(err))
		return response.PlantRecognitionResponse{}, err
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Accept", "application/json")
	resp, err := myHttpClient.Do(req)
	if err != nil {
		global.Logger.Error("请求百度接口失败!", zap.Error(err))
		return response.PlantRecognitionResponse{}, err
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		global.Logger.Error("解析百度接口响应失败!", zap.Error(err))
		return response.PlantRecognitionResponse{}, err
	}
	// 3. 解析JSON到结构体
	var plantResp response.PlantRecognitionResponse
	if err = json.Unmarshal(body, &plantResp); err != nil {
		global.Logger.Error("解析识别JSON失败!", zap.Error(err))
	}

	//4.异步写入库
	go func(userId string, apiResp response.PlantRecognitionResponse) {
		// A. 安全防护：防止协程 Panic 导致程序崩溃
		defer func() {
			if r := recover(); r != nil {
				global.Logger.Error("异步入库发生 Panic", zap.Any("recover", r))
			}
		}()
		var dbResults plant.ResultsArray = make(plant.ResultsArray, 0, len(apiResp.Result))
		// 2. 循环搬运数据
		for _, item := range apiResp.Result {
			// 处理嵌套的 BaikeInfo 指针
			var baikeInfo *plant.BaikeInfo
			if item.BaikeInfo != nil {
				baikeInfo = &plant.BaikeInfo{
					BaikeUrl:    item.BaikeInfo.BaikeUrl,
					ImageUrl:    item.BaikeInfo.ImageUrl,
					Description: item.BaikeInfo.Description,
				}
			}
			// 添加转换后的对象
			dbResults = append(dbResults, plant.ResultItem{
				Score:     item.Score,
				Name:      item.Name,
				BaikeInfo: baikeInfo, // 赋值刚才处理好的指针
			})
		}
		record := plant.ClassifyRecord{
			UserId:     userId,
			LogId:      apiResp.LogId,
			AllResults: dbResults,
		}
		if err := global.DB.Create(&record).Error; err != nil {
			global.Logger.Error("异步植物识别结果入库失败", zap.Error(err))
		}
	}(userId, plantResp)
	// 立即返回结果
	return plantResp, err

}

// MyClassifyLog 我的植物识别记录
func (s *OcrService) MyClassifyLog(req request.PageInfo, id string) (list interface{}, total int64, err error) {
	limit := req.PageSize
	offset := req.PageSize * (req.Current - 1)
	db := global.DB.Model(&plant.ClassifyRecord{})
	var records []plant.ClassifyRecord
	err = db.Count(&total).Error
	if err != nil {
		return
	}
	db = db.Where("user_id = ?", id).Limit(limit).Offset(offset).Order("created_at desc").Find(&records)
	return records, total, err

}

// DeleteClassifyLog 删除植物识别记录
func (s *OcrService) DeleteClassifyLog(req request.IdsReq, userId string) error {
	return global.DB.Where("id in ? and user_id = ?", req.Ids, userId).Unscoped().Delete(&plant.ClassifyRecord{}).Error
}

func getAccessToken() string {
	rpcUrl := "https://aip.baidubce.com/oauth/2.0/token"
	postData := fmt.Sprintf("grant_type=client_credentials&client_id=%s&client_secret=%s", global.Config.BaiduImgClassify.ApiKey, global.Config.BaiduImgClassify.SecretKey)
	resp, err := http.Post(rpcUrl, "application/x-www-form-urlencoded", strings.NewReader(postData))
	if err != nil {
		fmt.Println(err)
		return ""
	}
	defer resp.Body.Close()
	body, err := io.ReadAll(resp.Body)
	if err != nil {
		fmt.Println(err)
		return ""
	}
	accessTokenObj := map[string]any{}
	_ = json.Unmarshal([]byte(body), &accessTokenObj)
	return accessTokenObj["access_token"].(string)
}
