import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import Database from 'better-sqlite3';
import { mkdtemp, writeFile, mkdir, rm } from 'node:fs/promises';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { runMigrations } from '@main/db/migrations/index.js';
import { NoteRepository } from '@main/repository/NoteRepository.js';
import { ImportService } from '@main/services/ImportService.js';
import { MediaStore } from '@main/services/MediaStore.js';

describe('ImportService.applySyncFromDir', () => {
  let db: Database.Database;
  let repo: NoteRepository;
  let svc: ImportService;
  let workDir: string;

  beforeEach(async () => {
    db = new Database(':memory:');
    db.pragma('foreign_keys = ON');
    runMigrations(db);
    repo = new NoteRepository(db);
    workDir = await mkdtemp(join(tmpdir(), 'inkling-sync-'));
    const mediaStore = new MediaStore(workDir);
    svc = new ImportService(repo, mediaStore);
  });

  afterEach(async () => {
    db.close();
    await rm(workDir, { recursive: true, force: true });
  });

  it('inserts new notes and reports changedCount', async () => {
    const notesDir = join(workDir, 'notes');
    await mkdir(notesDir, { recursive: true });
    await writeFile(
      join(notesDir, 'a.md'),
      `---\nid: 00000000-0000-0000-0000-000000000001\ncreated_at: 2026-05-09T00:00:00Z\nupdated_at: 2026-05-10T00:00:00Z\ntitle: title\ntitle_source: ai\nsummary: summary\nsummary_source: ai\nstatus: active\ninkling_export_version: 1\n---\n\n# title\n\n> summary\n\nbody\n`
    );
    const r = await svc.applySyncFromDir(workDir);
    expect(r.changedCount).toBe(1);
    const note = repo.findById('00000000-0000-0000-0000-000000000001');
    expect(note?.rawText).toBe('body');
  });

  it('skips unchanged notes (no changedCount increment)', async () => {
    const created = repo.create({ rawText: 'body' });
    db.prepare(`UPDATE notes SET updated_at=? WHERE id=?`).run('2026-05-15T00:00:00Z', created.id);
    const notesDir = join(workDir, 'notes');
    await mkdir(notesDir, { recursive: true });
    await writeFile(
      join(notesDir, 'a.md'),
      `---\nid: ${created.id}\ncreated_at: 2026-05-09T00:00:00Z\nupdated_at: 2026-05-10T00:00:00Z\ntitle: t\ntitle_source: ai\nsummary: s\nsummary_source: ai\nstatus: active\ninkling_export_version: 1\n---\n\n# t\n\n> s\n\nbody\n`
    );
    const r = await svc.applySyncFromDir(workDir);
    expect(r.changedCount).toBe(0);
  });

  it('returns changedCount=0 for an empty notes directory', async () => {
    const notesDir = join(workDir, 'notes');
    await mkdir(notesDir, { recursive: true });
    const r = await svc.applySyncFromDir(workDir);
    expect(r.changedCount).toBe(0);
  });

  it('updates a note when source updatedAt is newer', async () => {
    const created = repo.create({ rawText: 'old body' });
    db.prepare(`UPDATE notes SET updated_at=? WHERE id=?`).run('2026-05-01T00:00:00Z', created.id);
    const notesDir = join(workDir, 'notes');
    await mkdir(notesDir, { recursive: true });
    await writeFile(
      join(notesDir, 'a.md'),
      `---\nid: ${created.id}\ncreated_at: 2026-05-09T00:00:00Z\nupdated_at: 2026-05-10T00:00:00Z\ntitle: t\ntitle_source: ai\nsummary: s\nsummary_source: ai\nstatus: active\ninkling_export_version: 1\n---\n\n# t\n\n> s\n\nnew body\n`
    );
    const r = await svc.applySyncFromDir(workDir);
    expect(r.changedCount).toBe(1);
    const note = repo.findById(created.id);
    expect(note?.rawText).toBe('new body');
  });

  it('preserves status field from frontmatter', async () => {
    const notesDir = join(workDir, 'notes');
    await mkdir(notesDir, { recursive: true });
    await writeFile(
      join(notesDir, 'a.md'),
      `---\nid: 00000000-0000-0000-0000-000000000002\ncreated_at: 2026-05-09T00:00:00Z\nupdated_at: 2026-05-10T00:00:00Z\ntitle: t\ntitle_source: ai\nsummary: s\nsummary_source: ai\nstatus: archived\nstatus_changed_at: 2026-05-08T00:00:00Z\nmove_reason: done\ninkling_export_version: 1\n---\n\n# t\n\n> s\n\nbody\n`
    );
    await svc.applySyncFromDir(workDir);
    const note = repo.findById('00000000-0000-0000-0000-000000000002');
    expect(note?.status).toBe('archived');
    expect(note?.statusChangedAt).toBe('2026-05-08T00:00:00Z');
    expect(note?.moveReason).toBe('done');
  });

  it('preserves dueDate from frontmatter', async () => {
    const notesDir = join(workDir, 'notes');
    await mkdir(notesDir, { recursive: true });
    await writeFile(
      join(notesDir, 'a.md'),
      `---\nid: 00000000-0000-0000-0000-000000000003\ncreated_at: 2026-05-09T00:00:00Z\nupdated_at: 2026-05-10T00:00:00Z\ntitle: t\ntitle_source: ai\nsummary: s\nsummary_source: ai\nstatus: active\ndue_date: 2026-06-01\ndue_date_source: user\ninkling_export_version: 1\n---\n\n# t\n\n> s\n\nbody\n`
    );
    await svc.applySyncFromDir(workDir);
    const note = repo.findById('00000000-0000-0000-0000-000000000003');
    expect(note?.dueDate).toBe('2026-06-01');
    expect(note?.dueDateEditedByUser).toBe(true);
  });
});
