import { BaseDO } from './_base.js';
import type { MultidealEnv } from '../lib/env.js';
import { getTransactionalDb } from '../lib/db.js';
import { handleGroupDealDeadline, handleGroupPartialTimeout } from '../lifecycle/group-deals.js';
import { createPushClient } from '@/server/push/send';
import type { DoClient } from '@/server/services/types';
import type { PushClient } from '@/server/push/types';

/**
 * GroupDealDO — Durable Object for group deal deadline alarms.
 *
 * Two alarm kinds stored in arm_kind:
 *   'deadline'          — group deal window/extendedDeadline has passed
 *   'partial_decision'  — vendor 24h decision window expired (PARTIAL_PENDING)
 */
export class GroupDealDO extends BaseDO {
  protected override className = 'GroupDealDO';
  constructor(ctx: DurableObjectState, env: MultidealEnv) {
    super(ctx, env);
  }

  override async alarm(): Promise<void> {
    const groupDealId = (await this.ctx.storage.get<string>('entity_id')) ?? this.ctx.id.toString();
    const kind = (await this.ctx.storage.get<string>('arm_kind')) ?? 'deadline';
    try {
      const db = getTransactionalDb(this.env);
      const push =
        this.env.VAPID_PUBLIC_KEY && this.env.VAPID_PRIVATE_KEY && this.env.VAPID_SUBJECT
          ? createPushClient(db, {
              VAPID_PUBLIC_KEY: this.env.VAPID_PUBLIC_KEY,
              VAPID_PRIVATE_KEY: this.env.VAPID_PRIVATE_KEY,
              VAPID_SUBJECT: this.env.VAPID_SUBJECT,
              DATABASE_URL: this.env.DATABASE_URL,
            })
          : {
              sendToUser: async () => undefined as unknown,
              sendToVendor: async () => undefined as unknown,
            };
      // Build a minimal DoClient from the DO worker's namespace bindings.
      // Used by the group-deal workflow to re-arm/disarm alarms on state transitions.

      const noop = async (..._args: unknown[]) => {};
      const doClient: DoClient = {
        armDealAlarm: async (dealId, at) => {
          const stub = this.env.DEAL_DO.get(this.env.DEAL_DO.idFromName(dealId));
          await stub.fetch('https://do/arm', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ at: at.getTime() }),
          });
        },
        disarmDealAlarm: async (dealId) => {
          const stub = this.env.DEAL_DO.get(this.env.DEAL_DO.idFromName(dealId));
          await stub.fetch('https://do/disarm', { method: 'POST' });
        },
        armGoldWindowAlarm: noop,
        disarmGoldWindowAlarm: noop,
        armGroupDealAlarm: async (id, at, kind) => {
          const stub = this.env.GROUP_DEAL_DO.get(this.env.GROUP_DEAL_DO.idFromName(id));
          await stub.fetch('https://do/arm', {
            method: 'POST',
            headers: { 'Content-Type': 'application/json' },
            body: JSON.stringify({ at: at.getTime(), kind: kind ?? 'deadline' }),
          });
        },
        disarmGroupDealAlarm: async (id) => {
          const stub = this.env.GROUP_DEAL_DO.get(this.env.GROUP_DEAL_DO.idFromName(id));
          await stub.fetch('https://do/disarm', { method: 'POST' });
        },
        armPersonalOfferAlarm: noop,
        disarmPersonalOfferAlarm: noop,
        armScheduledPublishAlarm: noop,
        disarmScheduledPublishAlarm: noop,
        armSupportTicketAlarm: noop,
        disarmSupportTicketAlarm: noop,
        armSupportCaseAlarm: noop,
        disarmSupportCaseAlarms: noop,
      };

      const publicBase =
        (this.env as { PUBLIC_SITE_URL?: string }).PUBLIC_SITE_URL ?? 'https://dev.multi.deal';
      const bucket = this.env.R2_BUCKET;
      const deps = {
        db,
        push: push as PushClient,
        qrSecret: this.env.QR_SECRET ?? '',
        doClient,
        payments: async () => {
          const { getPaymentProvider } = await import('@/server/payments/get-provider.js');
          return getPaymentProvider(this.env);
        },
        storage: {
          bucket: bucket!,
          publicBase,
          uploadQr: async (purchaseId: string, token: string) => {
            if (!bucket) throw new Error('R2_BUCKET binding required for group deal QR upload');
            const { uploadPurchaseQr } = await import('@/server/storage/qr_png.js');
            return uploadPurchaseQr(bucket, purchaseId, token, publicBase);
          },
        },
      };

      if (kind === 'partial_decision') {
        await handleGroupPartialTimeout(deps, groupDealId);
      } else {
        await handleGroupDealDeadline(deps, groupDealId);
      }

      await this.ctx.storage.delete('arm_at');
      await this.ctx.storage.delete('arm_kind');
    } catch (err) {
      console.error(
        JSON.stringify({
          event: 'do_alarm_error',
          class: 'GroupDealDO',
          groupDealId,
          kind,
          error: err instanceof Error ? err.message : String(err),
        }),
      );
      throw err;
    }
  }
}
