package regionalapi

import (
	"encoding/json"
	"net/http"

	"github.com/pingcap/log"
	"github.com/tidbcloud/dataflow-service-ng/pkg/redact"
	"github.com/tidbcloud/pkgv2/bizerr"
	"go.uber.org/zap"
)

type RegionalError struct {
	Code    int           `json:"code"`
	Message string        `json:"message"`
	Details []interface{} `json:"details"`
}

func ErrorResponse2BizErr(code int, body []byte) bizerr.BizError {
	if code == http.StatusOK {
		return nil
	}

	regionalError := &RegionalError{}
	if err := json.Unmarshal(body, regionalError); err != nil {
		// UnmarshalTypeError.Error can contain an input number. Only emit
		// the error category and offset, never the decoder's message or body.
		kind := "decode_failed"
		var offset int64
		switch decodeErr := err.(type) {
		case *json.SyntaxError:
			kind, offset = "invalid_json", decodeErr.Offset
		case *json.UnmarshalTypeError:
			kind, offset = "type_mismatch", decodeErr.Offset
		}
		log.Warn("failed to unmarshal body", zap.Int("code", code),
			zap.String("decode_error", kind), zap.Int64("offset", offset))
	}
	errStr := string(body)
	if regionalError.Message != "" {
		errStr = regionalError.Message
	}
	// Sanitize before restoring the business error, so callers cannot log or
	// persist credentials returned by the regional service or TiCDC.
	errStr = redact.Text(errStr)
	switch code {
	case http.StatusBadRequest:
		return bizerr.Restore(bizerr.ErrCodeParams, errStr)
	case http.StatusUnauthorized:
		return bizerr.Restore(bizerr.ErrCodeAuth, errStr)
	case http.StatusForbidden:
		return bizerr.Restore(bizerr.ErrCodePermission, errStr)
	case http.StatusNotFound:
		return bizerr.Restore(bizerr.ErrCodeRecordNotFound, errStr)
	case http.StatusConflict:
		return bizerr.Restore(bizerr.ErrCodeRecordExist, errStr)
	case http.StatusRequestTimeout:
		return bizerr.Restore(bizerr.ErrCodeTimeout, errStr)
	default:
		return bizerr.Restore(bizerr.ErrCodeUnknown, errStr)
	}
}
