package logic

import (
	"bytes"
	"context"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net/http"
	"strings"

	plantModel "sundynix-micro-go/app/plant/model"
	"sundynix-micro-go/app/plant/rpc/internal/config"
)

func wikiVectorID(wikiID string) string {
	sum := md5.Sum([]byte("sundynix-plant-wiki:" + wikiID))
	return hex.EncodeToString(sum[:])
}

func buildWikiVectorText(w plantModel.Wiki) string {
	return fmt.Sprintf("植物名字:%s. 拉丁名:%s. 别名:%s. 科属:%s. 分布区域:%s. 生命周期:%s. 生长习性:%s. 繁殖方法:%s. 病虫害:%s. 光照强度:%s. 光照类型:%s. 最佳温度:%s. 茎:%s. 叶型:%s. 叶色:%s. 叶形:%s. 高度:%d厘米. 开花期:%s. 花色:%s. 花形:%s. 花直径:%d厘米. 果实:%s.",
		w.Name, w.LatinName, w.Aliases, w.Genus, w.DistributionArea, w.LifeCycle,
		w.GrowthHabit, w.ReproductionMethod, w.PestsDiseases, w.LightIntensity,
		w.LightType, w.OptimalTempPeriod, w.Stem, w.FoliageType, w.FoliageColor,
		w.FoliageShape, w.Height, w.FloweringPeriod, w.FloweringColor,
		w.FloweringShape, w.FlowerDiameter, w.Fruit)
}

func embeddingModel(c config.Config) string {
	if c.Ai.EmbeddingModelName != "" {
		return c.Ai.EmbeddingModelName
	}
	return "text-embedding-3-small"
}

func createEmbedding(ctx context.Context, c config.Config, text string) ([]float32, error) {
	body, _ := json.Marshal(map[string]interface{}{
		"model": embeddingModel(c),
		"input": text,
	})
	req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.Ai.EmbeddingApiUrl, bytes.NewReader(body))
	if err != nil {
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Authorization", "Bearer "+c.Ai.EmbeddingApiKey)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		raw, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
		return nil, fmt.Errorf("embedding 请求失败: %s %s", resp.Status, strings.TrimSpace(string(raw)))
	}
	var parsed struct {
		Data []struct {
			Embedding []float32 `json:"embedding"`
		} `json:"data"`
	}
	if err := json.NewDecoder(resp.Body).Decode(&parsed); err != nil {
		return nil, err
	}
	if len(parsed.Data) == 0 || len(parsed.Data[0].Embedding) == 0 {
		return nil, errors.New("embedding 响应为空")
	}
	return parsed.Data[0].Embedding, nil
}

func qdrantURL(c config.Config, path string) string {
	return strings.TrimRight(c.Ai.QdrantUrl, "/") + path
}

func doQdrant(ctx context.Context, c config.Config, method, path string, body interface{}) error {
	var reader io.Reader
	if body != nil {
		raw, _ := json.Marshal(body)
		reader = bytes.NewReader(raw)
	}
	req, err := http.NewRequestWithContext(ctx, method, qdrantURL(c, path), reader)
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	if c.Ai.QdrantApiKey != "" {
		req.Header.Set("api-key", c.Ai.QdrantApiKey)
	}
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		raw, _ := io.ReadAll(io.LimitReader(resp.Body, 4096))
		return fmt.Errorf("qdrant 请求失败: %s %s", resp.Status, strings.TrimSpace(string(raw)))
	}
	return nil
}

func ensureQdrantCollection(ctx context.Context, c config.Config, dim int) error {
	getReq, err := http.NewRequestWithContext(ctx, http.MethodGet, qdrantURL(c, "/collections/"+c.Ai.QdrantCollection), nil)
	if err != nil {
		return err
	}
	if c.Ai.QdrantApiKey != "" {
		getReq.Header.Set("api-key", c.Ai.QdrantApiKey)
	}
	if resp, err := http.DefaultClient.Do(getReq); err == nil {
		_ = resp.Body.Close()
		if resp.StatusCode >= 200 && resp.StatusCode < 300 {
			return nil
		}
	}
	if dim <= 0 {
		dim = c.Ai.VectorDimension
	}
	if dim <= 0 {
		dim = 1536
	}
	return doQdrant(ctx, c, http.MethodPut, "/collections/"+c.Ai.QdrantCollection, map[string]interface{}{
		"vectors": map[string]interface{}{
			"size":     dim,
			"distance": "Cosine",
		},
	})
}

func upsertWikiVector(ctx context.Context, c config.Config, w plantModel.Wiki) error {
	text := buildWikiVectorText(w)
	vector, err := createEmbedding(ctx, c, text)
	if err != nil {
		return err
	}
	if err := ensureQdrantCollection(ctx, c, len(vector)); err != nil {
		return err
	}
	return doQdrant(ctx, c, http.MethodPut, "/collections/"+c.Ai.QdrantCollection+"/points?wait=true", map[string]interface{}{
		"points": []map[string]interface{}{
			{
				"id":     wikiVectorID(w.ID),
				"vector": vector,
				"payload": map[string]interface{}{
					"wiki_id":   w.ID,
					"name":      w.Name,
					"full_text": text,
				},
			},
		},
	})
}

func deleteWikiVector(ctx context.Context, c config.Config, wikiID string) error {
	return doQdrant(ctx, c, http.MethodPost, "/collections/"+c.Ai.QdrantCollection+"/points/delete?wait=true", map[string]interface{}{
		"points": []string{wikiVectorID(wikiID)},
	})
}
