package db

import (
	"encoding/json"
	"time"
)

// Mailbox is a real, IMAP-retrievable local mailbox — distinct from Sender (which is
// relay/auth-only). PasswordHash authenticates the self-service web portal only;
// IMAP/SMTP client login always goes through a MailboxAppPassword instead.
type Mailbox struct {
	ID           int64
	Email        string
	DomainID     int64
	PasswordHash string
	IsActive     bool
	QuotaBytes   int64
	UsedBytes    int64
	DEKWrapped   []byte
	DEKNonce     []byte
	CreatedAt    time.Time
	CreatedBy    *int64
	TOTPSecret   string
	TOTPEnabled  bool
	// MFAExempt overrides [Auth] enforce_mailbox_mfa off for this mailbox specifically,
	// even if its domain isn't exempt.
	MFAExempt bool
	// GroupMessages collapses a run of same-subject messages in a folder view into one
	// expandable row when true. Off by default — a display preference, not a policy.
	GroupMessages bool
	// RemoteImagesMode is "ask" (default), "trusted", or "always" — see the
	// remote_images_mode column comment in schema.go.
	RemoteImagesMode string
}

// MailboxSession is a self-service webmail portal login — a parallel schema to
// AdminSession, not shared (see esrv_mailbox_sessions in schema.go).
type MailboxSession struct {
	Token       string
	MailboxID   int64
	MFAVerified bool
	CreatedAt   time.Time
	ExpiresAt   time.Time
}

// MailboxWebAuthnCredential is a mailbox owner's passkey — a parallel schema to
// WebAuthnCredential, not shared.
type MailboxWebAuthnCredential struct {
	ID             int64
	MailboxID      int64
	Name           string
	CredentialID   string
	CredentialData string
	CreatedAt      time.Time
}

// MailboxAlias is an alternate address for a mailbox — receive-only by default, or
// also usable as MAIL FROM once authenticated (CanSendAs). Login is always the
// mailbox's own primary address, never an alias.
type MailboxAlias struct {
	ID        int64
	MailboxID int64
	Email     string
	DomainID  int64
	CanSendAs bool
	IsActive  bool
	CreatedAt time.Time
}

// MailboxAllowBlockEntry is one allow-, block-, or junk-list pattern for a mailbox —
// see esrv_mailbox_allowblock's schema comment for what each list_type means.
type MailboxAllowBlockEntry struct {
	ID        int64
	MailboxID int64
	ListType  string // "allow" | "block" | "junk"
	Pattern   string
	CreatedAt time.Time
}

// MailboxContact is one entry in a mailbox owner's own address book.
type MailboxContact struct {
	ID        int64
	MailboxID int64
	Email     string
	Name      string
	Phone     string
	CreatedAt time.Time
}

// MailboxFilterRule is one priority-ordered, first-match-wins delivery rule.
// ConditionField/Op/Value are the legacy single-condition columns; ConditionsJSON
// (when non-empty) is the current multi-condition representation — see Conditions().
type MailboxFilterRule struct {
	ID                int64
	MailboxID         int64
	Name              string
	Priority          int
	ConditionField    string // "from" | "to" | "subject" | "body" | "has_attachment" | "recipient_type"
	ConditionOp       string // "contains" | "equals" | "starts_with"
	ConditionValue    string
	Action            string // "move_to_folder" | "delete" | "mark_read" | "mark_as_spam" | "forward"
	ActionValue       string
	ActionOptionsJSON string
	IsActive          bool
	ConditionsJSON    string
	MatchType         string // "all" (AND, default) | "any" (OR)
	CreatedAt         time.Time
}

// RuleCondition is one condition within a filter rule's "if" clause. Value may hold
// several "\n"-joined values (e.g. several From addresses, or several subject
// keywords picked in the rule builder's chip input) — matches if any one does, see
// mailstore.matchCondition.
type RuleCondition struct {
	Field string `json:"field"`
	Op    string `json:"op"`
	Value string `json:"value"`
}

// RuleActionOptions holds action-specific parameters that only some actions need —
// currently just "forward"'s KeepCopy. Stored as MailboxFilterRule.ActionOptionsJSON.
type RuleActionOptions struct {
	// KeepCopy: for the "forward" action, whether a copy is also stored in this
	// mailbox (true) or the message is forwarded and dropped otherwise (false).
	// Defaults true (see ActionOptions) — the safer default is to not silently lose
	// mail just because forwarding was set up.
	KeepCopy bool `json:"keep_copy"`
}

// ActionOptions parses ActionOptionsJSON, defaulting KeepCopy true for a rule created
// before this existed (or with the field simply left unset).
func (r MailboxFilterRule) ActionOptions() RuleActionOptions {
	opts := RuleActionOptions{KeepCopy: true}
	if r.ActionOptionsJSON != "" {
		json.Unmarshal([]byte(r.ActionOptionsJSON), &opts)
	}
	return opts
}

// Conditions returns this rule's conditions and how they combine ("all"=AND,
// "any"=OR) — parses ConditionsJSON when present, falling back to the single legacy
// condition_field/op/value columns for rules created before multi-condition support
// existed. Shared by mailstore.ApplyRules (evaluation) and the webui (display), so
// both stay in sync with the same fallback rule.
func (r MailboxFilterRule) Conditions() ([]RuleCondition, string) {
	if r.ConditionsJSON != "" {
		var parsed []RuleCondition
		if err := json.Unmarshal([]byte(r.ConditionsJSON), &parsed); err == nil && len(parsed) > 0 {
			matchType := r.MatchType
			if matchType != "any" {
				matchType = "all"
			}
			return parsed, matchType
		}
	}
	return []RuleCondition{{Field: r.ConditionField, Op: r.ConditionOp, Value: r.ConditionValue}}, "all"
}

// MailboxAppPassword is the only credential an IMAP/SMTP client ever uses. Plaintext
// is shown once at creation and never stored. ExpiresAt is nil for a password that
// never expires (the default).
type MailboxAppPassword struct {
	ID           int64
	MailboxID    int64
	Label        string
	PasswordHash string
	IsActive     bool
	CreatedAt    time.Time
	LastUsedAt   *time.Time
	ExpiresAt    *time.Time
}

// MailboxMessage is one stored message. CachedFrom/CachedTo/CachedSubject are
// plaintext by design (see schema.go); the rest of the message lives encrypted at
// StoragePath. CachedTo exists purely so folder listings (e.g. Sent) can show the
// recipient without decrypting every message just to render a list.
type MailboxMessage struct {
	ID              int64
	MailboxID       int64
	Folder          string
	MessageIDHeader string
	Flags           string
	InternalDate    time.Time
	SizeBytes       int64
	CachedFrom      string
	CachedTo        string
	CachedSubject   string
	CachedPreview   string
	StoragePath     string
	Nonce           []byte
	CreatedAt       time.Time
}

// MailboxSignature is one of a mailbox's saved email signatures — HTML content, shown
// as an option in compose and optionally auto-inserted for a new message or a
// reply/forward (IsDefaultNew/IsDefaultReply).
type MailboxSignature struct {
	ID             int64
	MailboxID      int64
	Name           string
	ContentHTML    string
	IsDefaultNew   bool
	IsDefaultReply bool
	CreatedAt      time.Time
}

// SignatureAliasDefault is one per-alias override of which signature is the default
// for a mailbox with send-as aliases — see GetDefaultSignature.
type SignatureAliasDefault struct {
	ID          int64
	MailboxID   int64
	ForEmail    string
	ForReply    bool
	SignatureID int64
}

// MailboxSMIMEIdentity is one of a mailbox's own S/MIME certificate + private key
// pairs — a mailbox may hold several. Both halves are stored plain: S/MIME is
// sign-only in this codebase, so the key never protects anything beyond what the
// server already has access to.
type MailboxSMIMEIdentity struct {
	ID        int64
	MailboxID int64
	CertPEM   string
	KeyPEM    string
	NotAfter  time.Time
	CreatedAt time.Time
}

// MailboxSMIMEContact is another address's public certificate a mailbox owner has
// collected, either added by hand or auto-captured off a verified signature.
type MailboxSMIMEContact struct {
	ID        int64
	MailboxID int64
	Email     string
	CertPEM   string
	CreatedAt time.Time
}

// MailboxPGPIdentity is one of a mailbox's own PGP keypairs — a mailbox may hold
// several. PrivateKeyArmor is stored exactly as the pgp package serializes it,
// already passphrase-protected via OpenPGP's own native key-encryption format (no
// separate ciphertext/nonce/salt columns needed, unlike MailboxSMIMEIdentity).
// Label is a free-text user note distinguishing keys (PGP keys have no expiry).
type MailboxPGPIdentity struct {
	ID              int64
	MailboxID       int64
	Label           string
	Email           string
	Fingerprint     string
	PublicKeyArmor  string
	PrivateKeyArmor string
	CreatedAt       time.Time
}

// MailboxPGPContact is another address's PGP public key a mailbox owner has
// collected — mirrors MailboxSMIMEContact.
type MailboxPGPContact struct {
	ID             int64
	MailboxID      int64
	Email          string
	Label          string
	PublicKeyArmor string
	Fingerprint    string
	CreatedAt      time.Time
}
