package compapi

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"strings"
	"wechat-api/internal/types"
)

// 包装了原始的 API 响应，并提供了解析助手方法
type ChatResult struct {
	*types.CompOpenApiResp
	err error
}

// NewChatResult 是 ChatResult 的构造函数
func NewChatResult(resp any) *ChatResult {
	nresp, err := AnyToCompOpenApiResp(resp)
	res := ChatResult{nresp, err}
	if nresp == nil {
		res.err = errors.New("CompOpenApiRes is nil")
	}
	return &res
}

// 以下是ChatResul助手方法

// GetContentString 返回第一个 Choice 的 Message Content (标准方式)
func (r *ChatResult) GetContentString() (string, error) {
	var (
		content string = ""
		err     error
	)
	if r.err == nil && len(r.Choices) > 0 {
		content = r.Choices[0].Message.Content
	} else if r.err == nil && len(r.Choices) == 0 {
		err = errors.New("choices empty")
	}
	return content, err
}

func (r *ChatResult) GetContentJsonStr() (string, error) {
	var (
		content string = ""
		err     error
	)
	if r.err == nil && len(r.Choices) > 0 {
		content = r.Choices[0].Message.Content
	} else if r.err == nil && len(r.Choices) == 0 {
		err = errors.New("choices empty")
	}
	if !IsOpenaiModel(r.Model) { //不支持Response Schema的要特殊处理一下
		content, _, err = ExtractJSONContent(content)
		if err != nil {
			return "", fmt.Errorf("GCJS ExtractJSONContent err:'%s'", err)
		}
	}
	return content, err
}

// ParseContentAs 解析 Message Content 中的 JSON 到指定的 Go 结构体
// target 必须是一个指向目标结构体实例的指针 (e.g., &MyStruct{})
func (r *ChatResult) ParseContentAs(target any) error {
	content, err := r.GetContentString()
	if err != nil {
		return fmt.Errorf("parseContent err: %s", err)
	} else if content == "" {
		return errors.New("parseContent err: content is empty or unavailable")
	}
	if !IsOpenaiModel(r.Model) { //不支持Response Schema的要特殊处理一下
		content, _, err = ExtractJSONContent(content)
		if err != nil {
			return fmt.Errorf("PCA ExtractJSONContent err:'%s'", err)
		}
	}
	return ParseContentAs(content, target, false)
}

func AnyToBytes(in any) ([]byte, error) {
	switch v := in.(type) {
	case string:
		return []byte(v), nil
	case []byte:
		return v, nil
	default:
		return json.Marshal(v)
	}
}

func AnyToCompOpenApiResp(in any) (*types.CompOpenApiResp, error) {
	if resp, ok := in.(*types.CompOpenApiResp); ok {
		return resp, nil
	}
	if resp, ok := in.(types.CompOpenApiResp); ok {
		return &resp, nil
	}
	bs, err := AnyToBytes(in)
	if err != nil {
		return nil, err
	}
	nresp := &types.CompOpenApiResp{}
	err = json.Unmarshal(bs, nresp)
	if err != nil {
		return nil, err
	}
	return nresp, nil
}

func AnyToCompApiReq(in any) (*types.CompApiReq, error) {
	if req, ok := in.(*types.CompApiReq); ok {
		return req, nil
	}
	if req, ok := in.(types.CompApiReq); ok {
		return &req, nil
	}
	bs, err := AnyToBytes(in)
	if err != nil {
		return nil, err
	}
	nreq := &types.CompApiReq{}
	err = json.Unmarshal(bs, nreq)
	if err != nil {
		return nil, err
	}
	return nreq, nil
}

func CheckJSON(input any, checkEmpty bool) (bool, error) {
	inputBytes, err := AnyToBytes(input)
	if err != nil {
		return false, err
	}
	var raw json.RawMessage
	err = json.Unmarshal(inputBytes, &raw)
	if err != nil {
		return false, fmt.Errorf("input is not valid JSON: %w", err)
	}
	if checkEmpty {
		trimmed := bytes.TrimSpace(inputBytes)
		if len(trimmed) == 0 {
			return false, fmt.Errorf("input is empty")
		}
	}
	return true, nil
}

func WrapJSON(input any, warpKey string, checkValid bool) ([]byte, error) {
	var (
		inputBytes  []byte
		outputBytes []byte
		err         error
	)
	if inputBytes, err = AnyToBytes(input); err != nil {
		return nil, err
	}

	if checkValid {
		if _, err = CheckJSON(inputBytes, true); err != nil {
			return nil, err
		}
	}
	if len(warpKey) == 0 {
		return inputBytes, nil
	}
	wrapper := map[string]json.RawMessage{
		warpKey: json.RawMessage(inputBytes),
	}
	if outputBytes, err = json.Marshal(wrapper); err != nil {
		return nil, fmt.Errorf("failed to marshal wrapper structure: %w", err)
	}
	return outputBytes, nil
}

func ParseContentAs(content string, target any, removeJsonBlock bool) error {

	if removeJsonBlock &&
		strings.HasPrefix(content, "```json") && strings.HasSuffix(content, "```") {
		content = strings.TrimSuffix(strings.TrimPrefix(content, "```json"), "```")
		content = strings.TrimSpace(content)
	}

	if err := json.Unmarshal([]byte(content), target); err != nil {
		return fmt.Errorf("parseContent err:failed to unmarshal"+
			" content JSON into target type '%w'", err)
	}
	return nil
}

func ExtractJSONContent(s string) (string, bool, error) {
	startMarker := "```json"
	endMarker := "```"

	// 寻找起始标记
	startIdx := strings.Index(s, startMarker)
	if startIdx == -1 {
		return s, false, nil // 没有起始标记就不进行之后步骤
	}

	// 寻找结束标记（需在起始标记之后查找）
	endIdx := strings.LastIndex(s, endMarker)
	if endIdx == -1 || endIdx <= startIdx {
		return s, false, errors.New("lost endMarker") // 没有结束标记或标记顺序错误
	}

	// 计算内容范围
	contentStart := startIdx + len(startMarker)
	contentEnd := endIdx

	// 提取内容并去除前后空白
	content := strings.TrimSpace(s[contentStart:contentEnd])

	// 若内容为空视为无效
	if content == "" {
		return s, false, errors.New("empty content")
	}

	return content, true, nil
}
