import { describe, test, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import {
  mkdtempSync, rmSync, mkdirSync, writeFileSync, readFileSync, existsSync, statSync,
} from 'node:fs';
import { join, dirname } from 'node:path';
import { tmpdir } from 'node:os';
import { migrateData } from '../migrate-data.mjs';

function writeDeep(p, content) {
  mkdirSync(dirname(p), { recursive: true });
  writeFileSync(p, content);
}

describe('migrateData', () => {
  let pluginRoot;
  let dataRoot;
  const savedData = process.env.LINKEDIN_STUDIO_DATA;

  afterEach(() => {
    for (const d of [pluginRoot, dataRoot]) {
      if (d && existsSync(d)) rmSync(d, { recursive: true, force: true });
    }
    pluginRoot = undefined;
    dataRoot = undefined;
    if (savedData === undefined) delete process.env.LINKEDIN_STUDIO_DATA;
    else process.env.LINKEDIN_STUDIO_DATA = savedData;
  });

  function setup({ withRuntime = false, withScaffolds = false } = {}) {
    pluginRoot = mkdtempSync(join(tmpdir(), 'lis-plugin-'));
    dataRoot = mkdtempSync(join(tmpdir(), 'lis-data-'));
    process.env.LINKEDIN_STUDIO_DATA = dataRoot; // never the real $HOME
    if (withRuntime) {
      writeDeep(join(pluginRoot, 'assets/voice-samples/authentic-voice-samples.local.md'), 'REAL VOICE 227 lines');
      writeDeep(join(pluginRoot, 'assets/drafts/queue.json'), '{"version":1,"queue":[]}');
      writeDeep(join(pluginRoot, 'assets/analytics/exports/content-2026-W22.csv'), 'a,b\n1,2\n');
      writeDeep(join(pluginRoot, 'assets/analytics/posts/2026-05-26.json'), '{"x":1}');
      writeDeep(join(pluginRoot, 'assets/analytics/weekly-reports/2026-W22.json'), '{"w":22}');
    }
    if (withScaffolds) {
      writeDeep(join(pluginRoot, 'assets/examples/high-engagement-posts.md'), 'MY REAL POST');
      writeDeep(join(pluginRoot, 'assets/audience-insights/demographics.md'), 'demo');
      writeDeep(join(pluginRoot, 'assets/audience-insights/engagement-patterns.md'), 'patterns');
      writeDeep(join(pluginRoot, 'assets/templates/my-post-templates.md'), 'my templates');
    }
  }

  test('(a) gitignored runtime files MOVED: source gone, dest byte-equal, voice drops .local', () => {
    setup({ withRuntime: true });
    migrateData({ pluginRoot });

    assert.ok(!existsSync(join(pluginRoot, 'assets/voice-samples/authentic-voice-samples.local.md')), 'voice source gone');
    assert.ok(!existsSync(join(pluginRoot, 'assets/drafts/queue.json')), 'queue source gone');
    assert.ok(!existsSync(join(pluginRoot, 'assets/analytics/exports/content-2026-W22.csv')), 'export source gone');

    assert.equal(readFileSync(join(dataRoot, 'voice-samples/authentic-voice-samples.md'), 'utf-8'), 'REAL VOICE 227 lines');
    assert.equal(readFileSync(join(dataRoot, 'drafts/queue.json'), 'utf-8'), '{"version":1,"queue":[]}');
    assert.ok(existsSync(join(dataRoot, 'analytics/exports/content-2026-W22.csv')));
    assert.ok(existsSync(join(dataRoot, 'analytics/posts/2026-05-26.json')));
    assert.ok(existsSync(join(dataRoot, 'analytics/weekly-reports/2026-W22.json')));
    assert.ok(existsSync(join(dataRoot, '.migrated')), 'marker written');
  });

  test('(b) tracked scaffolds COPIED: external instance written, tracked source preserved', () => {
    setup({ withScaffolds: true });
    migrateData({ pluginRoot });

    assert.ok(existsSync(join(pluginRoot, 'assets/examples/high-engagement-posts.md')), 'scaffold source preserved');
    assert.ok(existsSync(join(pluginRoot, 'assets/templates/my-post-templates.md')), 'scaffold source preserved');

    assert.equal(readFileSync(join(dataRoot, 'examples/high-engagement-posts.md'), 'utf-8'), 'MY REAL POST');
    assert.equal(readFileSync(join(dataRoot, 'audience-insights/demographics.md'), 'utf-8'), 'demo');
    assert.equal(readFileSync(join(dataRoot, 'audience-insights/engagement-patterns.md'), 'utf-8'), 'patterns');
    assert.equal(readFileSync(join(dataRoot, 'templates/my-post-templates.md'), 'utf-8'), 'my templates');
  });

  test('(c) re-run is an idempotent no-op (.migrated short-circuits, no throw, dest unchanged)', () => {
    setup({ withRuntime: true, withScaffolds: true });
    migrateData({ pluginRoot });
    const voiceDest = join(dataRoot, 'voice-samples/authentic-voice-samples.md');
    const before = readFileSync(voiceDest, 'utf-8');
    const mtimeBefore = statSync(voiceDest).mtimeMs;

    const r2 = migrateData({ pluginRoot });
    assert.equal(r2.status, 'already-migrated');
    assert.equal(readFileSync(voiceDest, 'utf-8'), before);
    assert.equal(statSync(voiceDest).mtimeMs, mtimeBefore);
  });

  test('(d) empty fixture (no user data) completes without error', () => {
    setup({});
    const r = migrateData({ pluginRoot });
    assert.ok(r);
    assert.ok(existsSync(join(dataRoot, '.migrated')));
  });

  test('(e) collision: existing external file kept, in-plugin source NOT clobbered', () => {
    setup({ withRuntime: true });
    writeDeep(join(dataRoot, 'voice-samples/authentic-voice-samples.md'), 'EXTERNAL CANONICAL');
    migrateData({ pluginRoot });

    assert.equal(readFileSync(join(dataRoot, 'voice-samples/authentic-voice-samples.md'), 'utf-8'), 'EXTERNAL CANONICAL');
    assert.equal(readFileSync(join(pluginRoot, 'assets/voice-samples/authentic-voice-samples.local.md'), 'utf-8'), 'REAL VOICE 227 lines');
  });
});
