import { adminUserId, makeUser, load } from './setup.mjs';
import test from 'node:test';
import assert from 'node:assert/strict';

const { compareVersions, evaluateUpdateState, mapGraphAppToCatalog } = await load('../services/appVersionService.js');
const { createApplication, listApplications, loadApplication, updateApplication, deleteApplication, recordVersionCheck } = await load('../models/applicationModel.js');
const { runCatalogChecks } = await load('../services/catalogWorker.js');

const owner = adminUserId();
const other = makeUser({ email: 'app-other@test.local' });

// --- pure version logic ---------------------------------------------------

test('compareVersions orders numeric segments correctly', () => {
  assert.equal(compareVersions('1.2.0', '1.10.0'), -1);
  assert.equal(compareVersions('2.0', '1.9.9'), 1);
  assert.equal(compareVersions('1.0.0', '1.0.0'), 0);
  assert.equal(compareVersions('v3.1', '3.1.0'), 0);
});

test('evaluateUpdateState flags newer upstream versions', () => {
  assert.equal(evaluateUpdateState({ currentVersion: '1.0.0', latestKnownVersion: '1.1.0' }).updateAvailable, true);
  assert.equal(evaluateUpdateState({ currentVersion: '2.0.0', latestKnownVersion: '1.1.0' }).updateAvailable, false);
  assert.equal(evaluateUpdateState({ currentVersion: '', latestKnownVersion: '1.0.0' }).updateAvailable, true);
  assert.equal(evaluateUpdateState({ currentVersion: '1.0.0', latestKnownVersion: '' }).updateAvailable, false);
});

test('mapGraphAppToCatalog maps the key fields', () => {
  const mapped = mapGraphAppToCatalog({ id: 'app-1', displayName: 'Contoso VPN', publisher: 'Contoso', displayVersion: '3.1.0' });
  assert.equal(mapped.name, 'Contoso VPN');
  assert.equal(mapped.vendor, 'Contoso');
  assert.equal(mapped.currentVersion, '3.1.0');
  assert.equal(mapped.currentGraphAppId, 'app-1');
});

// --- catalog model --------------------------------------------------------

test('applications are visibility-scoped', () => {
  const app = createApplication({ name: 'Private App', currentVersion: '1.0.0', visibility: 'personal' }, owner);
  assert.ok(listApplications(owner).some((a) => a.id === app.id));
  assert.ok(!listApplications(other).some((a) => a.id === app.id));
  assert.equal(loadApplication(app.id, other), null);
});

test('application read surfaces a computed updateState', () => {
  const app = createApplication({ name: 'Edge', currentVersion: '1.0.0', latestKnownVersion: '1.2.0', visibility: 'shared' }, owner);
  const loaded = loadApplication(app.id, owner);
  assert.equal(loaded.updateState.updateAvailable, true);
  assert.equal(loaded.updateState.latest, '1.2.0');
});

test('recordVersionCheck updates the latest known version', () => {
  const app = createApplication({ name: 'Firefox', currentVersion: '120.0', visibility: 'shared' }, owner);
  const updated = recordVersionCheck(app.id, owner, { latestKnownVersion: '121.0', message: '' });
  assert.equal(updated.latestKnownVersion, '121.0');
  assert.equal(updated.updateState.updateAvailable, true);
  assert.ok(updated.lastCheckedAt);
});

test('auto-check worker only processes opted-in apps and records new versions', async () => {
  const optedIn = createApplication({ name: 'Worker Opt-In', currentVersion: '1.0.0', latestKnownVersion: '1.0.0', versionSource: 'url', versionSourceRef: 'https://example/v', autoCheck: true, visibility: 'shared' }, owner);
  const optedOut = createApplication({ name: 'Worker Opt-Out', currentVersion: '1.0.0', latestKnownVersion: '1.0.0', autoCheck: false, visibility: 'shared' }, owner);

  // Injected fetcher avoids network; returns a newer version.
  const summary = await runCatalogChecks({ fetcher: async () => ({ version: '2.0.0', message: '' }) });

  assert.ok(summary.checked >= 1);
  assert.ok(summary.updated >= 1);
  assert.equal(loadApplication(optedIn.id, owner).latestKnownVersion, '2.0.0');
  assert.equal(loadApplication(optedOut.id, owner).latestKnownVersion, '1.0.0');
});

test('a non-owner cannot update or delete a personal application', () => {
  const app = createApplication({ name: 'Locked App', visibility: 'personal' }, owner);
  assert.equal(updateApplication(app.id, { name: 'Hijacked' }, other), null);
  deleteApplication(app.id, other);
  assert.ok(loadApplication(app.id, owner), 'should survive foreign delete');
});
