package payments

import "slices"

// PurchaseOverride is a per-account purchase override that forces purchases allowed or denied for
// one account regardless of the operational rail switch, or is absent (OverrideDefault) when the
// account follows the rail switch. The zero value is OverrideDefault, so a missing override row maps
// to it. OverrideAllow bypasses ONLY the operational rail switch — never the security / compliance
// gates (trusted platform, the D36 email anchor, the VK-iOS spend freeze, the min client version),
// which CreateOrder enforces separately.
type PurchaseOverride int

const (
	// OverrideDefault follows the rail switch (no override row for the account).
	OverrideDefault PurchaseOverride = iota
	// OverrideAllow always allows the purchase, bypassing only the operational rail switch.
	OverrideAllow
	// OverrideDeny always denies the purchase for the account.
	OverrideDeny
)

// RailAvailability is a payment rail's operational availability: whether purchases are enabled and,
// when disabled, the operator's explanation in each language, shown to the user on a purchase
// attempt. A rail with no status row is treated as enabled (fail-open — see the store), so the
// zero value is never used as a live "enabled" default.
type RailAvailability struct {
	Enabled   bool
	MessageRU string
	MessageEN string
}

// PurchaseGate decides whether an account may open a purchase order on a rail, from the per-account
// override and the rail's operational availability. It is the operational layer only — the caller
// still enforces the security gates. On a block it returns a reason localized to lang ("ru", else
// English). Fail-open: OverrideDefault on an enabled rail allows.
func PurchaseGate(override PurchaseOverride, rail RailAvailability, lang string) (ok bool, reason string) {
	switch override {
	case OverrideAllow:
		return true, "" // bypasses only the ops switch; the security gates still apply upstream
	case OverrideDeny:
		return false, defaultUnavailable(lang) // a per-account block — a neutral reason
	default: // OverrideDefault — follow the rail switch
		if rail.Enabled {
			return true, ""
		}
		return false, railMessage(rail, lang)
	}
}

// railMessage returns the operator's rail-off message in lang, falling back to the other language
// and then to the built-in default when the operator left both blank.
func railMessage(rail RailAvailability, lang string) string {
	if lang == "ru" {
		if rail.MessageRU != "" {
			return rail.MessageRU
		}
		if rail.MessageEN != "" {
			return rail.MessageEN
		}
	} else {
		if rail.MessageEN != "" {
			return rail.MessageEN
		}
		if rail.MessageRU != "" {
			return rail.MessageRU
		}
	}
	return defaultUnavailable(lang)
}

// defaultUnavailable is the built-in localized "payments unavailable" reason used when the operator
// set no custom message, and for a per-account deny.
func defaultUnavailable(lang string) string {
	if lang == "ru" {
		return "Оплата временно недоступна. Попробуйте позже."
	}
	return "Payments are temporarily unavailable. Please try again later."
}

// Rail keys name the operational payment rails the kill switch and the per-account override key on.
// The direct rail is split per channel (D42); vk and telegram are single-rail.
const (
	RailDirectWeb     = "direct:web"
	RailDirectAndroid = "direct:android"
	RailVK            = "vk"
	RailTelegram      = "telegram"
)

// KnownRails is the fixed set of rail keys, for the admin editor and for validation.
var KnownRails = []string{RailDirectWeb, RailDirectAndroid, RailVK, RailTelegram}

// RailKey maps a payment context to its operational rail key: the direct rail by channel subtype
// (android, else web), or the store rail's own name (vk / telegram).
func RailKey(kind Source, subtype string) string {
	if kind == SourceDirect {
		if subtype == "android" {
			return RailDirectAndroid
		}
		return RailDirectWeb
	}
	return string(kind)
}

// isKnownRail reports whether rail is one of the fixed rail keys.
func isKnownRail(rail string) bool {
	return slices.Contains(KnownRails, rail)
}
