/**
 * Base class for Multideal Durable Objects.
 *
 * Provides:
 *   POST /arm   { at: number }  — store timestamp, set CF alarm
 *   POST /disarm                — clear alarm + stored timestamp
 *   GET  /status                — return { at: number | null }
 *
 * Concrete subclasses override alarm() to implement entity-specific expiry.
 */
import type { MultidealEnv } from '../lib/env.js';

export abstract class BaseDO {
  protected ctx: DurableObjectState;
  protected env: MultidealEnv;

  constructor(ctx: DurableObjectState, env: MultidealEnv) {
    this.ctx = ctx;
    this.env = env;
  }

  protected abstract className: string;

  async fetch(request: Request): Promise<Response> {
    const url = new URL(request.url);
    const path = url.pathname;

    if (request.method === 'POST' && path === '/arm') {
      const body = (await request.json()) as { at?: number; kind?: string; entityId?: string };
      const at = body.at;
      if (typeof at !== 'number' || !isFinite(at)) {
        return new Response(
          JSON.stringify({ ok: false, error: 'at must be a finite number (epoch ms)' }),
          {
            status: 400,
            headers: { 'Content-Type': 'application/json' },
          },
        );
      }
      await this.ctx.storage.put('arm_at', at);
      if (body.entityId) {
        await this.ctx.storage.put('entity_id', body.entityId);
      }
      if (body.kind) {
        await this.ctx.storage.put('arm_kind', body.kind);
      }
      await this.ctx.storage.setAlarm(at);
      return new Response(JSON.stringify({ ok: true, at }), {
        status: 200,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    if (request.method === 'POST' && path === '/disarm') {
      await this.ctx.storage.deleteAlarm();
      await this.ctx.storage.delete('arm_at');
      await this.ctx.storage.delete('arm_kind');
      await this.ctx.storage.delete('entity_id');
      return new Response(JSON.stringify({ ok: true }), {
        status: 200,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    if (request.method === 'GET' && path === '/status') {
      const at = (await this.ctx.storage.get<number>('arm_at')) ?? null;
      const kind = (await this.ctx.storage.get<string>('arm_kind')) ?? null;
      return new Response(JSON.stringify({ at, kind }), {
        status: 200,
        headers: { 'Content-Type': 'application/json' },
      });
    }

    return new Response(JSON.stringify({ ok: false, error: 'Not found' }), {
      status: 404,
      headers: { 'Content-Type': 'application/json' },
    });
  }

  async alarm(): Promise<void> {
    console.warn(
      JSON.stringify({
        event: 'do_alarm_fired',
        class: this.className,
        id: this.ctx.id.toString(),
        at: Date.now(),
      }),
    );
  }
}
