// Package history 是会话短期多轮历史的存储后端（Redis CacheDB）。
// 与长期偏好记忆（Postgres）分开：历史是易失的、按会话滚动保留最近 N 轮。
package history

import (
	"context"
	"encoding/json"
	"log"
	"time"

	"github.com/redis/go-redis/v9"
)

// maxTurns 是单会话保留的最近消息条数（user/assistant 各算一条）。
const maxTurns = 20

// Turn 是一条历史消息。
type Turn struct {
	Role    string `json:"role"`    // user / assistant
	Content string `json:"content"`
}

// Store 封装会话历史读写。rdb 为 nil 表示降级（无 Redis 时历史为空，不阻断）。
type Store struct{ rdb *redis.Client }

// Open 连接 Redis。失败不 fatal：返回降级实例。
func Open(addr string) *Store {
	rdb := redis.NewClient(&redis.Options{Addr: addr})
	ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
	defer cancel()
	if err := rdb.Ping(ctx).Err(); err != nil {
		log.Printf("[history] redis 不可用，会话历史降级（为空）: %v", err)
		_ = rdb.Close()
		return &Store{}
	}
	log.Println("[history] redis connected")
	return &Store{rdb: rdb}
}

func key(session string) string { return "sundynix:history:" + session }

// Get 返回某会话最近的历史（按时间正序）。
func (s *Store) Get(ctx context.Context, session string) ([]Turn, error) {
	if s.rdb == nil || session == "" {
		return nil, nil
	}
	// 列表头插尾老：取全部后反转为正序。
	vals, err := s.rdb.LRange(ctx, key(session), 0, maxTurns-1).Result()
	if err != nil {
		return nil, err
	}
	turns := make([]Turn, 0, len(vals))
	for i := len(vals) - 1; i >= 0; i-- { // 反转 → 正序
		var t Turn
		if json.Unmarshal([]byte(vals[i]), &t) == nil {
			turns = append(turns, t)
		}
	}
	return turns, nil
}

// Append 追加一条消息并裁剪到最近 maxTurns 条，刷新 TTL。
func (s *Store) Append(ctx context.Context, session, role, content string) error {
	if s.rdb == nil || session == "" {
		return nil
	}
	data, err := json.Marshal(Turn{Role: role, Content: content})
	if err != nil {
		return err
	}
	pipe := s.rdb.TxPipeline()
	pipe.LPush(ctx, key(session), data)
	pipe.LTrim(ctx, key(session), 0, maxTurns-1)
	pipe.Expire(ctx, key(session), 24*time.Hour)
	_, err = pipe.Exec(ctx)
	return err
}

// Close 释放连接。
func (s *Store) Close() {
	if s.rdb != nil {
		_ = s.rdb.Close()
	}
}
