package billing

import (
	"context"
	"errors"
)

var (
	ErrDisabled         = errors.New("billing is not configured")
	ErrInvalidPlan      = errors.New("invalid billing plan")
	ErrInvalidRequest   = errors.New("invalid billing request")
	ErrNotFound         = errors.New("billing record not found")
	ErrConflict         = errors.New("billing request is already being created")
	ErrInvalidSignature = errors.New("invalid stripe signature")
	ErrCustomerRequired = errors.New("stripe customer is not available")
)

const (
	PlanFree    = "free"
	PlanStarter = "starter"
	PlanPro     = "pro"
)

type CheckoutAttempt struct {
	ID                string `bson:"_id"`
	UID               int64  `bson:"uid"`
	RequestID         string `bson:"request_id"`
	PlanID            string `bson:"plan_id"`
	PriceID           string `bson:"price_id"`
	SessionID         string `bson:"session_id,omitempty"`
	URL               string `bson:"url,omitempty"`
	ExpiresAt         int64  `bson:"expires_at,omitempty"`
	Status            string `bson:"status"`
	PaymentStatus     string `bson:"payment_status,omitempty"`
	FulfillmentStatus string `bson:"fulfillment_status"`
	CustomerID        string `bson:"customer_id,omitempty"`
	SubscriptionID    string `bson:"subscription_id,omitempty"`
	Error             string `bson:"error,omitempty"`
	CreatedAt         int64  `bson:"created_at"`
	UpdatedAt         int64  `bson:"updated_at"`
}

type SubscriptionState struct {
	UID                int64
	PlanID             string
	PaidPlanID         string
	Status             string
	CustomerID         string
	SubscriptionID     string
	CurrentPeriodStart int64
	CurrentPeriodEnd   int64
	CancelAtPeriodEnd  bool
	AdminOverride      bool
}

type ProviderCheckout struct {
	SessionID      string
	URL            string
	ExpiresAt      int64
	Status         string
	PaymentStatus  string
	PriceID        string
	CustomerID     string
	SubscriptionID string
}

type CheckoutParams struct {
	UID            int64
	AttemptID      string
	PlanID         string
	PriceID        string
	CustomerID     string
	SuccessURL     string
	CancelURL      string
	IdempotencyKey string
}

type ProviderSubscription struct {
	ID                 string
	CustomerID         string
	Status             string
	PriceID            string
	CurrentPeriodStart int64
	CurrentPeriodEnd   int64
	CancelAtPeriodEnd  bool
}

type Event struct {
	ID                string
	Type              string
	Created           int64
	ObjectID          string
	SubscriptionID    string
	CustomerID        string
	PriceID           string
	Status            string
	PeriodStart       int64
	PeriodEnd         int64
	CancelAtPeriodEnd bool
}

type EntitlementUpdate struct {
	EventID            string
	EventCreatedAt     int64
	UID                int64
	PlanID             string
	PaidPlanID         string
	Status             string
	CustomerID         string
	SubscriptionID     string
	CurrentPeriodStart int64
	CurrentPeriodEnd   int64
	CancelAtPeriodEnd  bool
}

type PurchaseAudit struct {
	ID                   string `bson:"_id"`
	UID                  int64  `bson:"uid"`
	PlanID               string `bson:"plan_id"`
	StripeEventID        string `bson:"stripe_event_id"`
	StripeSessionID      string `bson:"stripe_session_id,omitempty"`
	StripeCustomerID     string `bson:"stripe_customer_id,omitempty"`
	StripeSubscriptionID string `bson:"stripe_subscription_id,omitempty"`
	Status               string `bson:"status"`
	CreatedAt            int64  `bson:"created_at"`
}

type Provider interface {
	CreateCheckout(context.Context, CheckoutParams) (*ProviderCheckout, error)
	GetCheckout(context.Context, string) (*ProviderCheckout, error)
	GetSubscription(context.Context, string) (*ProviderSubscription, error)
	CreatePortal(context.Context, string, string) (string, error)
	VerifyEvent([]byte, string, string) (*Event, error)
}

type Repository interface {
	InsertCheckout(context.Context, *CheckoutAttempt) (bool, error)
	GetCheckoutByRequest(context.Context, int64, string) (*CheckoutAttempt, error)
	GetCheckout(context.Context, int64, string) (*CheckoutAttempt, error)
	GetCheckoutBySession(context.Context, string) (*CheckoutAttempt, error)
	UpdateCheckout(context.Context, *CheckoutAttempt) error
	GetSubscription(context.Context, int64) (*SubscriptionState, error)
	FindUIDByCustomer(context.Context, string) (int64, error)
	ApplyEntitlement(context.Context, EntitlementUpdate) (bool, error)
	// ClaimWebhook atomically records an event as being handled, reporting false when another
	// delivery already claimed it. Stripe retries aggressively and can deliver the same event
	// concurrently, so a read-then-write check would let two deliveries both proceed.
	ClaimWebhook(ctx context.Context, id, eventType string, created int64) (claimed bool, err error)
	// ReleaseWebhookClaim drops a claim whose processing failed, so the next delivery retries.
	ReleaseWebhookClaim(ctx context.Context, id string) error
	UpsertPurchase(context.Context, *PurchaseAudit) error
}
