import { eq } from 'drizzle-orm';
import type { Event } from './machine';
import { returns, returnEvents, outbox } from '@/server/db/schema';
import type { DrizzleClient } from '@/server/db/client';
import {
  computeVendorRespondBy,
  computeBuyerShipBy,
  computeVendorInspectBy,
} from '@/server/returns/sla';

export type ApplyContext = {
  tx: DrizzleClient;
  returnId: string;
  now: Date;
};

export async function applyEvents(ctx: ApplyContext, events: Event[]): Promise<void> {
  for (const ev of events) {
    const rows = await ctx.tx
      .select({ status: returns.status, receivedAt: returns.receivedAt })
      .from(returns)
      .where(eq(returns.id, ctx.returnId));
    const current = rows[0];
    if (!current) throw new Error(`Return not found: ${ctx.returnId}`);
    const fromStatus = current.status;

    const patch: Record<string, unknown> = { updatedAt: ctx.now };
    let toStatus: string | null = null;
    let outboxEvent: string | null = null;

    switch (ev.kind) {
      case 'approved':
        toStatus = 'approved';
        patch.buyerShipBy = computeBuyerShipBy(ctx.now);
        outboxEvent = 'returns.approved';
        break;
      case 'denied':
        toStatus = 'denied';
        patch.closedAt = ctx.now;
        outboxEvent = 'returns.denied';
        break;
      case 'cancelled':
        toStatus = 'cancelled';
        patch.closedAt = ctx.now;
        outboxEvent = 'returns.cancelled';
        break;
      case 'tracking_entered':
        toStatus = 'in_transit';
        patch.carrier = ev.carrier;
        patch.trackingNumber = ev.tracking;
        patch.trackingEnteredAt = ctx.now;
        outboxEvent = 'returns.tracking_entered';
        break;
      case 'received':
        toStatus = 'received';
        patch.receivedAt = ctx.now;
        patch.vendorInspectBy = computeVendorInspectBy(ctx.now);
        outboxEvent = 'returns.received';
        break;
      case 'refund_executed':
        toStatus = 'refunded';
        patch.closedAt = ctx.now;
        outboxEvent = ev.actorType === 'system' ? 'returns.auto_refunded_sla' : 'returns.refunded';
        break;
      case 'expired':
        toStatus = 'expired';
        patch.closedAt = ctx.now;
        outboxEvent = 'returns.sla_expired_buyer_ship';
        break;
    }

    if (toStatus) patch.status = toStatus as never;

    await ctx.tx.update(returns).set(patch).where(eq(returns.id, ctx.returnId));

    await ctx.tx.insert(returnEvents).values({
      returnId: ctx.returnId,
      fromStatus,
      toStatus: toStatus as never,
      actorType: ev.actorType,
      actorId: ev.actorId,
      reason: 'reason' in ev ? (ev.reason ?? null) : null,
      metadata: {},
    });

    if (outboxEvent) {
      await ctx.tx.insert(outbox).values({
        aggregateType: 'return',
        aggregateId: ctx.returnId,
        eventType: outboxEvent,
        payload: { returnId: ctx.returnId },
      });
    }
  }
}

export function initialDeadlines(now: Date) {
  return { vendorRespondBy: computeVendorRespondBy(now) };
}
