/**
 * Vendor Violation workflows (FDS §6.10).
 *
 * Warning → Freeze (optional duration) → Unfreeze → Ban
 *
 * State-transition logic lives in domain/violation/machine.ts (pure decider).
 * Side effects are executed by domain/violation/apply-effects.ts.
 */

import type { DrizzleClient } from '../db/client.js';
import * as vendorQueries from '../db/queries/vendors.js';
import type { ResendEnv } from '../email/resend.js';
import { decide } from '@/server/domain/violation/machine.js';
import { applyEffects } from '@/server/domain/violation/apply-effects.js';
import type { ViolationEvent, VendorAccountState } from '@/server/domain/violation/events.js';

// ─── Deps ─────────────────────────────────────────────────────────────────────

export interface ViolationDeps {
  db: DrizzleClient;
  email: ResendEnv;
  /** Vendor email (plaintext) - caller decrypts before passing */
  getVendorEmail: (vendorId: string) => Promise<string | null>;
}

// ─── Errors ───────────────────────────────────────────────────────────────────

export class ViolationError extends Error {
  readonly code: string;
  constructor(code: string, message: string) {
    super(message);
    this.name = 'ViolationError';
    this.code = code;
  }
}

// ─── Internal helper: load vendor + run decider + apply ─────────────────────

async function runViolationTransition(
  deps: ViolationDeps,
  vendorId: string,
  event: ViolationEvent,
): Promise<void> {
  const vendor = await vendorQueries.findById(deps.db, vendorId);
  if (!vendor) throw new ViolationError('VENDOR_NOT_FOUND', `Vendor ${vendorId} not found`);

  const result = decide(
    { state: vendor.accountState as VendorAccountState, businessName: vendor.businessName },
    event,
  );

  if (!result.ok) {
    let code: string;
    if (result.error === 'ALREADY_BANNED') code = 'ALREADY_BANNED';
    else if (result.error === 'NOT_FROZEN') code = 'NOT_FROZEN';
    else if (event.kind === 'freeze_requested') code = 'VENDOR_BANNED';
    else code = 'VENDOR_BANNED';
    throw new ViolationError(code, result.message);
  }

  const vendorEmail = await deps.getVendorEmail(vendorId);
  await applyEffects(
    {
      db: deps.db,
      email: deps.email,
      vendorEmail,
      vendorBusinessName: vendor.businessName,
    },
    result.effects,
  );
}

// ─── issueWarning ─────────────────────────────────────────────────────────────

export async function issueWarning(
  deps: ViolationDeps,
  { adminId, vendorId, note }: { adminId: string; vendorId: string; note: string },
) {
  await runViolationTransition(deps, vendorId, {
    kind: 'warn_requested',
    adminId,
    vendorId,
    note,
    at: new Date(),
  });
  return { vendorId, action: 'warning', note };
}

// ─── freezeVendor ─────────────────────────────────────────────────────────────

export async function freezeVendor(
  deps: ViolationDeps,
  {
    adminId,
    vendorId,
    reason,
    durationDays,
  }: { adminId: string; vendorId: string; reason: string; durationDays?: number },
) {
  const at = new Date();
  const days = durationDays ?? null;
  await runViolationTransition(deps, vendorId, {
    kind: 'freeze_requested',
    adminId,
    vendorId,
    reason,
    durationDays: days,
    at,
  });
  const unfreezeAt = days !== null ? new Date(at.getTime() + days * 24 * 60 * 60 * 1000) : null;
  return { vendorId, action: 'freeze', unfreezeAt };
}

// ─── unfreezeVendor ───────────────────────────────────────────────────────────

export async function unfreezeVendor(
  deps: ViolationDeps,
  { adminId, vendorId }: { adminId: string; vendorId: string },
) {
  await runViolationTransition(deps, vendorId, {
    kind: 'unfreeze_requested',
    adminId,
    vendorId,
    at: new Date(),
  });
  return { vendorId, action: 'unfreeze' };
}

// ─── banVendor ────────────────────────────────────────────────────────────────

export async function banVendor(
  deps: ViolationDeps,
  { adminId, vendorId, reason }: { adminId: string; vendorId: string; reason: string },
) {
  await runViolationTransition(deps, vendorId, {
    kind: 'ban_requested',
    adminId,
    vendorId,
    reason,
    at: new Date(),
  });
  return { vendorId, action: 'ban', reason };
}
