package fuiou

import (
	"encoding/xml"
	"fmt"
	"io"
	"net/url"
	"strings"
)

// ParseNotify 解析回调通知（不验签）
// 用于在验签前提取订单号以确定使用哪套支付配置
func ParseNotify(rawBody []byte) (*NotifyRequest, error) {
	utf8Str, err := decodeNotifyXML(rawBody)
	if err != nil {
		return nil, err
	}
	var notify NotifyRequest
	if err := xml.Unmarshal([]byte(utf8Str), &notify); err != nil {
		return nil, fmt.Errorf("XML 解析回调数据失败: %w", err)
	}
	return &notify, nil
}

// VerifyNotify 验证回调通知并解析数据
// rawBody: 原始请求 body（可能包含 req= 前缀的 URL 编码 GBK XML）
func (c *Client) VerifyNotify(rawBody []byte) (*NotifyRequest, error) {
	utf8Str, err := decodeNotifyXML(rawBody)
	if err != nil {
		return nil, err
	}
	var notify NotifyRequest
	if err := xml.Unmarshal([]byte(utf8Str), &notify); err != nil {
		return nil, fmt.Errorf("XML 解析回调数据失败: %w", err)
	}

	signStr, err := buildNotifySignString(utf8Str)
	if err != nil {
		return nil, err
	}

	// 富友回调字段会随支付通道扩展，验签必须以 XML 实际出现的字段为准。
	if err := c.verifySignString(signStr, notify.Sign); err != nil {
		return nil, fmt.Errorf("回调签名验证失败: %w", err)
	}

	// 检查结果码
	if notify.ResultCode != "000000" {
		return &notify, fmt.Errorf("回调结果非成功: %s - %s", notify.ResultCode, notify.ResultMsg)
	}

	return &notify, nil
}

// buildNotifySignString 从回调 XML 实际字段构建验签原文。
func buildNotifySignString(utf8XML string) (string, error) {
	fields, err := extractNotifySignFields(utf8XML)
	if err != nil {
		return "", err
	}
	return buildSignStringFromMap(fields), nil
}

// extractNotifySignFields 提取 XML 根节点下参与验签的字段。
func extractNotifySignFields(utf8XML string) (map[string]string, error) {
	fields := make(map[string]string)
	decoder := xml.NewDecoder(strings.NewReader(utf8XML))
	depth := 0
	currentField := ""

	for {
		token, err := decoder.Token()
		if err == io.EOF {
			break
		}
		if err != nil {
			return nil, fmt.Errorf("XML 提取验签字段失败: %w", err)
		}

		switch t := token.(type) {
		case xml.StartElement:
			depth++
			if depth == 2 {
				currentField = t.Name.Local
				fields[currentField] = ""
			}
		case xml.CharData:
			if depth == 2 && currentField != "" {
				fields[currentField] += string(t)
			}
		case xml.EndElement:
			if depth == 2 {
				currentField = ""
			}
			depth--
		}
	}

	for key := range fields {
		if key == "sign" || strings.HasPrefix(key, "reserved") {
			delete(fields, key)
		}
	}
	return fields, nil
}

// decodeNotifyXML 将富友回调报文统一解码为 UTF-8 XML。
func decodeNotifyXML(rawBody []byte) (string, error) {
	content, err := extractNotifyContent(rawBody)
	if err != nil {
		return "", err
	}

	decoded, err := urlDecodeNotifyContent(content)
	if err != nil {
		return "", err
	}
	if strings.TrimSpace(decoded) == "" {
		return "", fmt.Errorf("回调 XML 内容为空")
	}

	return notifyXMLToUTF8(decoded)
}

// extractNotifyContent 提取 req 字段或原始 XML 内容。
func extractNotifyContent(rawBody []byte) (string, error) {
	content := strings.TrimSpace(string(rawBody))
	if content == "" {
		return "", fmt.Errorf("回调数据为空")
	}
	if strings.HasPrefix(content, "<") {
		return content, nil
	}

	if values, err := url.ParseQuery(content); err == nil {
		if reqValues, ok := values["req"]; ok {
			if len(reqValues) == 0 || strings.TrimSpace(reqValues[0]) == "" {
				return "", fmt.Errorf("回调 req 内容为空")
			}
			return reqValues[0], nil
		}
	}

	if strings.HasPrefix(content, "req=") {
		content = strings.TrimPrefix(content, "req=")
	}
	if strings.TrimSpace(content) == "" {
		return "", fmt.Errorf("回调 req 内容为空")
	}
	return content, nil
}

// urlDecodeNotifyContent 兼容富友单次或双次 URL 编码的 XML。
func urlDecodeNotifyContent(content string) (string, error) {
	decoded := strings.TrimSpace(content)
	for i := 0; i < 2; i++ {
		if strings.HasPrefix(decoded, "<") {
			return decoded, nil
		}
		if !strings.ContainsAny(decoded, "%+") {
			return decoded, nil
		}

		next, err := url.QueryUnescape(decoded)
		if err != nil {
			return "", fmt.Errorf("URL 解码回调数据失败: %w", err)
		}
		decoded = strings.TrimSpace(next)
	}
	return decoded, nil
}

// notifyXMLToUTF8 将 XML 内容转换为 UTF-8 并修正编码声明。
func notifyXMLToUTF8(content string) (string, error) {
	if hasUTF8XMLDeclaration(content) {
		return content, nil
	}

	utf8Bytes, err := GBKToUTF8([]byte(content))
	if err != nil {
		return "", fmt.Errorf("GBK 转 UTF-8 失败: %w", err)
	}

	replacer := strings.NewReplacer(
		`encoding="GBK"`, `encoding="UTF-8"`,
		`encoding="gbk"`, `encoding="UTF-8"`,
		`encoding='GBK'`, `encoding='UTF-8'`,
		`encoding='gbk'`, `encoding='UTF-8'`,
	)
	return replacer.Replace(string(utf8Bytes)), nil
}

// hasUTF8XMLDeclaration 判断 XML 声明是否已标明 UTF-8。
func hasUTF8XMLDeclaration(content string) bool {
	head := content
	if len(head) > 128 {
		head = head[:128]
	}
	head = strings.ToLower(head)
	return strings.Contains(head, `encoding="utf-8"`) || strings.Contains(head, `encoding='utf-8'`)
}

// BuildNotifySuccessResponse 构建成功响应。
func BuildNotifySuccessResponse() []byte {
	return []byte("1")
}

// BuildNotifyFailResponse 构建失败响应。
func BuildNotifyFailResponse(_ string) []byte {
	return []byte("0")
}

// buildNotifyResponse 构建回调响应 XML（GBK 编码）
func buildNotifyResponse(code, msg string) []byte {
	resp := NotifyResponse{
		ResultCode: code,
		ResultMsg:  msg,
	}

	xmlBytes, err := xml.Marshal(resp)
	if err != nil {
		return []byte(`<?xml version="1.0" encoding="GBK"?><xml><result_code>999999</result_code><result_msg>internal error</result_msg></xml>`)
	}

	xmlStr := `<?xml version="1.0" encoding="GBK"?>` + string(xmlBytes)

	gbkBytes, err := utf8ToGBK([]byte(xmlStr))
	if err != nil {
		return []byte(xmlStr)
	}

	return gbkBytes
}
