// Package models holds admin's own documents.
//
// These are admin-owned and never shared with the control plane. The two
// structs that ARE shared — Instance and User on the control-plane side — come
// from shared/models, so there is no second copy of those shapes to drift.
package models

import (
	"time"

	"github.com/mrhid6/vantage/shared/license"
	"go.mongodb.org/mongo-driver/v2/bson"
)

// Instance statuses.
const (
	StatusAwaitingLink = "awaiting_link"
	StatusActive       = "active"
	StatusLapsed       = "lapsed"
	StatusCancelled    = "cancelled"
)

// Account statuses.
const (
	AccountActive    = "active"
	AccountSuspended = "suspended"
)

// Licence issuance reasons. These end up in support conversations, so they are
// stable identifiers rather than prose.
const (
	ReasonNew        = "new"
	ReasonRenewal    = "renewal"
	ReasonTierChange = "tier_change"
	ReasonRelink     = "relink"
	ReasonManual     = "manual"
)

// MaxRelinksPerTerm is the customer-facing relink cap.
//
// This is an abuse SIGNAL, not abuse prevention — offline licences cannot be
// revoked, so a determined customer is not stopped by a counter. Its real job is
// to put a human in front of the fourth attempt.
const MaxRelinksPerTerm = 3

// GracePeriod is added to every licence expiry beyond the billing period end,
// so a renewal webhook arriving slightly late does not create a gap in which a
// paying customer's instance goes read-only.
const GracePeriod = 3 * 24 * time.Hour

type Account struct {
	ID               bson.ObjectID `bson:"_id,omitempty" json:"-"`
	AccountID        string        `bson:"account_id" json:"account_id"`
	Name             string        `bson:"name" json:"name"`
	BillingEmail     string        `bson:"billing_email" json:"billing_email"`
	PaddleCustomerID string        `bson:"paddle_customer_id,omitempty" json:"paddle_customer_id,omitempty"`
	Status           string        `bson:"status" json:"status"`
	CreatedAt        time.Time     `bson:"created_at" json:"created_at"`
}

// Instance is admin's record of one deployment.
//
// For cloud, InstanceID equals the control-plane instance_id. For self-hosted it
// is the UUID the customer pasted — their database is theirs, and we cannot see
// it, so this row is the only thing that exists on our side.
type Instance struct {
	ID             bson.ObjectID `bson:"_id,omitempty" json:"-"`
	InstanceID     string        `bson:"instance_id" json:"instance_id"`
	AccountID      string        `bson:"account_id" json:"account_id"`
	Name           string        `bson:"name" json:"name"`
	Slug           string        `bson:"slug,omitempty" json:"slug,omitempty"`
	Deployment     string        `bson:"deployment" json:"deployment"`
	Tier           string        `bson:"tier,omitempty" json:"tier,omitempty"`
	Status         string        `bson:"status" json:"status"`
	CurrentLicense string        `bson:"current_license,omitempty" json:"current_license,omitempty"`
	RelinkCount    int           `bson:"relink_count" json:"relink_count"`
	InjectFailedAt *time.Time    `bson:"inject_failed_at,omitempty" json:"inject_failed_at,omitempty"`
	CreatedAt      time.Time     `bson:"created_at" json:"created_at"`
}

// License is append-only. A renewal writes a new row and sets SupersededBy on
// the old one. Nothing here is ever edited or deleted: when a support question
// arrives about why an instance stopped working on a given date, the answer has
// to still be in the table.
type License struct {
	ID           bson.ObjectID  `bson:"_id,omitempty" json:"-"`
	LicenseID    string         `bson:"license_id" json:"license_id"`
	InstanceID   string         `bson:"instance_id" json:"instance_id"`
	AccountID    string         `bson:"account_id" json:"account_id"`
	Tier         string         `bson:"tier" json:"tier"`
	Deployment   string         `bson:"deployment" json:"deployment"`
	Limits       license.Limits `bson:"limits" json:"limits"`
	Features     []string       `bson:"features" json:"features"`
	IssuedAt     time.Time      `bson:"issued_at" json:"issued_at"`
	ExpiresAt    time.Time      `bson:"expires_at" json:"expires_at"`
	Blob         string         `bson:"blob" json:"-"`
	SupersededBy string         `bson:"superseded_by,omitempty" json:"superseded_by,omitempty"`
	IssuedBy     string         `bson:"issued_by" json:"issued_by"`
	Reason       string         `bson:"reason" json:"reason"`
}

type Subscription struct {
	ID                   bson.ObjectID `bson:"_id,omitempty" json:"-"`
	SubscriptionID       string        `bson:"subscription_id" json:"subscription_id"`
	AccountID            string        `bson:"account_id" json:"account_id"`
	InstanceID           string        `bson:"instance_id,omitempty" json:"instance_id,omitempty"`
	PaddleSubscriptionID string        `bson:"paddle_subscription_id,omitempty" json:"paddle_subscription_id,omitempty"`
	PaddlePriceID        string        `bson:"paddle_price_id,omitempty" json:"paddle_price_id,omitempty"`
	Tier                 string        `bson:"tier" json:"tier"`
	Term                 string        `bson:"term" json:"term"`
	Status               string        `bson:"status" json:"status"`
	CurrentPeriodEnd     time.Time     `bson:"current_period_end" json:"current_period_end"`
}

// Plan is the authoritative tier definition, seeded from shared/license.
//
// It lives in the database so tier contents change without a deploy. Every
// issued licence snapshots it, so editing a plan never rewrites an existing
// licence — the same rule as workflow_runs.steps_snapshot.
type Plan struct {
	ID              bson.ObjectID     `bson:"_id,omitempty" json:"-"`
	Tier            string            `bson:"tier" json:"tier"`
	Name            string            `bson:"name" json:"name"`
	Deployment      string            `bson:"deployment" json:"deployment"`
	Limits          license.Limits    `bson:"limits" json:"limits"`
	Features        []string          `bson:"features" json:"features"`
	PaddleProductID string            `bson:"paddle_product_id,omitempty" json:"paddle_product_id,omitempty"`
	PaddlePriceIDs  map[string]string `bson:"paddle_price_ids,omitempty" json:"paddle_price_ids,omitempty"`
	Active          bool              `bson:"active" json:"active"`
}

type StaffUser struct {
	ID           bson.ObjectID `bson:"_id,omitempty" json:"-"`
	UserID       string        `bson:"user_id" json:"user_id"`
	Email        string        `bson:"email" json:"email"`
	PasswordHash string        `bson:"password_hash" json:"-"`
	Name         string        `bson:"name" json:"name"`
	CreatedAt    time.Time     `bson:"created_at" json:"created_at"`
}

// CustomerUser is a self-hosted customer's login. Cloud customers do not have
// one — they authenticate against the control plane with credentials they
// already hold.
type CustomerUser struct {
	ID                bson.ObjectID `bson:"_id,omitempty" json:"-"`
	UserID            string        `bson:"user_id" json:"user_id"`
	AccountID         string        `bson:"account_id" json:"account_id"`
	Email             string        `bson:"email" json:"email"`
	PasswordHash      string        `bson:"password_hash" json:"-"`
	VerifiedAt        *time.Time    `bson:"verified_at,omitempty" json:"verified_at,omitempty"`
	VerifyTokenHash   string        `bson:"verify_token_hash,omitempty" json:"-"`
	VerifyTokenExpiry *time.Time    `bson:"verify_token_expiry,omitempty" json:"-"`
	CreatedAt         time.Time     `bson:"created_at" json:"created_at"`
}

type AuditEntry struct {
	ID        bson.ObjectID `bson:"_id,omitempty" json:"-"`
	Actor     string        `bson:"actor" json:"actor"`
	Action    string        `bson:"action" json:"action"`
	AccountID string        `bson:"account_id,omitempty" json:"account_id,omitempty"`
	Target    string        `bson:"target,omitempty" json:"target,omitempty"`
	Detail    string        `bson:"detail,omitempty" json:"detail,omitempty"`
	IP        string        `bson:"ip,omitempty" json:"ip,omitempty"`
	CreatedAt time.Time     `bson:"created_at" json:"created_at"`
}
