import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose'
import { HydratedDocument, Types } from 'mongoose'

export type DomainDocument = HydratedDocument<Domain>

// Overall lifecycle of a customer email domain:
//   pending    — created in Stalwart, ownership TXT not yet seen
//   verifying  — ownership confirmed, mail records still propagating / failing
//   active     — every required record (MX/SPF/DKIM/DMARC) resolves correctly
//   error      — Stalwart provisioning failed (see stalwart.error)
export type DomainStatus = 'pending' | 'verifying' | 'active' | 'error'

// Per-record verification tone — mirrors the frontend DNS_FIX semantics exactly
// (ok / warn / bad), plus `pending` for "not checked yet".
export type RecordStatus = 'ok' | 'warn' | 'bad' | 'pending'

// Which DNS concern a record belongs to. `ownership` is the one-time TXT proving
// the customer controls the domain; the other four map to the UI's status slots.
export type RecordKind = 'ownership' | 'mx' | 'spf' | 'dkim' | 'dmarc'

export type DmarcPolicy = 'none' | 'quarantine' | 'reject'

// A single expected DNS record + the last observed result. `expected` is the
// authoritative value (from Stalwart's dnsZoneFile, except ownership which we
// mint). `host` is the relative name to paste at a DNS provider ('@' for apex);
// `fqdn` is the full name we actually query.
@Schema({ _id: false })
export class DomainRecord {
  @Prop({ required: true, enum: ['ownership', 'mx', 'spf', 'dkim', 'dmarc'] })
  kind!: RecordKind

  @Prop({ required: true })
  type!: string // DNS record type: 'TXT' | 'MX' | 'CNAME'

  @Prop({ required: true })
  host!: string // relative host, e.g. '@', '_dmarc', 'sel._domainkey'

  @Prop({ required: true })
  fqdn!: string // full name queried, e.g. '_dmarc.acme.dk'

  @Prop({ required: true })
  expected!: string

  @Prop({ type: Number })
  priority?: number // MX only

  @Prop()
  observed?: string

  @Prop({ enum: ['ok', 'warn', 'bad', 'pending'], default: 'pending' })
  status!: RecordStatus

  @Prop()
  checkedAt?: Date
}
export const DomainRecordSchema = SchemaFactory.createForClass(DomainRecord)

// A customer-owned email domain. Distinct from the loose `tenant.domains`
// string[] (kept as a denormalised primary-host convenience) — this collection
// holds the per-domain provisioning + DNS-verification state behind the
// customer-admin Domains page.
@Schema({ collection: 'domains', timestamps: true })
export class Domain {
  @Prop({ type: Types.ObjectId, ref: 'Tenant', required: true, index: true })
  tenantId!: Types.ObjectId

  @Prop({ required: true, lowercase: true, trim: true, index: true })
  domain!: string

  // First domain added for a tenant is primary. Display-only for now.
  @Prop({ default: false })
  isPrimary!: boolean

  // Random token published as a `_dezky-verify.<domain>` TXT to prove ownership.
  @Prop({ required: true })
  verificationToken!: string

  @Prop({ default: false })
  ownershipVerified!: boolean

  @Prop()
  verifiedAt?: Date

  // Customer's chosen DMARC enforcement level (wizard step 5). Drives the
  // expected `_dmarc` value we verify against. Stalwart defaults to reject.
  @Prop({ enum: ['none', 'quarantine', 'reject'], default: 'quarantine' })
  dmarcPolicy!: DmarcPolicy

  // Stalwart x:Domain handle + last provisioning error.
  @Prop({ index: true, sparse: true })
  stalwartId?: string

  @Prop({ default: false })
  stalwartProvisioned!: boolean

  @Prop()
  stalwartError?: string

  // Snapshot of the last expected-vs-observed diff. Replaced wholesale on each
  // recheck. Empty until the first check runs.
  @Prop({ type: [DomainRecordSchema], default: [] })
  records!: DomainRecord[]

  @Prop({ enum: ['pending', 'verifying', 'active', 'error'], default: 'pending', index: true })
  status!: DomainStatus

  @Prop()
  lastCheckedAt?: Date
}

export const DomainSchema = SchemaFactory.createForClass(Domain)

// A tenant can only register a given domain once.
DomainSchema.index({ tenantId: 1, domain: 1 }, { unique: true })
