/**
 * Regression tests – Phase 9 defect: FitService.preview() must reject
 * archived or inactive presets at apply/preview time. Historical reads
 * (GET /fit-presets/:id) remain valid and return the preset regardless.
 *
 * These are unit tests (no DB) using the validated logic extracted from
 * fit.service.ts preview() method.
 */
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { BadRequestException, NotFoundException } from '@nestjs/common';
import { FitAdjustmentType, FitPresetApplicability } from '@prisma/client';

// Replicate the validation logic from fit.service.ts preview() so this test
// acts as a canary. Also directly tests FitService by constructing one with
// a mock prisma.
import { FitService } from './fit.service';

// ---- Helpers ----

function makePreset(overrides: {
  isActive?: boolean;
  archivedAt?: Date | null;
}) {
  return {
    id: 'preset_1',
    organizationId: 'org_1',
    code: 'SLIM',
    name: 'Slim Fit',
    applicability: FitPresetApplicability.BOTH,
    isActive: overrides.isActive ?? true,
    archivedAt: overrides.archivedAt ?? null,
    sortOrder: 0,
    createdAt: new Date(),
    updatedAt: new Date(),
    rules: [
      {
        id: 'rule_1',
        organizationId: 'org_1',
        fitPresetId: 'preset_1',
        fieldKey: 'kCh',
        adjustmentType: FitAdjustmentType.INCHES,
        adjustmentValue: { toString: () => '2', toNumber: () => 2 } as never,
        applicability: FitPresetApplicability.KS as FitPresetApplicability,
        isActive: true,
        sortOrder: 0,
        createdAt: new Date(),
        updatedAt: new Date(),
      },
    ],
  };
}

function makeService(preset: ReturnType<typeof makePreset>): FitService {
  const fakePrisma = {
    fitPreset: {
      findFirst: async () => preset,
    },
  };
  return new FitService(fakePrisma as never, null as never, null as never);
}

// ---- Tests ----

test('fit preview: accepts active, non-archived preset', async () => {
  const preset = makePreset({ isActive: true, archivedAt: null });
  const svc = makeService(preset);
  const result = await svc.preview('org_1', {
    type: 'KS',
    fitPresetId: 'preset_1',
    actualFields: { kCh: '40' },
  });
  assert.equal(result.fitPresetId, 'preset_1');
  assert.equal(result.finalSnapshotJson.fields['kCh'], '42'); // +2 inches
});

test('fit preview: rejects archived preset', async () => {
  const preset = makePreset({ isActive: false, archivedAt: new Date('2024-01-01') });
  const svc = makeService(preset);
  await assert.rejects(
    () => svc.preview('org_1', { type: 'KS', fitPresetId: 'preset_1', actualFields: { kCh: '40' } }),
    (err: unknown) => {
      assert.ok(err instanceof BadRequestException, 'Expected BadRequestException');
      assert.match((err as BadRequestException).message, /archived/i);
      return true;
    },
  );
});

test('fit preview: rejects inactive preset even when not archived', async () => {
  const preset = makePreset({ isActive: false, archivedAt: null });
  const svc = makeService(preset);
  await assert.rejects(
    () => svc.preview('org_1', { type: 'KS', fitPresetId: 'preset_1', actualFields: { kCh: '40' } }),
    (err: unknown) => {
      assert.ok(err instanceof BadRequestException, 'Expected BadRequestException');
      assert.match((err as BadRequestException).message, /inactive/i);
      return true;
    },
  );
});

test('fit preview: no preset → returns final = actual (no error)', async () => {
  // When no fitPresetId, preview is a no-op pass-through
  const fakePrisma = { fitPreset: { findFirst: async () => null } };
  const svc = new FitService(fakePrisma as never, null as never, null as never);
  const result = await svc.preview('org_1', {
    type: 'KS',
    fitPresetId: null,
    actualFields: { kCh: '40', kSh: '18' },
  });
  assert.deepEqual(result.finalSnapshotJson.fields, { kCh: '40', kSh: '18' });
  assert.equal(result.fitPresetId, null);
});

test('fit get: historical read works on archived preset (no rejection)', async () => {
  // GET /fit-presets/:id is used for audit and history; it should NOT reject
  // archived presets. The get() method just returns the preset as-is.
  const archivedPreset = makePreset({ isActive: false, archivedAt: new Date('2024-01-01') });
  const fakePrisma = {
    fitPreset: {
      findFirst: async () => archivedPreset,
    },
  };
  const svc = new FitService(fakePrisma as never, null as never, null as never);
  const result = await svc.get('org_1', 'preset_1');
  assert.equal(result.id, 'preset_1');
  assert.ok(result.archivedAt instanceof Date, 'archivedAt should be present');
});

test('fit get: NotFoundException when preset not found (org scope preserved)', async () => {
  const fakePrisma = { fitPreset: { findFirst: async () => null } };
  const svc = new FitService(fakePrisma as never, null as never, null as never);
  await assert.rejects(
    () => svc.get('org_1', 'missing_id'),
    NotFoundException,
  );
});

test('fit list hides legacy free-text presets from booking and flags them for admin remediation', async () => {
  const validPreset = makePreset({ isActive: true, archivedAt: null });
  const legacyPreset = makePreset({ isActive: true, archivedAt: null });
  legacyPreset.id = 'legacy_preset';
  legacyPreset.rules[0]!.fieldKey = 'Chest';
  legacyPreset.rules[0]!.applicability = FitPresetApplicability.BOTH;
  const fakePrisma = {
    fitPreset: {
      findMany: async () => [validPreset, legacyPreset],
    },
  };
  const svc = new FitService(fakePrisma as never, null as never, null as never);

  const bookingList = await svc.list('org_1', { includeArchived: false });
  assert.deepEqual(bookingList.map((preset) => preset.id), ['preset_1']);
  assert.equal(bookingList[0]!.requiresRemediation, false);

  const adminList = await svc.list('org_1', { includeArchived: true });
  const legacyResult = adminList.find((preset) => preset.id === 'legacy_preset');
  assert.equal(legacyResult?.requiresRemediation, true);
  assert.match(legacyResult?.remediationMessage ?? '', /replaced or removed/i);
  assert.doesNotMatch(legacyResult?.remediationMessage ?? '', /Chest/);
});

test('fit preview rejects a stale legacy preset with an actionable message that does not expose its key', async () => {
  const legacyPreset = makePreset({ isActive: true, archivedAt: null });
  legacyPreset.rules[0]!.fieldKey = 'Chest';
  legacyPreset.rules[0]!.applicability = FitPresetApplicability.BOTH;
  const svc = makeService(legacyPreset);

  await assert.rejects(
    () => svc.preview('org_1', {
      type: 'KS',
      fitPresetId: 'preset_1',
      actualFields: { kCh: '40' },
    }),
    (error: unknown) => {
      assert.ok(error instanceof BadRequestException);
      assert.match(error.message, /replaced or removed/i);
      assert.doesNotMatch(error.message, /Chest/);
      return true;
    },
  );
});

test('fit restore refuses legacy rules before activating the preset', async () => {
  const legacyPreset = makePreset({ isActive: false, archivedAt: new Date('2024-01-01') });
  legacyPreset.rules[0]!.fieldKey = 'Chest';
  legacyPreset.rules[0]!.applicability = FitPresetApplicability.BOTH;
  let updateCalled = false;
  const fakePrisma = {
    fitPreset: {
      findFirst: async () => legacyPreset,
      update: async () => {
        updateCalled = true;
        return legacyPreset;
      },
    },
  };
  const svc = new FitService(fakePrisma as never, null as never, null as never);

  await assert.rejects(
    () => svc.restore('org_1', 'preset_1', 'user_1'),
    /replaced or removed/i,
  );
  assert.equal(updateCalled, false);
});
