/**
 * Workspace-scoped domain helpers.
 *
 * Thin DB accessor layer on top of vibn_domains / vibn_domain_events /
 * vibn_billing_ledger. All inserts are idempotent by `(workspace_id, domain)`
 * so an accidental double-POST from an agent can't charge twice.
 */

import { query, queryOne } from '@/lib/db-postgres';
import { encryptSecret } from '@/lib/auth/secret-box';
import { domainTld } from '@/lib/opensrs';

export type DomainStatus =
  | 'pending'       // intent recorded, not yet sent to registrar
  | 'registering'   // OpenSRS call in flight
  | 'active'        // registered, on our books
  | 'failed'        // registrar rejected
  | 'expired'
  | 'deleted';

export interface VibnDomain {
  id: string;
  workspace_id: string;
  domain: string;
  tld: string;
  status: DomainStatus;
  registrar: string;
  registrar_order_id: string | null;
  registrar_username: string | null;
  /** Encrypted; call `getDomainRegistrarPassword` to decrypt. */
  registrar_password_enc: string | null;
  period_years: number;
  whois_privacy: boolean;
  auto_renew: boolean;
  registered_at: Date | null;
  expires_at: Date | null;
  dns_provider: string | null;
  dns_zone_id: string | null;
  dns_nameservers: string[] | null;
  price_paid_cents: number | null;
  price_currency: string | null;
  created_by: string;
  created_at: Date;
  updated_at: Date;
}

export async function listDomainsForWorkspace(workspaceId: string): Promise<VibnDomain[]> {
  return query<VibnDomain>(
    `SELECT * FROM vibn_domains
      WHERE workspace_id = $1
      ORDER BY created_at DESC`,
    [workspaceId],
  );
}

export async function getDomainForWorkspace(
  workspaceId: string,
  domain: string,
): Promise<VibnDomain | null> {
  const row = await queryOne<VibnDomain>(
    `SELECT * FROM vibn_domains
      WHERE workspace_id = $1 AND domain = $2
      LIMIT 1`,
    [workspaceId, domain.toLowerCase()],
  );
  return row ?? null;
}

export interface CreateDomainIntentInput {
  workspaceId: string;
  domain: string;
  createdBy: string;
  periodYears?: number;
  whoisPrivacy?: boolean;
}

export async function createDomainIntent(input: CreateDomainIntentInput): Promise<VibnDomain> {
  const normalized = input.domain.toLowerCase().trim();
  const tld = domainTld(normalized);
  const existing = await getDomainForWorkspace(input.workspaceId, normalized);
  if (existing) return existing;

  const rows = await query<VibnDomain>(
    `INSERT INTO vibn_domains
       (workspace_id, domain, tld, status, registrar, period_years, whois_privacy, created_by)
     VALUES ($1, $2, $3, 'pending', 'opensrs', $4, $5, $6)
     RETURNING *`,
    [
      input.workspaceId,
      normalized,
      tld,
      input.periodYears ?? 1,
      input.whoisPrivacy ?? true,
      input.createdBy,
    ],
  );
  return rows[0];
}

export interface MarkRegisteredInput {
  domainId: string;
  registrarOrderId: string;
  registrarUsername: string;
  registrarPassword: string;
  periodYears: number;
  pricePaidCents: number | null;
  priceCurrency: string | null;
  registeredAt?: Date;
  /** Days from registeredAt to expiry (365*years). */
  expiresAt?: Date;
}

export async function markDomainRegistered(input: MarkRegisteredInput): Promise<VibnDomain> {
  const encPw = encryptSecret(input.registrarPassword);
  const rows = await query<VibnDomain>(
    `UPDATE vibn_domains
        SET status = 'active',
            registrar_order_id = $2,
            registrar_username = $3,
            registrar_password_enc = $4,
            period_years = $5,
            price_paid_cents = $6,
            price_currency = $7,
            registered_at = COALESCE($8, now()),
            expires_at = $9,
            updated_at = now()
      WHERE id = $1
      RETURNING *`,
    [
      input.domainId,
      input.registrarOrderId,
      input.registrarUsername,
      encPw,
      input.periodYears,
      input.pricePaidCents,
      input.priceCurrency,
      input.registeredAt ?? null,
      input.expiresAt ?? null,
    ],
  );
  return rows[0];
}

export interface AttachDomainDnsInput {
  domainId: string;
  dnsProvider: string;
  dnsZoneId: string;
  dnsNameservers: string[];
}

export async function markDomainAttached(input: AttachDomainDnsInput): Promise<VibnDomain> {
  const rows = await query<VibnDomain>(
    `UPDATE vibn_domains
        SET dns_provider = $2,
            dns_zone_id = $3,
            dns_nameservers = $4::jsonb,
            updated_at = now()
      WHERE id = $1
      RETURNING *`,
    [input.domainId, input.dnsProvider, input.dnsZoneId, JSON.stringify(input.dnsNameservers)],
  );
  return rows[0];
}

export async function markDomainFailed(domainId: string, reason: string): Promise<void> {
  const row = await queryOne<{ workspace_id: string }>(
    `UPDATE vibn_domains SET status = 'failed', updated_at = now()
      WHERE id = $1
      RETURNING workspace_id`,
    [domainId],
  );
  if (!row) return;
  await recordDomainEvent({
    domainId,
    workspaceId: row.workspace_id,
    type: 'register.failed',
    payload: { reason },
  });
}

export interface RecordDomainEventInput {
  domainId: string;
  workspaceId: string;
  type: string;
  payload?: Record<string, unknown>;
}

export async function recordDomainEvent(input: RecordDomainEventInput): Promise<void> {
  await query(
    `INSERT INTO vibn_domain_events (domain_id, workspace_id, type, payload)
     VALUES ($1, $2, $3, $4::jsonb)`,
    [input.domainId, input.workspaceId, input.type, JSON.stringify(input.payload ?? {})],
  );
}

export interface RecordLedgerInput {
  workspaceId: string;
  kind: 'debit' | 'credit';
  amountCents: number;
  currency?: string;
  refType?: string;
  refId?: string;
  note?: string;
}

export async function recordLedgerEntry(input: RecordLedgerInput): Promise<void> {
  await query(
    `INSERT INTO vibn_billing_ledger
       (workspace_id, kind, amount_cents, currency, ref_type, ref_id, note)
     VALUES ($1, $2, $3, $4, $5, $6, $7)`,
    [
      input.workspaceId,
      input.kind,
      input.amountCents,
      input.currency ?? 'CAD',
      input.refType ?? null,
      input.refId ?? null,
      input.note ?? null,
    ],
  );
}
