import { afterEach, beforeEach, describe, expect, test } from 'bun:test';
import type { Pool } from 'pg';

import {
  enqueueReminderChange,
  listPendingReminderSends,
  markReminderSendResult,
} from '@/features/ireminders';
import { injectDb, resetDb } from '@/shared/db';

class FakePool {
  rows: Array<Record<string, unknown>> = [];

  async query<T = Record<string, unknown>>(sql: string, params: unknown[] = []) {
    if (sql.startsWith('INSERT')) {
      const [deviceId, action, payloadJson] = params as [string, string, string];
      const row = {
        id: `id-${this.rows.length + 1}`,
        device_id: deviceId,
        action,
        payload: JSON.parse(payloadJson),
        status: 'queued',
        sent_at: null,
        failure_reason: null,
        created_at: new Date(Date.now() + this.rows.length).toISOString(),
        updated_at: new Date().toISOString(),
      };
      this.rows.push(row);
      return { rows: [{ id: row.id }] as unknown as T[] };
    }
    if (sql.includes('SELECT id, device_id')) {
      const [deviceId] = params as [string];
      const matching = this.rows
        .filter((r) => r.device_id === deviceId && r.status === 'queued')
        .sort((a, b) => String(a.created_at).localeCompare(String(b.created_at)));
      return { rows: matching as unknown as T[] };
    }
    if (sql.includes("status = 'sent'")) {
      const [id] = params as [string];
      const row = this.rows.find((r) => r.id === id);
      if (row) {
        row.status = 'sent';
      }
      return { rows: [] as unknown as T[] };
    }
    if (sql.includes("status = 'failed'")) {
      const [id, reason] = params as [string, string | null];
      const row = this.rows.find((r) => r.id === id);
      if (row) {
        row.status = 'failed';
        row.failure_reason = reason;
      }
      return { rows: [] as unknown as T[] };
    }
    return { rows: [] as unknown as T[] };
  }
}

describe('ireminders sendService', () => {
  let pool: FakePool;

  beforeEach(() => {
    pool = new FakePool();
    injectDb(pool as unknown as Pool);
  });

  afterEach(() => {
    resetDb();
  });

  test('enqueue → listPending → markResult round-trip', async () => {
    const { id } = await enqueueReminderChange({
      deviceId: 'dev-1',
      action: 'create_reminder',
      payload: { calendarIdentifier: 'list-1', title: 'Buy milk', priority: 5 },
    });

    const pending = await listPendingReminderSends('dev-1');
    expect(pending).toHaveLength(1);
    expect(pending[0]?.id).toBe(id);
    expect(pending[0]?.payload.title).toBe('Buy milk');

    await markReminderSendResult({ id, status: 'sent' });
    expect(await listPendingReminderSends('dev-1')).toHaveLength(0);
  });

  test('priority must be within 0–9', async () => {
    await expect(
      enqueueReminderChange({
        deviceId: 'dev-1',
        action: 'create_reminder',
        payload: { calendarIdentifier: 'l', priority: 99 },
      }),
    ).rejects.toThrow();
  });
});
