package billing

import (
	"context"
	"net/url"
	"time"

	"github.com/tidbcloud/AgentTeam/internal/billing/alipay"
	"github.com/tidbcloud/AgentTeam/internal/store/identity"
	"github.com/tidbcloud/AgentTeam/internal/store/organization"
	"github.com/tidbcloud/AgentTeam/internal/store/seatbilling"
)

type SessionAuthenticator interface {
	AuthenticateSession(ctx context.Context, token string) (identity.AuthenticatedUser, error)
}

type OrganizationProvider interface {
	GetWorkspaceOrganizationForUser(ctx context.Context, userID string, workspaceID string) (organization.OrganizationSummary, error)
}

type AlipayClient interface {
	CreatePagePayURL(input alipay.PagePayInput) (string, error)
	VerifyNotify(form url.Values) error
	QueryTrade(ctx context.Context, outTradeNo string) (alipay.QueryResult, error)
	CloseTrade(ctx context.Context, outTradeNo string) (alipay.CloseResult, error)
}

type SeatBillingStore interface {
	ListSeatPlans(ctx context.Context) ([]seatPlanView, error)
	LoadSeatSummary(ctx context.Context, organizationID string, now time.Time) (seatSummaryView, error)
	LoadSeatSubscription(ctx context.Context, organizationID string) (seatSubscriptionView, bool, error)
	CreateSeatOrder(ctx context.Context, params seatOrderCreateParams) (seatOrderView, error)
	LoadSeatOrder(ctx context.Context, organizationID string, orderID string) (seatOrderView, bool, error)
	LoadSeatOrderByProviderOrderID(ctx context.Context, provider string, providerOrderID string) (seatOrderView, bool, error)
	ListSeatOrders(ctx context.Context, params seatOrderListOptions) ([]seatOrderView, error)
	SettleSeatPaymentPaid(ctx context.Context, params seatPaymentSettleParams) (seatOrderView, error)
	CancelSeatOrder(ctx context.Context, params seatOrderCancelParams) (seatOrderView, error)
	ListSeatPaymentHistory(ctx context.Context, params seatPaymentHistoryOptions) ([]seatPaymentHistoryView, error)
}

type BillingClient interface {
	LoadSummary(ctx context.Context, organizationID string) (billingSummary, error)
	ListLedger(ctx context.Context, organizationID string, options billingListOptions) (billingLedgerPage, error)
	ListModelUsage(ctx context.Context, organizationID string, options billingListOptions) (billingModelUsage, error)
	ListPayments(ctx context.Context, organizationID string, options billingListOptions) (billingPaymentOrderPage, error)
	LoadPricing(ctx context.Context, organizationID string) (billingPricing, error)
	CreateCreditRechargeOrder(ctx context.Context, params createCreditRechargeOrderParams) (billingPaymentOrder, error)
	LoadCreditRechargeOrder(ctx context.Context, organizationID string, orderID string) (billingPaymentOrder, error)
	LoadCreditRechargeOrderByOutTradeNo(ctx context.Context, outTradeNo string) (billingPaymentOrder, error)
	ConfirmCreditRechargeOrderPaid(ctx context.Context, orderID string, params confirmCreditRechargeOrderPaidParams) (billingPaymentOrder, error)
	CloseCreditRechargeOrder(ctx context.Context, orderID string, params closeCreditRechargeOrderParams) (billingPaymentOrder, error)
}

type billingListOptions struct {
	Month  string
	Limit  int
	Cursor string
	Type   string
}

type createCreditRechargeOrderParams struct {
	OrganizationID     string         `json:"organizationId"`
	WorkspaceID        string         `json:"workspaceId"`
	CreatedByUserID    string         `json:"createdByUserId"`
	IdempotencyKey     string         `json:"idempotencyKey"`
	CreditUnit         string         `json:"creditUnit"`
	CreditAmount       int64          `json:"creditAmount"`
	Currency           string         `json:"currency"`
	PayableAmountCents int64          `json:"payableAmountCents"`
	PriceSnapshot      map[string]any `json:"priceSnapshot"`
	Provider           string         `json:"provider"`
	ProviderMode       string         `json:"providerMode"`
	ExpiresAt          time.Time      `json:"expiresAt"`
}

type confirmCreditRechargeOrderPaidParams struct {
	OrganizationID      string    `json:"organizationId"`
	Provider            string    `json:"provider"`
	Source              string    `json:"source"`
	OutTradeNo          string    `json:"outTradeNo"`
	ProviderTradeNo     string    `json:"providerTradeNo"`
	ProviderTradeStatus string    `json:"providerTradeStatus"`
	PaidAmountCents     int64     `json:"paidAmountCents"`
	Currency            string    `json:"currency"`
	PaidAt              time.Time `json:"paidAt"`
	RawPayloadSHA256    string    `json:"rawPayloadSha256"`
}

type closeCreditRechargeOrderParams struct {
	OrganizationID      string    `json:"organizationId"`
	Provider            string    `json:"provider"`
	Source              string    `json:"source"`
	OutTradeNo          string    `json:"outTradeNo"`
	ProviderTradeNo     string    `json:"providerTradeNo"`
	ProviderTradeStatus string    `json:"providerTradeStatus"`
	AmountCents         int64     `json:"amountCents"`
	Currency            string    `json:"currency"`
	ClosedAt            time.Time `json:"closedAt"`
	RawPayloadSHA256    string    `json:"rawPayloadSha256"`
}

// HostedCreditGrantParams creates a trusted-control-plane hosted credit grant in agentteam-maas.
type HostedCreditGrantParams struct {
	OrganizationID    string     `json:"organizationId"`
	IdempotencyKey    string     `json:"idempotencyKey"`
	CreditUnit        string     `json:"creditUnit"`
	CreditAmount      int64      `json:"creditAmount,omitempty"`
	CreditAmountAtoms int64      `json:"creditAmountAtoms,omitempty"`
	GrantSourceType   string     `json:"grantSourceType"`
	GrantReasonRef    string     `json:"grantReasonRef"`
	Refundable        bool       `json:"refundable"`
	ExpiresAt         *time.Time `json:"expiresAt,omitempty"`
	OperatorIdentity  string     `json:"operatorIdentity"`
	SourceRef         string     `json:"sourceRef,omitempty"`
}

// HostedCreditGrant is the agentteam-maas hosted credit grant response shape.
type HostedCreditGrant struct {
	GrantID           string     `json:"grantId"`
	OperationID       string     `json:"operationId"`
	Created           bool       `json:"created"`
	OrganizationID    string     `json:"organizationId"`
	CreditUnit        string     `json:"creditUnit"`
	CreditUnitSource  string     `json:"creditUnitSource,omitempty"`
	CreditAmount      int64      `json:"creditAmount"`
	CreditAmountAtoms int64      `json:"creditAmountAtoms"`
	GrantSourceType   string     `json:"grantSourceType"`
	GrantReasonRef    string     `json:"grantReasonRef"`
	Refundable        bool       `json:"refundable"`
	ExpiresAt         *time.Time `json:"expiresAt,omitempty"`
	OperatorIdentity  string     `json:"operatorIdentity,omitempty"`
	SourceRef         string     `json:"sourceRef,omitempty"`
	IdempotencyKey    string     `json:"idempotencyKey,omitempty"`
	CreatedAt         time.Time  `json:"createdAt"`
	UpdatedAt         time.Time  `json:"updatedAt"`
}

type billingPaymentOrder struct {
	OrderID            string     `json:"orderId"`
	OrganizationID     string     `json:"organizationId"`
	WorkspaceID        string     `json:"workspaceId,omitempty"`
	CreatedByUserID    string     `json:"createdByUserId"`
	OutTradeNo         string     `json:"outTradeNo"`
	ProviderTradeNo    string     `json:"providerTradeNo,omitempty"`
	Status             string     `json:"status"`
	CreditUnit         string     `json:"creditUnit"`
	CreditAmount       int64      `json:"creditAmount"`
	Currency           string     `json:"currency"`
	PayableAmountCents int64      `json:"payableAmountCents"`
	Provider           string     `json:"provider"`
	ProviderMode       string     `json:"providerMode"`
	ExpiresAt          time.Time  `json:"expiresAt"`
	PaidAt             *time.Time `json:"paidAt,omitempty"`
	ClosedAt           *time.Time `json:"closedAt,omitempty"`
	FailedAt           *time.Time `json:"failedAt,omitempty"`
	CreatedAt          time.Time  `json:"createdAt"`
	UpdatedAt          time.Time  `json:"updatedAt"`
}

type billingSummary struct {
	OrganizationID        string    `json:"organizationId"`
	CreditUnit            string    `json:"creditUnit"`
	AvailableCreditAtoms  int64     `json:"availableCreditAtoms"`
	HeldCreditAtoms       int64     `json:"heldCreditAtoms"`
	ConsumedCreditAtoms   int64     `json:"consumedCreditAtoms"`
	TodaySpendCreditAtoms int64     `json:"todaySpendCreditAtoms"`
	MonthSpendCreditAtoms int64     `json:"monthSpendCreditAtoms"`
	UpdatedAt             time.Time `json:"updatedAt"`
}

type billingLedgerItem struct {
	ID               string    `json:"id"`
	Type             string    `json:"type"`
	CreditUnit       string    `json:"creditUnit"`
	CreditDeltaAtoms int64     `json:"creditDeltaAtoms"`
	Description      string    `json:"description"`
	PaymentOrderID   string    `json:"paymentOrderId,omitempty"`
	OccurredAt       time.Time `json:"occurredAt"`
}

type billingLedgerPage struct {
	Items      []billingLedgerItem `json:"items"`
	NextCursor string              `json:"nextCursor,omitempty"`
}

type billingModelUsageItem struct {
	ModelReference      string `json:"modelReference"`
	RequestCount        int64  `json:"requestCount"`
	CreditUnit          string `json:"creditUnit"`
	ConsumedCreditAtoms int64  `json:"consumedCreditAtoms"`
}

type billingModelUsage struct {
	Month                    string                  `json:"month"`
	CreditUnit               string                  `json:"creditUnit"`
	TotalRequestCount        int64                   `json:"totalRequestCount"`
	TotalConsumedCreditAtoms int64                   `json:"totalConsumedCreditAtoms"`
	Items                    []billingModelUsageItem `json:"items"`
}

type billingPaymentOrderPage struct {
	Items      []billingPaymentOrder `json:"items"`
	NextCursor string                `json:"nextCursor,omitempty"`
}

type billingHostedModelPricing struct {
	ID                                            string                 `json:"id"`
	ModelReference                                string                 `json:"modelReference"`
	HostedModelID                                 string                 `json:"hostedModelId,omitempty"`
	DisplayName                                   string                 `json:"displayName"`
	PricingVersionID                              string                 `json:"pricingVersionId,omitempty"`
	EffectivePricingVersionID                     string                 `json:"effectivePricingVersionId,omitempty"`
	PricingFormulaVersion                         string                 `json:"pricingFormulaVersion,omitempty"`
	BillableDimensions                            []string               `json:"billableDimensions,omitempty"`
	ContextTiers                                  []HostedPricingTier    `json:"contextTiers,omitempty"`
	InputCreditAtomsPerMillionTokens              *int64                 `json:"inputCreditAtomsPerMillionTokens,omitempty"`
	CacheReadInputCreditAtomsPerMillionTokens     *int64                 `json:"cacheReadInputCreditAtomsPerMillionTokens,omitempty"`
	OutputCreditAtomsPerMillionTokens             *int64                 `json:"outputCreditAtomsPerMillionTokens,omitempty"`
	BasePricingVersionID                          string                 `json:"basePricingVersionId,omitempty"`
	BasePricingFormulaVersion                     string                 `json:"basePricingFormulaVersion,omitempty"`
	BaseBillableDimensions                        []string               `json:"baseBillableDimensions,omitempty"`
	BaseContextTiers                              []HostedPricingTier    `json:"baseContextTiers,omitempty"`
	BaseInputCreditAtomsPerMillionTokens          *int64                 `json:"baseInputCreditAtomsPerMillionTokens,omitempty"`
	BaseCacheReadInputCreditAtomsPerMillionTokens *int64                 `json:"baseCacheReadInputCreditAtomsPerMillionTokens,omitempty"`
	BaseOutputCreditAtomsPerMillionTokens         *int64                 `json:"baseOutputCreditAtomsPerMillionTokens,omitempty"`
	PricingOverride                               *HostedPricingOverride `json:"pricingOverride,omitempty"`
}

// HostedPricingTier is one MaaS context-length pricing tier.
type HostedPricingTier struct {
	ID               string                 `json:"id"`
	MinContextTokens int64                  `json:"minContextTokens"`
	MaxContextTokens *int64                 `json:"maxContextTokens,omitempty"`
	Rates            HostedPricingTierRates `json:"rates"`
}

// HostedPricingTierRates carries hosted credit rates for a context tier.
type HostedPricingTierRates struct {
	InputTokensCreditAtomsPerMillionTokens    int64 `json:"inputTokensCreditAtomsPerMillionTokens"`
	CacheReadInputCreditAtomsPerMillionTokens int64 `json:"cacheReadInputCreditAtomsPerMillionTokens"`
	OutputCreditAtomsPerMillionTokens         int64 `json:"outputCreditAtomsPerMillionTokens"`
}

// HostedModelPricing is the public hosted model price subset shared with model pickers.
type HostedModelPricing struct {
	ModelReference                                string
	HostedModelID                                 string
	PricingVersionID                              string
	EffectivePricingVersionID                     string
	PricingFormulaVersion                         string
	BillableDimensions                            []string
	ContextTiers                                  []HostedPricingTier
	BasePricingVersionID                          string
	BasePricingFormulaVersion                     string
	BaseBillableDimensions                        []string
	BaseContextTiers                              []HostedPricingTier
	InputCreditAtomsPerMillionTokens              *int64
	CacheReadInputCreditAtomsPerMillionTokens     *int64
	OutputCreditAtomsPerMillionTokens             *int64
	BaseInputCreditAtomsPerMillionTokens          *int64
	BaseCacheReadInputCreditAtomsPerMillionTokens *int64
	BaseOutputCreditAtomsPerMillionTokens         *int64
	PricingOverride                               *HostedPricingOverride
}

// HostedPricing is the public hosted pricing subset shared with non-billing surfaces.
type HostedPricing struct {
	CreditAtomsPerCredit int64
	HostedModels         []HostedModelPricing
}

// HostedPricingOverride is the currently applied MaaS pricing override metadata.
type HostedPricingOverride struct {
	ID                        string     `json:"id"`
	HostedModelID             string     `json:"hostedModelId"`
	ScopeType                 string     `json:"scopeType"`
	ScopeID                   string     `json:"scopeId,omitempty"`
	BasePricingVersionID      string     `json:"basePricingVersionId"`
	EffectivePricingVersionID string     `json:"effectivePricingVersionId"`
	OverrideType              string     `json:"overrideType"`
	DisplayLabel              string     `json:"displayLabel,omitempty"`
	Priority                  int        `json:"priority"`
	EffectiveFrom             *time.Time `json:"effectiveFrom,omitempty"`
	EffectiveTo               *time.Time `json:"effectiveTo,omitempty"`
	Status                    string     `json:"status"`
}

type billingPricing struct {
	CreditUnit           string                      `json:"creditUnit"`
	Currency             string                      `json:"currency"`
	CreditAmountPerCNY   int64                       `json:"creditAmountPerCny"`
	CreditAtomsPerCredit int64                       `json:"creditAtomsPerCredit"`
	CreditAtomsPerCNY    int64                       `json:"creditAtomsPerCny"`
	RechargePresets      []int64                     `json:"rechargePresets"`
	CustomMinCredits     int64                       `json:"customMinCredits"`
	HostedModels         []billingHostedModelPricing `json:"hostedModels"`
}

type seatPlanView struct {
	ID                   string
	PlanKey              string
	Name                 string
	BillingPeriod        string
	UnitPriceCents       int64
	Currency             string
	ManagedAgentsPerSeat int64
	MinSeatQuantity      int
	MaxSeatQuantity      int
	SelfServiceEnabled   bool
	Status               string
}

type seatSubscriptionView struct {
	OrganizationID               string
	Status                       string
	PlanKey                      string
	BillingPeriod                string
	SeatQuantity                 int
	ManagedAgentsPerSeatSnapshot int64
	MaxHumanSeatsSnapshot        int
	UnitPriceCentsSnapshot       int64
	Currency                     string
	CurrentPeriodStart           time.Time
	CurrentPeriodEnd             time.Time
	CreatedAt                    time.Time
	UpdatedAt                    time.Time
}

type seatEntitlementView struct {
	OrganizationID         string
	PlanKey                string
	BillingPeriod          string
	HumanSeatLimit         int64
	FreeHumanSeatLimit     int64
	PaidHumanSeatLimit     int64
	GiftHumanSeatLimit     int64
	NextGrantExpiresAt     *time.Time
	OrganizationAgentLimit int64
	ManagedAgentsPerSeat   int64
	CurrentPeriodStart     *time.Time
	CurrentPeriodEnd       *time.Time
	Paid                   bool
	SubscriptionStatus     string
}

type seatSummaryView struct {
	Entitlement         seatEntitlementView
	CurrentHumanSeats   int64
	CurrentAgents       int64
	HumanSeatsOverLimit bool
	AgentsOverLimit     bool
}

type seatOrderCreateParams struct {
	OrganizationID    string
	CreatedByUserID   string
	OrderType         string
	ToPlanKey         string
	BillingPeriod     string
	SeatQuantityAfter int
	IdempotencyKey    string
	Now               time.Time
}

type seatOrderListOptions struct {
	OrganizationID string
	Limit          int
	Statuses       []string
}

type seatPaymentSettleParams struct {
	Provider        string
	ProviderOrderID string
	AmountCents     int64
	Currency        string
	PaidAt          *time.Time
	ConfirmedAt     time.Time
	RawPayloadJSON  string
}

type seatOrderCancelParams struct {
	OrganizationID    string
	OrderID           string
	CancelledByUserID string
	Now               time.Time
	RawPayloadJSON    string
}

type seatPaymentHistoryOptions struct {
	OrganizationID string
	MonthStart     time.Time
	MonthEnd       time.Time
	Limit          int
}

type seatOrderView struct {
	ID                 string
	OrganizationID     string
	OrderType          string
	Status             string
	FromPlanKey        *string
	ToPlanKey          *string
	BillingPeriod      string
	SeatQuantityBefore int
	SeatQuantityAfter  int
	SeatDelta          int
	AmountCents        int64
	Currency           string
	Provider           string
	ProviderOrderID    string
	PaymentStatus      string
	ExpiresAt          time.Time
	CreatedByUserID    string
	CancelledByUserID  *string
	SettledAt          *time.Time
	AppliedAt          *time.Time
	PaidAt             *time.Time
	ClosedAt           *time.Time
	CreatedAt          time.Time
	UpdatedAt          time.Time
}

type seatPaymentHistoryView struct {
	OrderID         string
	ProviderOrderID string
	Status          string
	AmountCents     int64
	Currency        string
	CreatedAt       time.Time
	PaidAt          *time.Time
}

type seatBillingStoreAdapter struct {
	store *seatbilling.Store
}

func NewSeatBillingStoreAdapter(store *seatbilling.Store) SeatBillingStore {
	if store == nil {
		return nil
	}
	return seatBillingStoreAdapter{store: store}
}

func (adapter seatBillingStoreAdapter) ListSeatPlans(ctx context.Context) ([]seatPlanView, error) {
	plans, err := adapter.store.ListActivePlans(ctx)
	if err != nil {
		return nil, err
	}
	views := make([]seatPlanView, 0, len(plans))
	for _, plan := range plans {
		views = append(views, seatPlanFromStore(plan))
	}
	return views, nil
}

func (adapter seatBillingStoreAdapter) LoadSeatSummary(ctx context.Context, organizationID string, now time.Time) (seatSummaryView, error) {
	summary, err := adapter.store.LoadSeatSummary(ctx, organizationID, now)
	if err != nil {
		return seatSummaryView{}, err
	}
	return seatSummaryFromStore(summary), nil
}

func (adapter seatBillingStoreAdapter) LoadSeatSubscription(ctx context.Context, organizationID string) (seatSubscriptionView, bool, error) {
	subscription, found, err := adapter.store.LoadSubscription(ctx, organizationID)
	if err != nil || !found {
		return seatSubscriptionView{}, found, err
	}
	return seatSubscriptionFromStore(subscription), true, nil
}

func (adapter seatBillingStoreAdapter) CreateSeatOrder(ctx context.Context, params seatOrderCreateParams) (seatOrderView, error) {
	order, err := adapter.store.CreateSeatOrder(ctx, seatbilling.CreateSeatOrderParams{
		OrganizationID:    params.OrganizationID,
		CreatedByUserID:   params.CreatedByUserID,
		OrderType:         params.OrderType,
		ToPlanKey:         params.ToPlanKey,
		BillingPeriod:     params.BillingPeriod,
		SeatQuantityAfter: params.SeatQuantityAfter,
		IdempotencyKey:    params.IdempotencyKey,
		Now:               params.Now,
	})
	if err != nil {
		return seatOrderView{}, err
	}
	return seatOrderFromStore(order), nil
}

func (adapter seatBillingStoreAdapter) LoadSeatOrder(ctx context.Context, organizationID string, orderID string) (seatOrderView, bool, error) {
	order, found, err := adapter.store.LoadSeatOrder(ctx, organizationID, orderID)
	if err != nil || !found {
		return seatOrderView{}, found, err
	}
	return seatOrderFromStore(order), true, nil
}

func (adapter seatBillingStoreAdapter) LoadSeatOrderByProviderOrderID(ctx context.Context, provider string, providerOrderID string) (seatOrderView, bool, error) {
	order, found, err := adapter.store.LoadSeatOrderByProviderOrderID(ctx, provider, providerOrderID)
	if err != nil || !found {
		return seatOrderView{}, found, err
	}
	return seatOrderFromStore(order), true, nil
}

func (adapter seatBillingStoreAdapter) ListSeatOrders(ctx context.Context, params seatOrderListOptions) ([]seatOrderView, error) {
	orders, err := adapter.store.ListSeatOrders(ctx, seatbilling.ListSeatOrdersParams{
		OrganizationID: params.OrganizationID,
		Limit:          params.Limit,
		Statuses:       params.Statuses,
	})
	if err != nil {
		return nil, err
	}
	views := make([]seatOrderView, 0, len(orders))
	for _, order := range orders {
		views = append(views, seatOrderFromStore(order))
	}
	return views, nil
}

func (adapter seatBillingStoreAdapter) SettleSeatPaymentPaid(ctx context.Context, params seatPaymentSettleParams) (seatOrderView, error) {
	order, err := adapter.store.SettleSeatPaymentPaid(ctx, seatbilling.SettleSeatPaymentParams{
		Provider:        params.Provider,
		ProviderOrderID: params.ProviderOrderID,
		AmountCents:     params.AmountCents,
		Currency:        params.Currency,
		PaidAt:          params.PaidAt,
		ConfirmedAt:     params.ConfirmedAt,
		RawPayloadJSON:  params.RawPayloadJSON,
	})
	if err != nil {
		return seatOrderView{}, err
	}
	return seatOrderFromStore(order), nil
}

func (adapter seatBillingStoreAdapter) CancelSeatOrder(ctx context.Context, params seatOrderCancelParams) (seatOrderView, error) {
	order, err := adapter.store.CancelSeatOrder(ctx, seatbilling.CancelSeatOrderParams{
		OrganizationID:    params.OrganizationID,
		OrderID:           params.OrderID,
		CancelledByUserID: params.CancelledByUserID,
		Now:               params.Now,
		RawPayloadJSON:    params.RawPayloadJSON,
	})
	if err != nil {
		return seatOrderView{}, err
	}
	return seatOrderFromStore(order), nil
}

func (adapter seatBillingStoreAdapter) ListSeatPaymentHistory(ctx context.Context, params seatPaymentHistoryOptions) ([]seatPaymentHistoryView, error) {
	rows, err := adapter.store.ListSeatPaymentHistory(ctx, seatbilling.ListSeatPaymentHistoryParams{
		OrganizationID: params.OrganizationID,
		MonthStart:     params.MonthStart,
		MonthEnd:       params.MonthEnd,
		Limit:          params.Limit,
	})
	if err != nil {
		return nil, err
	}
	views := make([]seatPaymentHistoryView, 0, len(rows))
	for _, row := range rows {
		views = append(views, seatPaymentHistoryFromStore(row))
	}
	return views, nil
}

func seatPlanFromStore(plan seatbilling.SeatPlan) seatPlanView {
	return seatPlanView{
		ID:                   plan.ID,
		PlanKey:              plan.PlanKey,
		Name:                 plan.Name,
		BillingPeriod:        plan.BillingPeriod,
		UnitPriceCents:       plan.UnitPriceCents,
		Currency:             plan.Currency,
		ManagedAgentsPerSeat: plan.ManagedAgentsPerSeat,
		MinSeatQuantity:      plan.MinSeatQuantity,
		MaxSeatQuantity:      plan.MaxSeatQuantity,
		SelfServiceEnabled:   plan.SelfServiceEnabled,
		Status:               plan.Status,
	}
}

func seatSubscriptionFromStore(subscription seatbilling.SeatSubscription) seatSubscriptionView {
	return seatSubscriptionView{
		OrganizationID:               subscription.OrganizationID,
		Status:                       subscription.Status,
		PlanKey:                      subscription.PlanKey,
		BillingPeriod:                subscription.BillingPeriod,
		SeatQuantity:                 subscription.SeatQuantity,
		ManagedAgentsPerSeatSnapshot: subscription.ManagedAgentsPerSeatSnapshot,
		MaxHumanSeatsSnapshot:        subscription.MaxHumanSeatsSnapshot,
		UnitPriceCentsSnapshot:       subscription.UnitPriceCentsSnapshot,
		Currency:                     subscription.Currency,
		CurrentPeriodStart:           subscription.CurrentPeriodStart,
		CurrentPeriodEnd:             subscription.CurrentPeriodEnd,
		CreatedAt:                    subscription.CreatedAt,
		UpdatedAt:                    subscription.UpdatedAt,
	}
}

func seatSummaryFromStore(summary seatbilling.SeatSummary) seatSummaryView {
	return seatSummaryView{
		Entitlement:         seatEntitlementFromStore(summary.Entitlement),
		CurrentHumanSeats:   summary.CurrentHumanSeats,
		CurrentAgents:       summary.CurrentAgents,
		HumanSeatsOverLimit: summary.HumanSeatsOverLimit,
		AgentsOverLimit:     summary.AgentsOverLimit,
	}
}

func seatEntitlementFromStore(entitlement seatbilling.SeatEntitlement) seatEntitlementView {
	return seatEntitlementView{
		OrganizationID:         entitlement.OrganizationID,
		PlanKey:                entitlement.PlanKey,
		BillingPeriod:          entitlement.BillingPeriod,
		HumanSeatLimit:         entitlement.HumanSeatLimit,
		FreeHumanSeatLimit:     entitlement.FreeHumanSeatLimit,
		PaidHumanSeatLimit:     entitlement.PaidHumanSeatLimit,
		GiftHumanSeatLimit:     entitlement.GiftHumanSeatLimit,
		NextGrantExpiresAt:     entitlement.NextGrantExpiresAt,
		OrganizationAgentLimit: entitlement.OrganizationAgentLimit,
		ManagedAgentsPerSeat:   entitlement.ManagedAgentsPerSeat,
		CurrentPeriodStart:     entitlement.CurrentPeriodStart,
		CurrentPeriodEnd:       entitlement.CurrentPeriodEnd,
		Paid:                   entitlement.Paid,
		SubscriptionStatus:     entitlement.SubscriptionStatus,
	}
}

func seatOrderFromStore(order seatbilling.SeatOrderWithPayment) seatOrderView {
	return seatOrderView{
		ID:                 order.Order.ID,
		OrganizationID:     order.Order.OrganizationID,
		OrderType:          order.Order.OrderType,
		Status:             order.Order.Status,
		FromPlanKey:        order.Order.FromPlanKey,
		ToPlanKey:          order.Order.ToPlanKey,
		BillingPeriod:      order.Order.BillingPeriod,
		SeatQuantityBefore: order.Order.SeatQuantityBefore,
		SeatQuantityAfter:  order.Order.SeatQuantityAfter,
		SeatDelta:          order.Order.SeatDelta,
		AmountCents:        order.Order.AmountCents,
		Currency:           order.Order.Currency,
		Provider:           order.Payment.Provider,
		ProviderOrderID:    order.Payment.ProviderOrderID,
		PaymentStatus:      order.Payment.Status,
		ExpiresAt:          order.Order.ExpiresAt,
		CreatedByUserID:    order.Order.CreatedByUserID,
		CancelledByUserID:  order.Order.CancelledByUserID,
		SettledAt:          order.Order.SettledAt,
		AppliedAt:          order.Order.AppliedAt,
		PaidAt:             order.Payment.PaidAt,
		ClosedAt:           order.Payment.ClosedAt,
		CreatedAt:          order.Order.CreatedAt,
		UpdatedAt:          order.Order.UpdatedAt,
	}
}

func seatPaymentHistoryFromStore(row seatbilling.SeatPaymentHistoryRow) seatPaymentHistoryView {
	return seatPaymentHistoryView{
		OrderID:         row.Order.ID,
		ProviderOrderID: row.Payment.ProviderOrderID,
		Status:          row.Order.Status,
		AmountCents:     row.Order.AmountCents,
		Currency:        row.Order.Currency,
		CreatedAt:       row.Payment.CreatedAt,
		PaidAt:          row.Payment.PaidAt,
	}
}
