package realname

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"strings"
	"time"
)

const cloudMarketProviderName = "aliyun_cloudmarket"

type CloudMarketConfig struct {
	URL     string
	AppCode string
}

type CloudMarketProvider struct {
	url     string
	appCode string
	client  *http.Client
}

type cloudMarketResponse struct {
	RequestID string          `json:"request_id"`
	Status    string          `json:"status"`
	Result    json.RawMessage `json:"result"`
	Reason    string          `json:"reason"`
}

func NewCloudMarketProvider(cfg CloudMarketConfig) (*CloudMarketProvider, error) {
	endpoint := strings.TrimSpace(cfg.URL)
	appCode := strings.TrimSpace(cfg.AppCode)
	if endpoint == "" || appCode == "" {
		return nil, ErrProviderConfigInvalid
	}
	if _, err := url.ParseRequestURI(endpoint); err != nil {
		return nil, fmt.Errorf("realname cloud market url invalid: %w", err)
	}
	return &CloudMarketProvider{
		url:     endpoint,
		appCode: appCode,
		client:  &http.Client{Timeout: 10 * time.Second},
	}, nil
}

func (p *CloudMarketProvider) Name() string {
	return cloudMarketProviderName
}

func (p *CloudMarketProvider) Start(ctx context.Context, req StartRequest) (ProviderResult, error) {
	endpoint, err := url.Parse(p.url)
	if err != nil {
		return ProviderResult{}, err
	}
	query := endpoint.Query()
	query.Set("name", strings.TrimSpace(req.Name))
	query.Set("id_number", strings.TrimSpace(req.IDNo))
	endpoint.RawQuery = query.Encode()

	httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, endpoint.String(), nil)
	if err != nil {
		return ProviderResult{}, err
	}
	httpReq.Header.Set("Authorization", "APPCODE "+p.appCode)
	httpReq.Header.Set("Accept", "application/json")

	resp, err := p.client.Do(httpReq)
	if err != nil {
		return ProviderResult{}, fmt.Errorf("realname cloud market request failed: %w", err)
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusTooManyRequests || resp.StatusCode == http.StatusForbidden {
		return ProviderResult{}, ErrProviderRateLimited
	}

	body, err := io.ReadAll(io.LimitReader(resp.Body, 4096))
	if err != nil {
		return ProviderResult{}, fmt.Errorf("%w: read cloud market response: %v", ErrProviderUnavailable, err)
	}

	var payload cloudMarketResponse
	if err := json.Unmarshal(body, &payload); err != nil {
		return ProviderResult{}, fmt.Errorf("%w: invalid cloud market response: %v", ErrProviderUnavailable, err)
	}
	if resp.StatusCode < http.StatusOK || resp.StatusCode >= http.StatusMultipleChoices {
		return ProviderResult{}, cloudMarketHTTPError(resp.StatusCode, payload.Reason)
	}
	if strings.ToUpper(strings.TrimSpace(payload.Status)) != "OK" {
		return ProviderResult{
			ProviderOrderNo: payload.RequestID,
			Status:          StatusRejected,
			MaskedName:      maskName(req.Name),
			MaskedIDNo:      maskIDNo(req.IDNo),
			FailReason:      firstNonEmpty(payload.Reason, "实名认证请求失败"),
		}, nil
	}

	result, err := parseCloudMarketResult(payload.Result)
	if err != nil {
		return ProviderResult{}, err
	}

	now := time.Now()
	base := ProviderResult{
		ProviderOrderNo: payload.RequestID,
		MaskedName:      maskName(req.Name),
		MaskedIDNo:      maskIDNo(req.IDNo),
	}
	switch result {
	case 1:
		base.Status = StatusVerified
		base.VerifiedAt = &now
	case 2:
		base.Status = StatusRejected
		base.FailReason = "姓名与身份证号不一致"
	case 3:
		base.Status = StatusRejected
		base.FailReason = "公安库无匹配记录"
	case 110:
		return ProviderResult{}, ErrProviderRateLimited
	default:
		base.Status = StatusRejected
		base.FailReason = fmt.Sprintf("未知核验结果：%d", result)
	}
	return base, nil
}

func cloudMarketHTTPError(statusCode int, reason string) error {
	reason = strings.TrimSpace(reason)
	switch statusCode {
	case http.StatusBadRequest:
		return fmt.Errorf("%w: %s", ErrInvalidRealnameInput, firstNonEmpty(reason, "cloud market bad request"))
	case http.StatusUnauthorized, http.StatusNotFound:
		return fmt.Errorf("%w: cloud market http status %d: %s", ErrProviderConfigInvalid, statusCode, reason)
	default:
		return fmt.Errorf("%w: cloud market http status %d: %s", ErrProviderUnavailable, statusCode, reason)
	}
}

func parseCloudMarketResult(raw json.RawMessage) (int, error) {
	if len(raw) == 0 || string(raw) == "null" {
		return 0, fmt.Errorf("realname cloud market result missing")
	}
	var number int
	if err := json.Unmarshal(raw, &number); err == nil {
		return number, nil
	}
	var text string
	if err := json.Unmarshal(raw, &text); err == nil {
		switch strings.TrimSpace(text) {
		case "1":
			return 1, nil
		case "2":
			return 2, nil
		case "3":
			return 3, nil
		case "110":
			return 110, nil
		}
	}
	return 0, fmt.Errorf("realname cloud market result invalid: %s", string(raw))
}

func firstNonEmpty(values ...string) string {
	for _, value := range values {
		value = strings.TrimSpace(value)
		if value != "" {
			return value
		}
	}
	return ""
}
