import { describe, it, expect } from 'vitest';

import {
  emptyPaper,
  extractPapers,
  upsertPaper,
  findPaper,
  integrityIssues,
  canPublish,
  publishPaper,
  touchPaper,
  type Paper,
} from './paperModel';

/** A paper that satisfies the integrity gate. */
function readyPaper(): Paper {
  return {
    ...emptyPaper(),
    title: 'Một nghiên cứu',
    abstractVi: 'Tóm tắt.',
    body: 'Nội dung.',
    sources: [{ id: 's1', title: 'Nguồn A', verifiedReal: true }],
  };
}

describe('paperModel', () => {
  it('extractPapers returns [] for missing/invalid content', () => {
    expect(extractPapers(undefined)).toEqual([]);
    expect(extractPapers(null)).toEqual([]);
    expect(extractPapers({})).toEqual([]);
    expect(extractPapers({ papers: 'nope' })).toEqual([]);
    expect(extractPapers({ papers: [{ noId: true }] })).toEqual([]);
  });

  it('extractPapers keeps well-formed paper objects', () => {
    const p = emptyPaper();
    expect(extractPapers({ papers: [p] })).toHaveLength(1);
  });

  it('upsertPaper appends a new paper and replaces an existing one by id', () => {
    const a = emptyPaper();
    const list = upsertPaper([], a);
    expect(list).toHaveLength(1);
    const changed = { ...a, title: 'changed' };
    const list2 = upsertPaper(list, changed);
    expect(list2).toHaveLength(1);
    expect(findPaper(list2, a.id)?.title).toBe('changed');
  });

  it('integrityIssues (INV-3) flags every missing requirement on an empty paper', () => {
    const issues = integrityIssues(emptyPaper());
    expect(issues.length).toBeGreaterThanOrEqual(4);
    expect(canPublish(emptyPaper())).toBe(false);
  });

  it('a complete paper with verified sources passes the gate', () => {
    const p = readyPaper();
    expect(integrityIssues(p)).toEqual([]);
    expect(canPublish(p)).toBe(true);
  });

  it('an unverified source blocks publication', () => {
    const p: Paper = { ...readyPaper(), sources: [{ id: 's1', title: 'A', verifiedReal: false }] };
    expect(canPublish(p)).toBe(false);
    expect(integrityIssues(p).some((m) => m.includes('xác minh'))).toBe(true);
  });

  it('publishPaper (INV-1) records the human approval and stamps published status', () => {
    const p = publishPaper(readyPaper(), { id: 'u1', name: 'TS. An' }, 'Đồng ý');
    expect(p.status).toBe('published');
    expect(p.approval?.approvedByUserId).toBe('u1');
    expect(p.approval?.approvedByName).toBe('TS. An');
    expect(p.approval?.note).toBe('Đồng ý');
    expect(p.publishedAt).toBeTruthy();
    expect(p.editHistory[p.editHistory.length - 1]?.action).toContain('xuất bản');
  });

  it('publishPaper refuses when the integrity gate is not satisfied', () => {
    expect(() => publishPaper(emptyPaper(), { id: 'u1', name: 'TS. An' })).toThrow();
  });

  it('an already-published paper cannot be re-published through the gate', () => {
    const p = publishPaper(readyPaper(), { id: 'u1', name: 'TS. An' });
    expect(canPublish(p)).toBe(false);
  });

  it('touchPaper appends to the edit history (INV-6)', () => {
    const before = readyPaper();
    const after = touchPaper(before, 'Lưu bản thảo');
    expect(after.editHistory.length).toBe(before.editHistory.length + 1);
    expect(after.editHistory[after.editHistory.length - 1]?.action).toBe('Lưu bản thảo');
  });
});
