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

import {
  enqueueNoteChange,
  listPendingNoteSends,
  markNoteSendResult,
} from '@/features/inotes';
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('inotes 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 enqueueNoteChange({
      deviceId: 'dev-1',
      action: 'create_note',
      payload: { folder: 'Personal', name: 'Hello', body: '<p>world</p>' },
    });

    const pending = await listPendingNoteSends('dev-1');
    expect(pending).toHaveLength(1);
    expect(pending[0]?.id).toBe(id);
    expect(pending[0]?.payload.name).toBe('Hello');

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

  test('delete enqueues with only noteIdentifier', async () => {
    const { id } = await enqueueNoteChange({
      deviceId: 'dev-1',
      action: 'delete_note',
      payload: { noteIdentifier: 'note-123' },
    });
    expect(id).toBe('id-1');
  });

  test('rejects unknown action', async () => {
    await expect(
      enqueueNoteChange({
        deviceId: 'dev-1',
        action: 'archive_note' as never,
        payload: { noteIdentifier: 'n' },
      }),
    ).rejects.toThrow();
  });
});
