package paymentutil

import (
	"encoding/json"
	"errors"
	"fmt"
)

// CallbackAck is the provider-facing HTTP acknowledgement returned after a
// payment notification is processed.
type CallbackAck struct {
	StatusCode  int
	ContentType string
	Body        []byte
}

// CallbackError carries a safe provider acknowledgement alongside the cause
// that should be logged or returned to the application layer.
type CallbackError struct {
	Cause error
	Ack   CallbackAck
}

func (e *CallbackError) Error() string {
	if e == nil || e.Cause == nil {
		return "支付回调处理失败"
	}
	return e.Cause.Error()
}

func (e *CallbackError) Unwrap() error {
	if e == nil {
		return nil
	}
	return e.Cause
}

func CallbackFailure(err error, fallback CallbackAck) CallbackAck {
	var callbackErr *CallbackError
	if errors.As(err, &callbackErr) && callbackErr.Ack.StatusCode != 0 {
		return callbackErr.Ack
	}
	return fallback
}

// DefaultCallbackAck returns the conventional acknowledgement for providers
// whose callback wire formats are stable across modules.
func DefaultCallbackAck(provider string, success bool) CallbackAck {
	status := 200
	if !success {
		status = 500
	}
	switch provider {
	case ProviderAlipay, ProviderAlipayV3:
		body := "success"
		if !success {
			body = "failure"
		}
		return CallbackAck{StatusCode: status, ContentType: "text/plain; charset=utf-8", Body: []byte(body)}
	case ProviderWechatV2:
		code, message := "SUCCESS", "OK"
		if !success {
			code, message = "FAIL", "FAIL"
		}
		body := fmt.Sprintf("<xml><return_code><![CDATA[%s]]></return_code><return_msg><![CDATA[%s]]></return_msg></xml>", code, message)
		return CallbackAck{StatusCode: status, ContentType: "application/xml; charset=utf-8", Body: []byte(body)}
	case ProviderWechatV3:
		code, message := "SUCCESS", "成功"
		if !success {
			code, message = "FAIL", "失败"
		}
		body, _ := json.Marshal(map[string]string{"code": code, "message": message})
		return CallbackAck{StatusCode: status, ContentType: "application/json; charset=utf-8", Body: body}
	case ProviderApple:
		return CallbackAck{StatusCode: status}
	default:
		body := "success"
		if !success {
			body = "failure"
		}
		return CallbackAck{StatusCode: status, ContentType: "text/plain; charset=utf-8", Body: []byte(body)}
	}
}
