import { describe, it, expect, beforeEach } from 'vitest';
import type Database from 'better-sqlite3';
import {
  createDispatch, publishDispatch, archiveDispatch,
  getDispatchById, getLatestPublishedDispatches, getAdjacentDispatches,
  createUser,
} from '../src/lib/db.js';
import { dispatchSlug, parseDispatchSlug } from '../src/lib/format.js';

const db = (globalThis as typeof globalThis & { __bifrost_db?: Database.Database }).__bifrost_db!;

let admin: number;

beforeEach(() => {
  db.exec(`
    DELETE FROM votes;
    DELETE FROM pulses;
    DELETE FROM activity;
    DELETE FROM dispatches;
    DELETE FROM users;
  `);
  admin = createUser({ email: 'a@x.test', password_hash: 'x', name: 'Admin', organisation: 'Fenja', role: 'fenja' });
});

describe('Dispatch status transitions', () => {
  it('draft create leaves published_at null', () => {
    const id = createDispatch({ title: 'T', body: 'B', excerpt: null, kind: 'note', author_id: admin, status: 'draft' });
    const d = getDispatchById(id)!;
    expect(d.status).toBe('draft');
    expect(d.published_at).toBeNull();
  });

  it('published create stamps published_at', () => {
    const id = createDispatch({ title: 'T', body: 'B', excerpt: null, kind: 'decision', author_id: admin, status: 'published' });
    const d = getDispatchById(id)!;
    expect(d.status).toBe('published');
    expect(d.published_at).not.toBeNull();
  });

  it('publishDispatch on a draft stamps published_at', () => {
    const id = createDispatch({ title: 'T', body: 'B', excerpt: null, kind: 'note', author_id: admin, status: 'draft' });
    publishDispatch(id);
    const d = getDispatchById(id)!;
    expect(d.status).toBe('published');
    expect(d.published_at).not.toBeNull();
  });

  it('publishDispatch is idempotent — re-publishing keeps the original published_at', () => {
    const id = createDispatch({ title: 'T', body: 'B', excerpt: null, kind: 'note', author_id: admin, status: 'draft' });
    publishDispatch(id);
    const first = getDispatchById(id)!.published_at;
    // Force a tick so a re-stamp would visibly differ
    publishDispatch(id);
    const second = getDispatchById(id)!.published_at;
    expect(second).toBe(first);
  });

  it('archiveDispatch preserves published_at and only flips status', () => {
    const id = createDispatch({ title: 'T', body: 'B', excerpt: null, kind: 'update', author_id: admin, status: 'published' });
    const before = getDispatchById(id)!.published_at;
    archiveDispatch(id);
    const d = getDispatchById(id)!;
    expect(d.status).toBe('archived');
    expect(d.published_at).toBe(before);
  });

  it('getLatestPublishedDispatches excludes drafts and archived', () => {
    createDispatch({ title: 'A', body: 'B', excerpt: null, kind: 'note', author_id: admin, status: 'draft' });
    const pubId = createDispatch({ title: 'B', body: 'B', excerpt: null, kind: 'note', author_id: admin, status: 'published' });
    const archId = createDispatch({ title: 'C', body: 'B', excerpt: null, kind: 'note', author_id: admin, status: 'published' });
    archiveDispatch(archId);
    const out = getLatestPublishedDispatches(10);
    expect(out.map(d => d.id)).toEqual([pubId]);
  });

  it('getAdjacentDispatches returns prev/next in published order', () => {
    const ids: number[] = [];
    for (let i = 0; i < 3; i += 1) {
      const id = createDispatch({ title: `T${i}`, body: 'B', excerpt: null, kind: 'note', author_id: admin, status: 'published' });
      // Backdate so ordering is deterministic
      db.prepare("UPDATE dispatches SET published_at = datetime('now', ?) WHERE id = ?").run(`-${(3 - i)} days`, id);
      ids.push(id);
    }
    const { prev, next } = getAdjacentDispatches(ids[1]);
    expect(prev?.id).toBe(ids[0]);
    expect(next?.id).toBe(ids[2]);
  });
});

describe('dispatch slug', () => {
  it('produces {id}-kebab(title)', () => {
    expect(dispatchSlug({ id: 7, title: 'A Decision About Telemetry' })).toBe('7-a-decision-about-telemetry');
    expect(dispatchSlug({ id: 12, title: 'Behind the Scenes: April' })).toBe('12-behind-the-scenes-april');
  });

  it('parseDispatchSlug pulls the leading id', () => {
    expect(parseDispatchSlug('7-a-decision-about-telemetry')).toBe(7);
    expect(parseDispatchSlug('99')).toBe(99);
    expect(parseDispatchSlug('not-a-slug')).toBeNull();
    expect(parseDispatchSlug('')).toBeNull();
  });
});
