package recovery

import (
	_ "embed"
	"encoding/json"
	"fmt"
	"strconv"
	"strings"
	"sync"
)

//go:embed recovery_orders.json
var recoveryOrdersJSON []byte

type Config struct {
	Orders []Order `json:"orders"`
}

type Order struct {
	OutOrderNo      string `json:"out_order_no,omitempty"`
	OrderNo         string `json:"order_no"`
	OrderStatus     string `json:"order_status,omitempty"`
	SubscribeId     int64  `json:"subscribe_id"`
	LegacyID        string `json:"订阅id,omitempty"`
	ExpireAt        int64  `json:"expire_at"`
	Quantity        int64  `json:"quantity,omitempty"`
	PaidAt          int64  `json:"paid_at,omitempty"`
	PaymentOrderNo  string `json:"payment_order_no,omitempty"`
	OrderMoneyCents int64  `json:"order_money_cents,omitempty"`
	Note            string `json:"note"`
}

var (
	loadOnce sync.Once
	orders   map[string]Order
	loadErr  error
)

func FindOrder(orderNo string) (Order, bool, error) {
	loadOnce.Do(func() {
		orders, loadErr = loadOrders()
	})
	if loadErr != nil {
		return Order{}, false, loadErr
	}
	item, ok := orders[strings.TrimSpace(orderNo)]
	return item, ok, nil
}

func loadOrders() (map[string]Order, error) {
	var cfg Config
	if err := json.Unmarshal(recoveryOrdersJSON, &cfg); err != nil {
		return nil, fmt.Errorf("parse recovery orders json: %w", err)
	}

	result := make(map[string]Order, len(cfg.Orders))
	for i, item := range cfg.Orders {
		item.OutOrderNo = strings.TrimSpace(item.OutOrderNo)
		item.OrderNo = strings.TrimSpace(item.OrderNo)
		item.OrderStatus = strings.ToLower(strings.TrimSpace(item.OrderStatus))
		if item.OutOrderNo == "" {
			item.OutOrderNo = item.OrderNo
		}
		if item.OrderNo == "" {
			item.OrderNo = item.OutOrderNo
		}
		if item.OrderStatus == "" {
			item.OrderStatus = "success"
		}
		if item.SubscribeId == 0 && strings.TrimSpace(item.LegacyID) != "" {
			id, err := strconv.ParseInt(strings.TrimSpace(item.LegacyID), 10, 64)
			if err != nil {
				return nil, fmt.Errorf("orders[%d] legacy subscribe id is invalid", i)
			}
			item.SubscribeId = id
		}
		if item.OutOrderNo == "" {
			return nil, fmt.Errorf("orders[%d] out_order_no is required", i)
		}
		if item.SubscribeId <= 0 {
			return nil, fmt.Errorf("orders[%d] subscribe_id is required", i)
		}
		if item.ExpireAt <= 0 {
			return nil, fmt.Errorf("orders[%d] expire_at is required", i)
		}
		if item.Quantity <= 0 {
			item.Quantity = quantityFromAmount(item.OrderMoneyCents)
		}
		if item.Quantity <= 0 {
			return nil, fmt.Errorf("orders[%d] quantity is required", i)
		}
		if item.PaidAt < 0 {
			return nil, fmt.Errorf("orders[%d] paid_at is invalid", i)
		}
		if item.OrderMoneyCents < 0 {
			return nil, fmt.Errorf("orders[%d] order_money_cents is invalid", i)
		}
		if _, exists := result[item.OutOrderNo]; exists {
			return nil, fmt.Errorf("duplicate recovery out_order_no %s", item.OutOrderNo)
		}
		result[item.OutOrderNo] = item
		if item.OrderNo != "" && item.OrderNo != item.OutOrderNo {
			if _, exists := result[item.OrderNo]; exists {
				return nil, fmt.Errorf("duplicate recovery order_no %s", item.OrderNo)
			}
			result[item.OrderNo] = item
		}
	}
	return result, nil
}

func quantityFromAmount(amount int64) int64 {
	switch amount {
	case 1953:
		return 7
	case 4193:
		return 30
	case 9093:
		return 90
	case 31500:
		return 365
	default:
		return 0
	}
}
