const { spawn } = require('child_process');
const crypto = require('crypto');
const fs = require('fs');
const http = require('http');
const os = require('os');
const path = require('path');

const port = 3999;
const baseUrl = `http://127.0.0.1:${port}`;
// Run against an isolated, freshly-seeded database so the test is deterministic
// and never reads or mutates the developer's working data.
const dbPath = path.join(os.tmpdir(), `deprecore-smoke-${Date.now()}.sqlite`);
const child = spawn(process.execPath, ['server/index.js'], {
  cwd: process.cwd(),
  env: { ...process.env, PORT: String(port), DEPRECORE_DB_PATH: dbPath },
  stdio: ['ignore', 'pipe', 'pipe']
});

function wait(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function request(path, options = {}) {
  const response = await fetch(`${baseUrl}${path}`, {
    ...options,
    headers: {
      'Content-Type': 'application/json',
      ...(options.headers || {})
    }
  });
  const body = await response.json().catch(() => ({}));
  if (!response.ok) {
    throw new Error(`${path} failed: ${response.status} ${JSON.stringify(body)}`);
  }
  return body;
}

async function waitForServer() {
  for (let attempt = 0; attempt < 60; attempt += 1) {
    try {
      await request('/api/health');
      return;
    } catch {
      await wait(250);
    }
  }
  throw new Error('Server did not start');
}

const pngDataUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYPhfDwAChwGA60e6kgAAAABJRU5ErkJggg==';

async function main() {
  await waitForServer();
  const login = await request('/api/auth/login', {
    method: 'POST',
    body: JSON.stringify({
      email: 'admin@deprecore.local',
      password: process.env.DEPRECORE_ADMIN_PASSWORD || 'ChangeMe123!'
    })
  });

  const auth = { Authorization: `Bearer ${login.token}` };
  if (!Array.isArray(login.capabilities) || !login.capabilities.includes('*')) {
    throw new Error('Login did not return admin capabilities');
  }
  const profile = await request('/api/profile', { headers: auth });
  if (profile.preferences.theme !== 'depreCoreDark') {
    throw new Error(`Expected default dark theme, found ${profile.preferences.theme}`);
  }
  if (!Array.isArray(profile.capabilities) || !profile.capabilities.includes('*')) {
    throw new Error('Profile did not return capabilities');
  }
  const updatedProfile = await request('/api/profile', {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({ preferences: { theme: 'depreCoreDark' } })
  });
  if (updatedProfile.preferences.theme !== 'depreCoreDark') {
    throw new Error('Profile theme preference did not persist');
  }

  const taxRules = await request('/api/tax-rules', { headers: auth });
  const activeRule = taxRules.ruleSets.find((rule) => rule.active) || taxRules.ruleSets[0];
  const methodCount = activeRule?.rules_json?.methods?.length || 0;
  if (methodCount !== 74) {
    throw new Error(`Expected 74 depreciation methods, found ${methodCount}`);
  }

  const suffix = Date.now().toString().slice(-6);
  const accessControl = await request('/api/access-control', { headers: auth });
  if (!accessControl.roles.some((r) => r.key === 'admin') || !accessControl.capabilities.some((c) => c.key === 'admin.users')) {
    throw new Error('Access control did not return roles + capability catalog');
  }
  // Built-in PM roles exist with the expected split.
  const pmAdmin = accessControl.roles.find((r) => r.key === 'pm_admin');
  const pmUser = accessControl.roles.find((r) => r.key === 'pm_user');
  if (!pmAdmin || !pmAdmin.capabilities.includes('pm.manage')) throw new Error('PM Admin role missing or lacks pm.manage');
  if (!pmUser || pmUser.capabilities.includes('pm.manage') || !pmUser.capabilities.includes('pm.complete')) {
    throw new Error('PM User role should complete PM but not manage plans');
  }

  // Custom role: create, assign to a user, enforce its capabilities, then guard deletion.
  const customRole = await request('/api/roles', {
    method: 'POST', headers: auth,
    body: JSON.stringify({ name: `Inspector ${suffix}`, description: 'Read + PM completion', capabilities: ['assets.view', 'pm.view', 'pm.complete'] })
  });
  const customRoleKey = customRole.role.key;
  if (!customRoleKey || customRole.role.capabilities.includes('assets.delete')) throw new Error('Custom role was not created with sanitized capabilities');
  const roleUser = await request('/api/users', {
    method: 'POST', headers: auth,
    body: JSON.stringify({ name: `Inspector User ${suffix}`, email: `inspector-${suffix}@example.com`, password: 'ChangeMe123!', role: customRoleKey, status: 'active' })
  });
  if (roleUser.user.role !== customRoleKey) throw new Error('User was not assigned the custom role');
  // Sign in as the custom-role user and verify enforcement.
  const roleLogin = await request('/api/auth/login', {
    method: 'POST', body: JSON.stringify({ email: `inspector-${suffix}@example.com`, password: 'ChangeMe123!' })
  });
  if (roleLogin.capabilities.includes('*') || !roleLogin.capabilities.includes('pm.complete')) throw new Error('Custom-role login capabilities incorrect');
  const roleAuth = { Authorization: `Bearer ${roleLogin.token}` };
  // Allowed: viewing assets. Denied: creating an asset (needs assets.edit) and managing settings.
  await request('/api/assets', { headers: roleAuth });
  const deniedCreate = await fetch(`${baseUrl}/api/assets`, {
    method: 'POST', headers: { ...roleAuth, 'Content-Type': 'application/json' }, body: JSON.stringify({ description: 'nope', category: 'Computer Equipment' })
  });
  if (deniedCreate.status !== 403) throw new Error('Custom role without assets.edit should be denied asset creation');
  const deniedSettings = await fetch(`${baseUrl}/api/settings`, { headers: roleAuth });
  if (deniedSettings.status !== 403) throw new Error('Custom role without admin.settings should be denied settings');
  // Editing the role grants a new capability that immediately takes effect on next login.
  await request(`/api/roles/${customRoleKey}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ capabilities: ['assets.view', 'assets.edit', 'pm.view', 'pm.complete'] })
  });
  const reLogin = await request('/api/auth/login', { method: 'POST', body: JSON.stringify({ email: `inspector-${suffix}@example.com`, password: 'ChangeMe123!' }) });
  const reAuth = { Authorization: `Bearer ${reLogin.token}` };
  const nowAllowed = await fetch(`${baseUrl}/api/assets`, {
    method: 'POST', headers: { ...reAuth, 'Content-Type': 'application/json' }, body: JSON.stringify({ description: `role asset ${suffix}`, category: 'Computer Equipment' })
  });
  if (nowAllowed.status !== 201) throw new Error('Role capability change did not take effect');
  // Deleting a role in use is blocked; built-in roles cannot be deleted.
  const blockedRoleDelete = await fetch(`${baseUrl}/api/roles/${customRoleKey}`, { method: 'DELETE', headers: auth });
  if (blockedRoleDelete.status !== 400) throw new Error('Deleting a role in use should be blocked');
  const blockedSystemDelete = await fetch(`${baseUrl}/api/roles/admin`, { method: 'DELETE', headers: auth });
  if (blockedSystemDelete.status !== 400) throw new Error('Built-in roles must not be deletable');

  const teamResult = await request('/api/teams', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      name: `Smoke Team ${suffix}`,
      description: 'Smoke test RBAC team'
    })
  });
  const renamedTeam = await request(`/api/teams/${teamResult.team.id}`, {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({ name: `Smoke Team ${suffix} (edited)`, description: 'edited' })
  });
  if (renamedTeam.team.name !== `Smoke Team ${suffix} (edited)`) throw new Error('Team update did not persist');

  const tmpTeam = await request('/api/teams', { method: 'POST', headers: auth, body: JSON.stringify({ name: `Tmp Team ${suffix}` }) });
  const okDelete = await fetch(`${baseUrl}/api/teams/${tmpTeam.team.id}`, { method: 'DELETE', headers: auth });
  if (okDelete.status !== 204) throw new Error('Deleting an empty team failed');

  const userResult = await request('/api/users', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      team_id: teamResult.team.id,
      name: 'Smoke Test Viewer',
      email: `viewer-${suffix}@example.com`,
      password: 'ChangeMe123!',
      role: 'viewer',
      status: 'active'
    })
  });
  const updatedUser = await request(`/api/users/${userResult.user.id}`, {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({
      team_id: teamResult.team.id,
      name: 'Smoke Test Operator',
      email: `operator-${suffix}@example.com`,
      role: 'operations',
      status: 'active'
    })
  });
  if (updatedUser.user.role !== 'operations' || updatedUser.user.team_id !== teamResult.team.id) {
    throw new Error('User role or team update did not persist');
  }

  const blockedDelete = await fetch(`${baseUrl}/api/teams/${teamResult.team.id}`, { method: 'DELETE', headers: auth });
  if (blockedDelete.status !== 400) throw new Error('Deleting a team with members should be blocked');

  await request('/api/templates', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      name: `Smoke template ${suffix}`,
      description: 'Template custom field smoke test',
      defaults: { category: 'Computer Equipment', useful_life_months: 60, depreciation_method: 'macrs_gds_200db_5' },
      custom_fields: [
        { key: 'employee_id', label: 'Employee ID', type: 'text', required: true },
        { key: 'calibration_due', label: 'Calibration due', type: 'date', required: false }
      ]
    })
  });
  const templates = await request('/api/templates', { headers: auth });
  const smokeTemplate = templates.templates.find((template) => template.name === `Smoke template ${suffix}`);
  if (!smokeTemplate || smokeTemplate.custom_fields.length !== 2) {
    throw new Error('Template custom fields were not saved or returned correctly');
  }
  if (smokeTemplate.asset_count !== 0) throw new Error('New template should report zero assigned assets');

  // Update the template.
  const updatedTemplate = await request(`/api/templates/${smokeTemplate.id}`, {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({
      name: `Smoke template ${suffix} (edited)`,
      description: 'edited',
      defaults: smokeTemplate.defaults,
      custom_fields: [{ key: 'employee_id', label: 'Employee ID', type: 'text', required: true }]
    })
  });
  if (updatedTemplate.template.name !== `Smoke template ${suffix} (edited)`) throw new Error('Template update did not persist');

  // Delete a disposable template and confirm it reports unlinked-asset impact.
  const disposable = await request('/api/templates', {
    method: 'POST', headers: auth,
    body: JSON.stringify({ name: `Disposable template ${suffix}`, defaults: {}, custom_fields: [] })
  });
  const tmplDelete = await request(`/api/templates/${disposable.template.id}`, { method: 'DELETE', headers: auth });
  if (!tmplDelete.deleted || typeof tmplDelete.unlinked_assets !== 'number') throw new Error('Template delete did not report unlink impact');
  const afterDelete = await request('/api/templates', { headers: auth });
  if (afterDelete.templates.some((t) => t.id === disposable.template.id)) throw new Error('Deleted template still present');

  // Asset categories: create, list, rename (cascades to assets), delete.
  const newCategory = await request('/api/asset-categories', {
    method: 'POST', headers: auth, body: JSON.stringify({ name: `Smoke Category ${suffix}`, code: 'SMK' })
  });
  if (!newCategory.category?.id || newCategory.category.asset_count !== 0) throw new Error('Asset category was not created');
  const dupCategory = await fetch(`${baseUrl}/api/asset-categories`, {
    method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' }, body: JSON.stringify({ name: `Smoke Category ${suffix}` })
  });
  if (dupCategory.status !== 400) throw new Error('Duplicate category should be rejected');
  // An asset using the category, to prove rename cascades.
  const catAsset = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({ asset_id: `CAT-${suffix}`, description: 'Category test', category: `Smoke Category ${suffix}`, acquisition_cost: 100 })
  });
  const renamedCategory = await request(`/api/asset-categories/${newCategory.category.id}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ name: `Smoke Category ${suffix} (renamed)` })
  });
  if (renamedCategory.renamed_assets < 1) throw new Error('Category rename did not cascade to assets');
  const renamedAsset = await request(`/api/assets/${catAsset.asset.id}`, { headers: auth });
  if (renamedAsset.asset.category !== `Smoke Category ${suffix} (renamed)`) throw new Error('Asset category was not updated by rename');
  const categoryList = await request('/api/asset-categories', { headers: auth });
  const listedCategory = categoryList.categories.find((c) => c.id === newCategory.category.id);
  if (!listedCategory || listedCategory.asset_count !== 1) throw new Error('Category usage count was not reported');
  const catDelete = await request(`/api/asset-categories/${newCategory.category.id}`, { method: 'DELETE', headers: auth });
  if (!catDelete.deleted || catDelete.asset_count !== 1) throw new Error('Category delete did not report usage impact');

  // IRS asset-class reference catalog is available for pre-populating class numbers.
  const irs = await request('/api/asset-classes', { headers: auth });
  if (!Array.isArray(irs.assetClasses) || irs.assetClasses.length < 280) throw new Error('IRS asset-class catalog was not returned');
  const computers = irs.assetClasses.find((c) => c.description === 'Computers' && c.table === 'common');
  if (!computers || computers.class_number !== '00.12' || computers.gds !== 5) throw new Error('IRS common catalog entry (Computers) was incorrect');
  const semiconductor = irs.assetClasses.find((c) => c.description === 'Semiconductor Manufacturing Equipment' && c.table === 'industry');
  if (!semiconductor || semiconductor.class_number !== '36.1') throw new Error('IRS manufacturing-industry catalog entry was missing');
  const construction = irs.assetClasses.find((c) => c.description === 'Construction' && c.table === 'business');
  if (!construction || construction.class_number !== '15.0') throw new Error('IRS business-activity catalog entry was missing');
  if (!irs.assetClasses[0].id) throw new Error('Asset classes should be managed records with ids');

  // Asset classes are now managed: create, edit, delete.
  const newClass = await request('/api/asset-classes', {
    method: 'POST', headers: auth,
    body: JSON.stringify({ description: `Smoke Class ${suffix}`, class_number: `99.${suffix.slice(-2)}`, gds: 7, ads: 12, source: 'custom' })
  });
  if (!newClass.assetClass?.id || newClass.assetClass.source !== 'custom') throw new Error('Asset class was not created');
  const editedClass = await request(`/api/asset-classes/${newClass.assetClass.id}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ gds: 5 })
  });
  if (editedClass.assetClass.gds !== 5) throw new Error('Asset class update did not persist');
  const classDelete = await fetch(`${baseUrl}/api/asset-classes/${newClass.assetClass.id}`, { method: 'DELETE', headers: auth });
  if (classDelete.status !== 204) throw new Error('Asset class delete failed');

  // Asset class number: shared by every asset in the category, and cascades on change.
  const classCat = await request('/api/asset-categories', {
    method: 'POST', headers: auth, body: JSON.stringify({ name: `Class Cat ${suffix}`, asset_class_number: 'AC-100' })
  });
  if (classCat.category.asset_class_number !== 'AC-100') throw new Error('Category asset class number was not stored');
  const classAsset = await request('/api/assets', {
    method: 'POST', headers: auth, body: JSON.stringify({ asset_id: `CLS-${suffix}`, description: 'Class test', category: `Class Cat ${suffix}`, acquisition_cost: 50 })
  });
  if (classAsset.asset.asset_class_number !== 'AC-100') throw new Error('Asset did not inherit the category class number');
  // Changing the category's class number cascades to its assets.
  await request(`/api/asset-categories/${classCat.category.id}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ asset_class_number: 'AC-200' })
  });
  const reclassed = await request(`/api/assets/${classAsset.asset.id}`, { headers: auth });
  if (reclassed.asset.asset_class_number !== 'AC-200') throw new Error('Class number change did not cascade to assets');

  // Asset class collision: 00.12 is BOTH "Computers" (common) and "Casinos" (business). Linking a
  // category by the class's unique id must resolve to the exact class, not the first number match.
  const computersClass = irs.assetClasses.find((c) => c.description === 'Computers' && c.class_number === '00.12');
  const casinoClass = irs.assetClasses.find((c) => c.description === 'Casinos' && c.class_number === '00.12');
  if (!computersClass || !casinoClass || computersClass.id === casinoClass.id) {
    throw new Error('Expected distinct Computers/Casinos classes sharing class number 00.12');
  }
  const computerCat = await request('/api/asset-categories', {
    method: 'POST', headers: auth, body: JSON.stringify({ name: `Computers ${suffix}`, asset_class_id: computersClass.id })
  });
  if (computerCat.category.asset_class_id !== computersClass.id) throw new Error('Category did not store the unique asset_class_id');
  if (computerCat.category.asset_class_number !== '00.12') throw new Error('Category did not derive the class number from the linked class');
  if (!/Computers/.test(computerCat.category.asset_class_label) || /Casino/.test(computerCat.category.asset_class_label)) {
    throw new Error(`Category resolved to the wrong class: ${computerCat.category.asset_class_label}`);
  }
  // Re-point the same category to the casino class by id; the label must follow the id, not the number.
  const repointed = await request(`/api/asset-categories/${computerCat.category.id}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ asset_class_id: casinoClass.id })
  });
  if (!/Casino/.test(repointed.category.asset_class_label) || repointed.category.asset_class_id !== casinoClass.id) {
    throw new Error('Re-pointing category by id did not resolve to the casino class');
  }
  // A manual number with no id stays unlinked (asset_class_id null) but keeps the number.
  const manualClassCat = await request(`/api/asset-categories/${computerCat.category.id}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ asset_class_id: null, asset_class_number: '99.99' })
  });
  if (manualClassCat.category.asset_class_id !== null || manualClassCat.category.asset_class_number !== '99.99') {
    throw new Error('Manual class number override did not clear the id link');
  }

  // Asset ID templates: pattern, preview, assign to a category, auto-increment on asset creation.
  const idTemplate = await request('/api/asset-id-templates', {
    method: 'POST', headers: auth, body: JSON.stringify({ name: `Tag ${suffix}`, pattern: 'T-A#####', next_number: 5 })
  });
  if (idTemplate.template.preview !== 'T-A00005') throw new Error('ID template preview was not formatted correctly');
  const badPattern = await fetch(`${baseUrl}/api/asset-id-templates`, {
    method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' }, body: JSON.stringify({ name: 'bad', pattern: 'NOHASH' })
  });
  if (badPattern.status !== 400) throw new Error('Pattern without # should be rejected');
  // A category that uses the template.
  const tagCategory = await request('/api/asset-categories', {
    method: 'POST', headers: auth, body: JSON.stringify({ name: `Tagged Cat ${suffix}`, id_template_id: idTemplate.template.id })
  });
  if (tagCategory.category.id_template_id !== idTemplate.template.id) throw new Error('Category did not store its ID template assignment');
  // New asset with no explicit asset_id should get the templated tag and advance the counter.
  const auto1 = await request('/api/assets', {
    method: 'POST', headers: auth, body: JSON.stringify({ description: 'Auto tag 1', category: `Tagged Cat ${suffix}`, acquisition_cost: 10 })
  });
  if (auto1.asset.asset_id !== 'T-A00005') throw new Error(`Expected auto tag T-A00005, got ${auto1.asset.asset_id}`);
  const auto2 = await request('/api/assets', {
    method: 'POST', headers: auth, body: JSON.stringify({ description: 'Auto tag 2', category: `Tagged Cat ${suffix}`, acquisition_cost: 10 })
  });
  if (auto2.asset.asset_id !== 'T-A00006') throw new Error(`Expected auto tag T-A00006, got ${auto2.asset.asset_id}`);
  // An explicit asset_id still wins over the template.
  const explicit = await request('/api/assets', {
    method: 'POST', headers: auth, body: JSON.stringify({ asset_id: `EXP-${suffix}`, description: 'Explicit', category: `Tagged Cat ${suffix}`, acquisition_cost: 10 })
  });
  if (explicit.asset.asset_id !== `EXP-${suffix}`) throw new Error('Explicit asset_id should override the template');
  // Deleting the template unassigns it from the category.
  const idTplDelete = await request(`/api/asset-id-templates/${idTemplate.template.id}`, { method: 'DELETE', headers: auth });
  if (!idTplDelete.deleted || idTplDelete.category_count !== 1) throw new Error('ID template delete did not unassign its category');

  // Asset departments: create, rename (cascades to assets), usage count, delete.
  const newDept = await request('/api/asset-departments', {
    method: 'POST', headers: auth, body: JSON.stringify({ name: `Smoke Dept ${suffix}`, code: 'SMKD' })
  });
  if (!newDept.department?.id) throw new Error('Asset department was not created');
  await request(`/api/assets/${catAsset.asset.id}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ department: `Smoke Dept ${suffix}` })
  });
  const renamedDept = await request(`/api/asset-departments/${newDept.department.id}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ name: `Smoke Dept ${suffix} (renamed)` })
  });
  if (renamedDept.renamed_assets < 1) throw new Error('Department rename did not cascade to assets');
  const deptAsset = await request(`/api/assets/${catAsset.asset.id}`, { headers: auth });
  if (deptAsset.asset.department !== `Smoke Dept ${suffix} (renamed)`) throw new Error('Asset department was not updated by rename');
  const deptList = await request('/api/asset-departments', { headers: auth });
  if (!deptList.departments.some((d) => d.id === newDept.department.id && d.asset_count === 1)) throw new Error('Department usage count was not reported');
  const deptDelete = await request(`/api/asset-departments/${newDept.department.id}`, { method: 'DELETE', headers: auth });
  if (!deptDelete.deleted || deptDelete.asset_count !== 1) throw new Error('Department delete did not report usage impact');

  const assetResult = await request('/api/assets', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      asset_id: `T-${suffix}`,
      description: 'Smoke test workstation',
      category: 'Computer Equipment',
      acquisition_cost: 2400,
      acquired_date: '2026-01-05',
      in_service_date: '2026-01-05',
      useful_life_months: 60,
      depreciation_method: 'macrs_gds_200db_5',
      books: [
        { book_type: 'GAAP', active: true, depreciation_method: 'straight_line', convention: 'half_year', useful_life_months: 60, cost: 2400 },
        { book_type: 'FEDERAL', active: true, depreciation_method: 'macrs_gds_200db_5', convention: 'half_year', useful_life_months: 60, section_179_amount: 500, bonus_percent: 0, cost: 2400 }
      ]
    })
  });

  const assetId = assetResult.asset.id;

  // Per-book persistence: GAAP and FEDERAL must keep distinct methods.
  const hydrated = await request(`/api/assets/${assetId}`, { headers: auth });
  const gaapBook = hydrated.asset.books.find((book) => book.book_type === 'GAAP');
  const federalBook = hydrated.asset.books.find((book) => book.book_type === 'FEDERAL');
  if (gaapBook?.depreciation_method !== 'straight_line' || federalBook?.depreciation_method !== 'macrs_gds_200db_5') {
    throw new Error('Per-book depreciation methods did not persist independently');
  }
  if (Number(federalBook.section_179_amount) !== 500) {
    throw new Error('Per-book Section 179 amount did not persist');
  }

  // File cabinet (multipart upload bypasses the JSON request helper)
  const uploadForm = new FormData();
  uploadForm.append('file', new Blob(['invoice contents'], { type: 'text/plain' }), 'invoice.txt');
  const uploadResponse = await fetch(`${baseUrl}/api/assets/${assetId}/files`, { method: 'POST', headers: auth, body: uploadForm });
  const fileResult = await uploadResponse.json();
  if (!uploadResponse.ok || !fileResult.file?.id) throw new Error('Asset file upload did not return a file');
  const fileDownload = await fetch(`${baseUrl}/api/assets/${assetId}/files/${fileResult.file.id}`, { headers: auth });
  if (!fileDownload.ok || (await fileDownload.text()) !== 'invoice contents') {
    throw new Error('Asset file download did not return stored contents');
  }

  // Warranty
  const warrantyResult = await request(`/api/assets/${assetId}/warranties`, {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ provider: 'Acme Care', start_date: '2026-01-05', expiration_date: '2029-01-05', coverage_details: 'Parts and labor' })
  });
  if (!warrantyResult.warranty?.id) throw new Error('Warranty was not created');

  // Task lifecycle
  const taskResult = await request(`/api/assets/${assetId}/tasks`, {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ title: 'Calibrate device', type: 'calibration', due_date: '2026-03-01' })
  });
  const completedTask = await request(`/api/assets/${assetId}/tasks/${taskResult.task.id}`, {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({ status: 'complete' })
  });
  if (completedTask.task.status !== 'complete') throw new Error('Task status update did not persist');

  // Notes
  await request(`/api/assets/${assetId}/notes`, {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ body: 'Received and tagged in receiving dock.' })
  });

  // Identification photos (base64 + caption)
  const photoResult = await request(`/api/assets/${assetId}/photos`, {
    method: 'POST', headers: auth,
    body: JSON.stringify({ name: 'front.png', mime: 'image/png', caption: 'Front nameplate', data: pngDataUrl })
  });
  if (!photoResult.photo?.id) throw new Error('Asset photo was not created');
  const recaptioned = await request(`/api/assets/${assetId}/photos/${photoResult.photo.id}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ caption: 'Serial label, left side' })
  });
  if (recaptioned.photo.caption !== 'Serial label, left side') throw new Error('Asset photo caption did not update');

  const detailed = await request(`/api/assets/${assetId}`, { headers: auth });
  if (!detailed.asset.files.length || !detailed.asset.warranties.length || !detailed.asset.notes_log.length) {
    throw new Error('Asset detail collections (files/warranties/notes) were not returned');
  }
  if (!detailed.asset.photos.length || !detailed.asset.photos[0].data || detailed.asset.photos[0].caption !== 'Serial label, left side') {
    throw new Error('Asset photos (with caption + data) were not returned');
  }

  // Barcode lookup: set a barcode value, then resolve it via the scan endpoint.
  await request(`/api/assets/${assetId}`, {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({ barcode_value: `BC-${suffix}` })
  });
  const scanned = await request(`/api/assets/lookup?code=BC-${suffix}`, { headers: auth });
  if (scanned.asset.id !== assetId) throw new Error('Barcode lookup did not resolve the scanned asset');
  const byAssetId = await request(`/api/assets/lookup?code=${encodeURIComponent(assetResult.asset.asset_id)}`, { headers: auth });
  if (byAssetId.asset.id !== assetId) throw new Error('Asset ID lookup fallback failed');

  // Reporting suite: catalog, several report types, export, and saved reports.
  const reportCatalog = await request('/api/reports/catalog', { headers: auth });
  if (!reportCatalog.reports.length || !reportCatalog.params.book) {
    throw new Error('Report catalog did not return reports and param specs');
  }
  const runReportApi = (type, options) => request('/api/reports/run', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ type, options })
  });
  const acq = await runReportApi('acquisitions', { year: 2026 });
  if (!Array.isArray(acq.report.rows)) throw new Error('Acquisitions report did not return rows');
  const disp = await runReportApi('disposals', { year: 2026 });
  if (!Array.isArray(disp.report.rows)) throw new Error('Disposals report did not return rows');
  const roll = await runReportApi('rollforward', { book: 'GAAP', year: 2026 });
  if (!roll.report.columns.length) throw new Error('Rollforward report did not return columns');
  const f4797 = await runReportApi('form_4797', { year: 2026 });
  if (!Array.isArray(f4797.report.rows)) throw new Error('Form 4797 report did not return rows');
  const custom = await runReportApi('custom', { columns: ['asset_id', 'acquisition_cost'] });
  if (custom.report.columns.length !== 2) throw new Error('Report builder column projection failed');
  for (const pmReport of ['pm_due', 'pm_uncompleted', 'pm_completed', 'alerts_since']) {
    const r = await runReportApi(pmReport, { before_date: '2099-01-01', since_date: '2000-01-01' });
    if (!Array.isArray(r.report.rows)) throw new Error(`${pmReport} report did not return rows`);
  }

  const exportResponse = await fetch(`${baseUrl}/api/reports/export`, {
    method: 'POST',
    headers: { ...auth, 'Content-Type': 'application/json' },
    body: JSON.stringify({ type: 'depreciation_expense', options: { book: 'GAAP', year: 2026 }, format: 'csv' })
  });
  if (!exportResponse.ok || !(await exportResponse.text()).includes('Asset ID')) {
    throw new Error('Report CSV export failed');
  }

  const savedReport = await request('/api/saved-reports', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ name: `Saved ${suffix}`, report_type: 'net_book_value', options: { book: 'GAAP', year: 2026 } })
  });
  const savedList = await request('/api/saved-reports', { headers: auth });
  if (!savedList.reports.some((report) => report.id === savedReport.report.id)) {
    throw new Error('Saved report was not persisted/listed');
  }

  // Tax rule sets: expand (method catalog), create, edit, activate, delete.
  const expanded = await request('/api/tax-rules/expand', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ rules_json: { jurisdiction: 'US-SMOKE', version: '1.0' } })
  });
  if ((expanded.rules_json.methods || []).length !== 74) {
    throw new Error('Rule set expansion did not generate the 74-method catalog');
  }
  const createdRule = await request('/api/tax-rules', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      jurisdiction: `US-SMOKE-${suffix}`,
      name: 'Smoke rule set',
      version: '1.0',
      effective_date: '2026-01-01',
      active: false,
      rules_json: { jurisdiction: `US-SMOKE-${suffix}`, version: '1.0', limits: { section179: { deductionLimit: 1000000 } } }
    })
  });
  const editedRule = await request(`/api/tax-rules/${createdRule.ruleSet.id}`, {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({ name: 'Smoke rule set (edited)', rules_json: { jurisdiction: `US-SMOKE-${suffix}`, version: '1.0', limits: { section179: { deductionLimit: 2000000 } } } })
  });
  if (editedRule.ruleSet.name !== 'Smoke rule set (edited)' || editedRule.ruleSet.rules_json.limits.section179.deductionLimit !== 2000000) {
    throw new Error('Tax rule set edit did not persist');
  }
  const activated = await request(`/api/tax-rules/${createdRule.ruleSet.id}/activate`, { method: 'POST', headers: auth });
  if (!activated.ruleSets.find((set) => set.id === createdRule.ruleSet.id)?.active) {
    throw new Error('Tax rule set activation did not persist');
  }
  const deleteRule = await fetch(`${baseUrl}/api/tax-rules/${createdRule.ruleSet.id}`, { method: 'DELETE', headers: auth });
  if (deleteRule.status !== 204) throw new Error('Tax rule set delete failed');

  // Books registry: list, edit (assign rule set + default method), and GL ledger.
  const booksList = await request('/api/books', { headers: auth });
  if (booksList.books.length < 6 || !Array.isArray(booksList.ruleSets)) {
    throw new Error('Books registry did not return the standard books and rule sets');
  }
  const gaapBookDef = booksList.books.find((book) => book.code === 'GAAP');
  const federalRuleSet = booksList.ruleSets[0];
  const editedBook = await request(`/api/books/${gaapBookDef.id}`, {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({ default_method: 'straight_line', tax_rule_set_id: federalRuleSet.id, enabled: true })
  });
  if (editedBook.book.tax_rule_set_id !== federalRuleSet.id) {
    throw new Error('Book tax-rule assignment did not persist');
  }
  // User-defined book: create, ensure it applies to a new asset, then guard + delete.
  const newBook = await request('/api/books', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ code: `IFRS${suffix}`.slice(0, 12), name: 'IFRS book', book_type: 'financial', enabled: true })
  });
  const newBookCode = newBook.book.code;
  const assetWithUserBook = await request('/api/assets', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ asset_id: `UB-${suffix}`, description: 'User-book asset', category: 'Computer Equipment', acquisition_cost: 1200 })
  });
  const hydratedUserBook = await request(`/api/assets/${assetWithUserBook.asset.id}`, { headers: auth });
  if (!hydratedUserBook.asset.books.some((b) => b.book_type === newBookCode)) {
    throw new Error('Newly created enabled book did not apply to a new asset');
  }
  const blockedBookDelete = await fetch(`${baseUrl}/api/books/${newBook.book.id}`, { method: 'DELETE', headers: auth });
  if (blockedBookDelete.status !== 400) throw new Error('Deleting a book in use should be blocked');
  const spareBook = await request('/api/books', {
    method: 'POST', headers: auth, body: JSON.stringify({ code: `TMP${suffix}`.slice(0, 12), name: 'Temp book' })
  });
  const okBookDelete = await fetch(`${baseUrl}/api/books/${spareBook.book.id}`, { method: 'DELETE', headers: auth });
  if (okBookDelete.status !== 204) throw new Error('Deleting an unused book failed');

  const ledger = await request('/api/books/GAAP/ledger?year=2026', { headers: auth });
  if (!ledger.ledger.accounts.length || typeof ledger.ledger.summary.nbv !== 'number') {
    throw new Error('Book GL ledger did not return accounts and summary');
  }
  const ledgerExport = await fetch(`${baseUrl}/api/books/GAAP/ledger/export`, {
    method: 'POST',
    headers: { ...auth, 'Content-Type': 'application/json' },
    body: JSON.stringify({ year: 2026, format: 'csv' })
  });
  if (!ledgerExport.ok || !(await ledgerExport.text()).includes('GL account')) {
    throw new Error('Book GL ledger export failed');
  }

  // Alerts: a clearly-overdue task should raise a critical alert on scan.
  await request(`/api/assets/${assetId}/tasks`, {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ title: 'Annual inspection', type: 'inspection', due_date: '2020-01-01' })
  });
  const scan = await request('/api/alerts/scan', { method: 'POST', headers: auth });
  const overdue = scan.alerts.find((a) => a.type === 'maintenance_overdue' && a.due_date === '2020-01-01');
  if (!overdue || overdue.severity !== 'critical') throw new Error('Overdue maintenance alert was not raised');
  const acked = await request(`/api/alerts/${overdue.id}/acknowledge`, { method: 'PUT', headers: auth });
  if (acked.alert.status !== 'acknowledged') throw new Error('Alert acknowledge did not persist');
  const dismissed = await request(`/api/alerts/${overdue.id}/dismiss`, { method: 'PUT', headers: auth });
  if (dismissed.alert.status !== 'dismissed') throw new Error('Alert dismiss did not persist');

  // Notification (SMTP) settings: password is write-only and never returned.
  await request('/api/notifications/settings', {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({
      smtp_host: 'smtp.example.com', smtp_port: 587, smtp_secure: false,
      smtp_user: 'mailer@example.com', smtp_password: 'secret', smtp_from: 'DepreCore <no-reply@example.com>',
      alerts_enabled: false, alerts_lead_days: 45, alerts_recipients: 'ops@example.com, finance@example.com'
    })
  });
  const notif = await request('/api/notifications/settings', { headers: auth });
  if (notif.settings.smtp_host !== 'smtp.example.com' || notif.settings.smtp_password_set !== true || notif.settings.smtp_password !== undefined) {
    throw new Error('Notification settings did not persist or leaked the password');
  }
  if (notif.settings.alerts_lead_days !== 45) throw new Error('Alert lead days did not persist');
  const adminSettings = await request('/api/settings', { headers: auth });
  if ('smtp_password' in adminSettings.settings) throw new Error('Admin settings endpoint leaked smtp_password');

  // Preventative maintenance: plan with steps, attach to asset, alert, complete, settings.
  const pmPlan = await request('/api/pm-plans', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      name: `Lift PM ${suffix}`,
      frequency_value: 3,
      frequency_unit: 'months',
      steps: [{ title: 'Tighten lug bolt A3', est_minutes: 15 }, { title: 'Inspect hydraulics', est_minutes: 30 }],
      components: [
        { part_number: 'OIL-10', description: 'Hydraulic oil', supplier: 'Acme', cost: 40 },
        { part_number: 'FLT-2', description: 'Filter', supplier: 'Acme', cost: 12 }
      ],
      photos: [{ name: 'diagram.png', mime: 'image/png', caption: 'Lug bolt locations', data: pngDataUrl }]
    })
  });
  if (pmPlan.plan.steps.length !== 2) throw new Error('PM plan steps were not saved');
  if (pmPlan.plan.estimated_minutes !== 45) throw new Error('Estimated minutes were not totaled from step times');
  if (pmPlan.plan.components.length !== 2 || pmPlan.plan.components_total !== 52) throw new Error('PM components were not saved/totaled');
  if (pmPlan.plan.photos.length !== 1 || !pmPlan.plan.photos[0].data) throw new Error('PM plan guidance photo was not saved');

  // PM categories: create, rename (cascades to plans), usage count, delete — separate from asset categories.
  const pmCat = await request('/api/pm-categories', {
    method: 'POST', headers: auth, body: JSON.stringify({ name: `PM Cat ${suffix}`, code: 'PMC' })
  });
  if (!pmCat.category?.id) throw new Error('PM category was not created');
  await request(`/api/pm-plans/${pmPlan.plan.id}`, { method: 'PUT', headers: auth, body: JSON.stringify({ category: `PM Cat ${suffix}` }) });
  const renamedPmCat = await request(`/api/pm-categories/${pmCat.category.id}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ name: `PM Cat ${suffix} (renamed)` })
  });
  if (renamedPmCat.renamed_plans < 1) throw new Error('PM category rename did not cascade to plans');
  const renamedPlan = await request(`/api/pm-plans/${pmPlan.plan.id}`, { headers: auth });
  if (renamedPlan.plan.category !== `PM Cat ${suffix} (renamed)`) throw new Error('PM plan category was not updated by rename');
  const pmCatList = await request('/api/pm-categories', { headers: auth });
  if (!pmCatList.categories.some((c) => c.id === pmCat.category.id && c.plan_count === 1)) throw new Error('PM category usage count was not reported');
  // PM categories are distinct from asset categories.
  const assetCats = await request('/api/asset-categories', { headers: auth });
  if (assetCats.categories.some((c) => c.name === `PM Cat ${suffix} (renamed)`)) throw new Error('PM category leaked into asset categories');
  const pmCatDelete = await request(`/api/pm-categories/${pmCat.category.id}`, { method: 'DELETE', headers: auth });
  if (!pmCatDelete.deleted || pmCatDelete.plan_count !== 1) throw new Error('PM category delete did not report usage impact');
  // The list view omits photo blobs; the single-plan fetch includes them.
  const planList = await request('/api/pm-plans', { headers: auth });
  const listed = planList.plans.find((p) => p.id === pmPlan.plan.id);
  if (listed.photo_count !== 1 || (listed.photos[0] && listed.photos[0].data)) throw new Error('PM plan list should report photo_count without blobs');
  const singlePlan = await request(`/api/pm-plans/${pmPlan.plan.id}`, { headers: auth });
  if (!singlePlan.plan.photos[0].data) throw new Error('Single PM plan fetch should include photo data');
  // Editing the plan keeps the existing photo (resent by id) and updates its caption.
  const editedPlan = await request(`/api/pm-plans/${pmPlan.plan.id}`, {
    method: 'PUT', headers: auth,
    body: JSON.stringify({ photos: [{ id: pmPlan.plan.photos[0].id, caption: 'Updated caption' }] })
  });
  if (editedPlan.plan.photos.length !== 1 || editedPlan.plan.photos[0].caption !== 'Updated caption' || !editedPlan.plan.photos[0].data) {
    throw new Error('Editing PM plan photo caption (resend by id) did not preserve the blob');
  }
  const planPrint = await request('/api/reports/run', { method: 'POST', headers: auth, body: JSON.stringify({ type: 'pm_plan', options: { pm_plan_id: pmPlan.plan.id } }) });
  if (planPrint.report.rows.length !== 2 || !planPrint.report.meta.note.includes('Frequency')) {
    throw new Error('Printable PM plan report was incomplete');
  }
  const attached = await request(`/api/assets/${assetId}/pm`, {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ plan_id: pmPlan.plan.id, frequency_value: 1, frequency_unit: 'months', start_date: '2020-01-01', next_due_date: '2020-01-01', end_date: '2035-01-01' })
  });
  const pmScan = await request('/api/alerts/scan', { method: 'POST', headers: auth });
  if (!pmScan.alerts.some((a) => a.type === 'pm_overdue' && a.reference_id === attached.pm.id)) {
    throw new Error('Overdue PM alert was not raised');
  }
  // Guidance photos surface on the asset's PM schedule for the completion view.
  const pmGuidance = await request(`/api/assets/${assetId}/pm`, { headers: auth });
  const pmSchedule = pmGuidance.pm.find((p) => p.id === attached.pm.id);
  if (!pmSchedule.plan_photos || !pmSchedule.plan_photos.length || !pmSchedule.plan_photos[0].data) {
    throw new Error('PM plan guidance photos were not exposed on the asset schedule');
  }
  const completed = await request(`/api/assets/${assetId}/pm/${attached.pm.id}/complete`, {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      steps: [{ title: 'Tighten lug bolt A3', done: true }],
      notes_list: ['Topped up fluids', 'Replaced worn filter'],
      ratings: { safety: 5, physical: 4, operating: 5 },
      signature: pngDataUrl,
      photos: [{ name: 'after.png', mime: 'image/png', data: pngDataUrl }]
    })
  });
  if (!completed.pm.next_due_date || completed.pm.next_due_date <= '2020-01-01') {
    throw new Error('Completing PM did not advance the next due date');
  }
  // Signature is required.
  const missingSig = await fetch(`${baseUrl}/api/assets/${assetId}/pm/${attached.pm.id}/complete`, {
    method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' }, body: JSON.stringify({ ratings: { safety: 3 } })
  });
  if (missingSig.status !== 400) throw new Error('PM completion without a signature should be rejected');
  const withPm = await request(`/api/assets/${assetId}`, { headers: auth });
  if (!withPm.asset.pm.length || !withPm.asset.pm[0].completions.length) {
    throw new Error('Asset PM schedule/completions were not returned');
  }
  if (withPm.asset.pm[0].total_cost !== 52) {
    throw new Error('PM completion cost (from components) was not captured');
  }
  const firstCompletion = withPm.asset.pm[0].completions[0];
  if (!firstCompletion.has_signature || firstCompletion.photo_count !== 1 || firstCompletion.ratings.safety !== 5) {
    throw new Error('PM completion metadata (signature/photos/ratings) was not stored');
  }
  const completionList = await request(`/api/pm-completions?asset_id=${assetId}`, { headers: auth });
  if (!completionList.completions.length) throw new Error('PM completions list was empty');
  const completionDetail = await request(`/api/pm-completions/${completionList.completions[0].id}`, { headers: auth });
  if (!completionDetail.completion.signature || !completionDetail.completion.photos.length || completionDetail.completion.notes.length !== 2) {
    throw new Error('PM completion form detail was incomplete');
  }

  // PM book: actual maintenance spend aggregated per asset.
  const booksWithPm = await request('/api/books', { headers: auth });
  if (!booksWithPm.books.some((b) => b.code === 'PM')) throw new Error('PM book missing from the registry');
  const yearNow = new Date().getFullYear();
  const pmLedger = await request(`/api/books/PM/ledger?year=${yearNow}`, { headers: auth });
  if (pmLedger.ledger.kind !== 'maintenance' || pmLedger.ledger.summary.cost < 52) {
    throw new Error('PM book ledger did not aggregate maintenance cost');
  }
  await request('/api/pm-settings', {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({ pm_default_frequency_value: 6, pm_default_frequency_unit: 'months', pm_lead_days: 14 })
  });
  const pmSettings = await request('/api/pm-settings', { headers: auth });
  if (pmSettings.settings.pm_default_frequency_value !== 6 || pmSettings.settings.pm_lead_days !== 14) {
    throw new Error('PM settings did not persist');
  }

  // ---- Dynamic PM scheduling: usage meters + lifespan wear curve --------------
  // Turn on both dynamic signals.
  await request('/api/pm-settings', {
    method: 'PUT', headers: auth,
    body: JSON.stringify({ pm_usage_tracking_enabled: true, pm_lifespan_curve_enabled: true, pm_lifespan_min_factor: 0.5, pm_lifespan_curve_exponent: 1 })
  });
  const dynSettings = await request('/api/pm-settings', { headers: auth });
  if (!dynSettings.settings.pm_usage_tracking_enabled || !dynSettings.settings.pm_lifespan_curve_enabled) {
    throw new Error('Dynamic PM settings did not persist');
  }

  // A plan that tracks a 500-unit meter (e.g. operating hours).
  const meterPlan = await request('/api/pm-plans', {
    method: 'POST', headers: auth,
    body: JSON.stringify({
      name: `Usage Plan ${suffix}`, frequency_value: 12, frequency_unit: 'months',
      meters: [{ label: 'Operating Hours', unit: 'hrs', usage_interval: 500 }]
    })
  });
  if (!meterPlan.plan.meters || meterPlan.plan.meters.length !== 1 || Number(meterPlan.plan.meters[0].usage_interval) !== 500) {
    throw new Error('Plan meter was not stored');
  }

  // A brand-new asset so the lifespan curve does NOT compress yet.
  const youngAsset = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({ asset_id: `PM-YOUNG-${suffix}`, description: 'Young pump', category: 'Computer Equipment', acquisition_cost: 1000, in_service_date: new Date().toISOString().slice(0, 10), useful_life_months: 60 })
  });
  const youngId = youngAsset.asset.id;
  const dueIn12mo = await request(`/api/assets/${youngId}/pm`, {
    method: 'POST', headers: auth,
    body: JSON.stringify({ plan_id: meterPlan.plan.id, frequency_value: 12, frequency_unit: 'months', lifespan_adjust: true })
  });
  const youngApId = dueIn12mo.pm.id;
  const youngMeterId = (await request(`/api/assets/${youngId}/pm`, { headers: auth })).pm.find((p) => p.id === youngApId).meters[0].id;

  // Complete with a baseline reading; threshold resets to baseline + 500.
  const today = new Date().toISOString().slice(0, 10);
  const baseDue = (await request(`/api/assets/${youngId}/pm/${youngApId}/complete`, {
    method: 'POST', headers: auth,
    body: JSON.stringify({ signature: pngDataUrl, completed_at: today, meter_readings: [{ asset_pm_meter_id: youngMeterId, reading: 1000 }] })
  })).pm.next_due_date;
  // A young asset on a 12-month calendar with no usage rate yet → ~12 months out (curve barely moves it).
  if (!baseDue || baseDue <= today) throw new Error('Composite next-due did not advance after completion');

  // Log a reading 10 days later implying ~30 hrs/day → reaches 1500 (the threshold) well before 12 months.
  const tenDays = new Date(Date.now() + 10 * 86400000).toISOString().slice(0, 10);
  const afterReading = await request(`/api/assets/${youngId}/pm/${youngApId}/readings`, {
    method: 'POST', headers: auth,
    body: JSON.stringify({ asset_pm_meter_id: youngMeterId, reading: 1300, reading_date: tenDays })
  });
  if (!(afterReading.pm.next_due_date < baseDue)) {
    throw new Error('Fast usage rate did not pull the next-due date in ahead of the calendar date');
  }

  // Push the meter past its threshold → a usage-driven overdue PM alert.
  await request(`/api/assets/${youngId}/pm/${youngApId}/readings`, {
    method: 'POST', headers: auth,
    body: JSON.stringify({ asset_pm_meter_id: youngMeterId, reading: 1600, reading_date: tenDays })
  });
  const usageScan = await request('/api/alerts/scan', { method: 'POST', headers: auth });
  const usageAlert = usageScan.alerts.find((a) => a.reference_id === youngApId && /Usage threshold/.test(a.message || ''));
  if (!usageAlert) throw new Error('Usage-threshold PM alert was not raised');

  // Lifespan curve: an end-of-life asset compresses the same interval vs the young one.
  const oldDate = new Date(Date.now() - 58 * 30 * 86400000).toISOString().slice(0, 10); // ~58 months old of a 60-month life
  const oldAsset = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({ asset_id: `PM-OLD-${suffix}`, description: 'Old pump', category: 'Computer Equipment', acquisition_cost: 1000, in_service_date: oldDate, useful_life_months: 60 })
  });
  const oldAttach = await request(`/api/assets/${oldAsset.asset.id}/pm`, {
    method: 'POST', headers: auth,
    body: JSON.stringify({ plan_id: meterPlan.plan.id, frequency_value: 12, frequency_unit: 'months', lifespan_adjust: true })
  });
  const oldMeterId = (await request(`/api/assets/${oldAsset.asset.id}/pm`, { headers: auth })).pm.find((p) => p.id === oldAttach.pm.id).meters[0].id;
  const oldDue = (await request(`/api/assets/${oldAsset.asset.id}/pm/${oldAttach.pm.id}/complete`, {
    method: 'POST', headers: auth,
    body: JSON.stringify({ signature: pngDataUrl, completed_at: today, meter_readings: [{ asset_pm_meter_id: oldMeterId, reading: 1000 }] })
  })).pm.next_due_date;
  if (!(oldDue < baseDue)) {
    throw new Error('Lifespan curve did not compress the interval for an end-of-life asset');
  }

  // Nightly recompute job: persist a manually-stale next_due_date, then sweep it back to "today"
  // because the meter is already past its threshold (usage projection → today).
  await request(`/api/assets/${youngId}/pm/${youngApId}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ next_due_date: '2099-01-01' })
  });
  const recompute = await request('/api/pm-recompute', { method: 'POST', headers: auth, body: JSON.stringify({}) });
  if (!recompute.result || recompute.result.updated < 1) throw new Error('Manual recompute did not update any schedules');
  if (!recompute.settings.pm_last_recompute_at) throw new Error('Recompute did not stamp last-run time');
  const sweptSchedule = (await request(`/api/assets/${youngId}/pm`, { headers: auth })).pm.find((p) => p.id === youngApId);
  if (sweptSchedule.next_due_date >= '2099-01-01') {
    throw new Error('Recompute did not refresh the stale projected due-date');
  }
  // Recompute settings persist.
  await request('/api/pm-settings', { method: 'PUT', headers: auth, body: JSON.stringify({ pm_recompute_enabled: true, pm_recompute_hour: 3 }) });
  const recomputeSettings = await request('/api/pm-settings', { headers: auth });
  if (!recomputeSettings.settings.pm_recompute_enabled || recomputeSettings.settings.pm_recompute_hour !== 3) {
    throw new Error('Recompute settings did not persist');
  }

  // Contacts directory: typed records, type-specific fields, filtering, notes.
  const vendorContact = await request('/api/contacts', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      type: 'vendor', organization: `Acme Supply ${suffix}`, email: `sales-${suffix}@acme.test`,
      phone: '+1 555-0100', rating: 4, credit_term: 'net_60', country: 'US'
    })
  });
  if (vendorContact.contact.type !== 'vendor' || vendorContact.contact.rating !== 4 || vendorContact.contact.credit_term !== 'net_60') {
    throw new Error('Vendor contact fields did not persist');
  }
  await request('/api/contacts', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      type: 'contractor', first_name: 'Dana', last_name: 'Cruz', email: `dana-${suffix}@contractor.test`,
      tax_id: 'BL-99821', department: 'Facilities'
    })
  });
  const vendorList = await request('/api/contacts?type=vendor', { headers: auth });
  if (!vendorList.contacts.some((c) => c.id === vendorContact.contact.id) || vendorList.contacts.some((c) => c.type !== 'vendor')) {
    throw new Error('Contact type filter failed');
  }
  const updatedContact = await request(`/api/contacts/${vendorContact.contact.id}`, {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({ rating: 5 })
  });
  if (updatedContact.contact.rating !== 5) throw new Error('Contact update did not persist');
  await request(`/api/contacts/${vendorContact.contact.id}/notes`, {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ body: 'Negotiated faster lead times.' })
  });
  const contactDetail = await request(`/api/contacts/${vendorContact.contact.id}`, { headers: auth });
  if (!contactDetail.contact.contact_notes.length) throw new Error('Contact notes were not returned');

  const employeeResult = await request('/api/employees', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      employee_number: `EMP-${suffix}`,
      name: 'Smoke Test Employee',
      email: `employee-${suffix}@example.com`,
      department: 'Operations',
      location: 'Headquarters'
    })
  });

  await request('/api/assignments', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      asset_id: assetResult.asset.id,
      employee_id: employeeResult.employee.id,
      notes: 'Smoke assignment'
    })
  });

  const assignments = await request('/api/assignments?active=true', { headers: auth });
  if (!assignments.assignments.some((assignment) => assignment.asset_id === assetResult.asset.id && assignment.employee_id === employeeResult.employee.id)) {
    throw new Error('Asset assignment was not saved or returned correctly');
  }

  const workday = await request('/api/workday/connection', {
    method: 'PUT',
    headers: auth,
    body: JSON.stringify({
      name: 'Smoke Workday',
      tenant: 'smoke',
      base_url: 'https://example.workday.com',
      token_url: 'https://example.workday.com/oauth2/token',
      client_id: 'client',
      client_secret: 'secret',
      workers_path: '/workers',
      enabled: false,
      sync_enabled: true,
      sync_interval_hours: 12
    })
  });
  if (!workday.connection.configured || workday.connection.sync_enabled !== true || workday.connection.sync_interval_hours !== 12) {
    throw new Error('Workday connection configuration did not persist');
  }

  await request('/api/workday/import-workers', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      workers: [{ id: `WD-${suffix}`, firstName: 'Pat', lastName: 'Lee', email: `pat-${suffix}@corp.test`, department: 'IT', managerName: 'Sam Ray', hireDate: '2024-02-01' }]
    })
  });
  const importedContacts = await request(`/api/contacts?type=employee&search=pat-${suffix}`, { headers: auth });
  if (!importedContacts.contacts.some((c) => c.workday_worker_id === `WD-${suffix}` && c.start_date === '2024-02-01' && c.manager_last_name === 'Ray')) {
    throw new Error('Workday import did not create an enriched employee contact');
  }

  const report = await request('/api/reports/depreciation?year=2026&book=GAAP', { headers: auth });
  if (!Array.isArray(report.rows) || !report.rows.length) {
    throw new Error('Depreciation report returned no rows');
  }

  // 200% declining-balance methods. MF200 (MACRS) ignores salvage and reproduces the IRS
  // 5-year table; DB200 (standard) honors salvage (recovers only cost − salvage).
  const round = (n) => Math.round(Number(n));
  const macrsAsset = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({
      asset_id: `MF-${suffix}`, description: 'MF200 press', category: 'Computer Equipment',
      acquisition_cost: 10000, salvage_value: 1000, in_service_date: '2026-01-01', useful_life_months: 60,
      books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'MF200', convention: 'half_year', useful_life_months: 60, cost: 10000 }]
    })
  });
  const mfSched = (await request(`/api/assets/${macrsAsset.asset.id}/depreciation?startYear=2026&endYear=2032`, { headers: auth }))
    .schedules.filter((r) => r.book === 'FEDERAL');
  const mfByYear = Object.fromEntries(mfSched.map((r) => [r.year, round(r.depreciation)]));
  // IRS 5-year 200%DB half-year: 2000, 3200, 1920, 1152, 1152, 576 — salvage is ignored.
  if (mfByYear[2026] !== 2000 || mfByYear[2027] !== 3200 || mfByYear[2028] !== 1920) {
    throw new Error(`MF200 schedule incorrect: ${JSON.stringify(mfByYear)}`);
  }
  const mfTotal = round(mfSched.reduce((s, r) => s + r.depreciation, 0));
  if (mfTotal !== 10000) throw new Error(`MF200 should recover full cost (MACRS ignores salvage), got ${mfTotal}`);

  const dbAsset = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({
      asset_id: `DB-${suffix}`, description: 'DB200 press', category: 'Computer Equipment',
      acquisition_cost: 10000, salvage_value: 1000, in_service_date: '2026-01-01', useful_life_months: 60,
      books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'DB200', convention: 'half_year', useful_life_months: 60, cost: 10000 }]
    })
  });
  const dbSched = (await request(`/api/assets/${dbAsset.asset.id}/depreciation?startYear=2026&endYear=2034`, { headers: auth }))
    .schedules.filter((r) => r.book === 'FEDERAL');
  const dbTotal = round(dbSched.reduce((s, r) => s + r.depreciation, 0));
  if (dbTotal !== 9000) throw new Error(`DB200 should recover cost − salvage (9000), got ${dbTotal}`);
  if (dbSched.some((r) => r.netBookValue < 999)) throw new Error('DB200 depreciated below salvage value');

  // New York Liberty Zone: seeded zone applies a 30% §168 allowance to the federal book in year 1.
  const zoneList = await request('/api/depreciation-zones', { headers: auth });
  const liberty = zoneList.zones.find((z) => z.code === 'ny_liberty');
  if (!liberty || liberty.allowance_percent !== 30) throw new Error('New York Liberty Zone was not seeded');
  const zoneAsset = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({
      asset_id: `LZ-${suffix}`, description: 'Liberty Zone equipment', category: 'Computer Equipment',
      acquisition_cost: 10000, in_service_date: '2005-01-01', useful_life_months: 60, special_zone: 'ny_liberty',
      books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'MF200', convention: 'half_year', useful_life_months: 60, cost: 10000 }]
    })
  });
  const lzSched = (await request(`/api/assets/${zoneAsset.asset.id}/depreciation?startYear=2005&endYear=2011`, { headers: auth }))
    .schedules.filter((r) => r.book === 'FEDERAL');
  const lzByYear = Object.fromEntries(lzSched.map((r) => [r.year, round(r.depreciation)]));
  // 30% allowance (3000) + first-year MACRS (20% half-year) on the remaining 7000 (1400) = 4400.
  if (lzByYear[2005] !== 4400) throw new Error(`Liberty Zone year-1 allowance incorrect: ${JSON.stringify(lzByYear)}`);

  // Outside the placed-in-service window (2008) the allowance must NOT apply.
  const outAsset = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({
      asset_id: `LZX-${suffix}`, description: 'Out-of-window', category: 'Computer Equipment',
      acquisition_cost: 10000, in_service_date: '2008-01-01', useful_life_months: 60, special_zone: 'ny_liberty',
      books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'MF200', convention: 'half_year', useful_life_months: 60, cost: 10000 }]
    })
  });
  const outYear1 = round((await request(`/api/assets/${outAsset.asset.id}/depreciation?startYear=2008&endYear=2008`, { headers: auth }))
    .schedules.filter((r) => r.book === 'FEDERAL')[0].depreciation);
  if (outYear1 !== 2000) throw new Error(`Out-of-window asset should get plain MACRS year-1 (2000), got ${outYear1}`);

  // Section 179 limits + Enterprise Zone: the seeded 2015 limit ($500,000) caps a plain asset's
  // §179, but an Enterprise Zone asset gets the +$35,000 increase, letting a larger §179 through.
  const limitList = await request('/api/section-179-limits', { headers: auth });
  if (!limitList.limits.some((l) => l.tax_year === 2015 && l.base_limit === 500000)) {
    throw new Error('2015 §179 limit was not seeded');
  }
  const ezList = await request('/api/depreciation-zones', { headers: auth });
  const ez = ezList.zones.find((z) => z.code === 'enterprise_zone');
  if (!ez || ez.section_179_increase !== 35000 || ez.section_179_cost_factor !== 0.5) {
    throw new Error('Enterprise Zone was not seeded with the §179 increase/cost factor');
  }
  // Plain asset (no zone): §179 entered 520k is capped at the 500k base limit. Straight-line, 5yr,
  // half-year → basis 100k, year-1 SL 10k → year-1 = 500000 + 10000 = 510000.
  const s179Plain = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({
      asset_id: `S179-${suffix}`, description: '§179 capped', category: 'Computer Equipment',
      acquisition_cost: 600000, in_service_date: '2015-01-01', useful_life_months: 60,
      books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'straight_line', convention: 'half_year', useful_life_months: 60, section_179_amount: 520000, cost: 600000 }]
    })
  });
  const plainY1 = round((await request(`/api/assets/${s179Plain.asset.id}/depreciation?startYear=2015&endYear=2015`, { headers: auth }))
    .schedules.filter((r) => r.book === 'FEDERAL')[0].depreciation);
  if (plainY1 !== 510000) throw new Error(`§179 should cap at the 500k base limit (year-1 510000), got ${plainY1}`);
  // Enterprise Zone asset: limit 500k + 35k = 535k, so the full 520k §179 applies. basis 80k,
  // year-1 SL 8k → year-1 = 520000 + 8000 = 528000.
  const s179Ez = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({
      asset_id: `S179EZ-${suffix}`, description: '§179 enterprise zone', category: 'Computer Equipment',
      acquisition_cost: 600000, in_service_date: '2015-01-01', useful_life_months: 60, special_zone: 'enterprise_zone',
      books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'straight_line', convention: 'half_year', useful_life_months: 60, section_179_amount: 520000, cost: 600000 }]
    })
  });
  const ezY1 = round((await request(`/api/assets/${s179Ez.asset.id}/depreciation?startYear=2015&endYear=2015`, { headers: auth }))
    .schedules.filter((r) => r.book === 'FEDERAL')[0].depreciation);
  if (ezY1 !== 528000) throw new Error(`Enterprise Zone §179 increase should allow 520k (year-1 528000), got ${ezY1}`);

  // Gulf Opportunity (GO) Zone: combines a 50% §168 allowance AND a +$100,000 §179 increase.
  const go = ezList.zones.find((z) => z.code === 'go_zone');
  if (!go || go.allowance_percent !== 50 || go.section_179_increase !== 100000 || go.section_179_threshold_increase !== 600000) {
    throw new Error('GO Zone was not seeded with the 50% allowance + §179 increase/threshold');
  }
  // 2007 base §179 limit is $125,000; GO Zone raises it to $225,000, so the entered $200,000 §179
  // applies in full. Then the 50% §168 allowance hits the post-§179 basis, plus first-year SL.
  // §179 200000 + bonus 50%×(600000−200000)=200000 + SL ((600000−200000−200000)/5)×0.5=20000 = 420000.
  const goAsset = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({
      asset_id: `GO-${suffix}`, description: 'GO Zone equipment', category: 'Computer Equipment',
      acquisition_cost: 600000, in_service_date: '2007-06-01', useful_life_months: 60, special_zone: 'go_zone',
      books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'straight_line', convention: 'half_year', useful_life_months: 60, section_179_amount: 200000, cost: 600000 }]
    })
  });
  const goY1 = round((await request(`/api/assets/${goAsset.asset.id}/depreciation?startYear=2007&endYear=2007`, { headers: auth }))
    .schedules.filter((r) => r.book === 'FEDERAL')[0].depreciation);
  if (goY1 !== 420000) throw new Error(`GO Zone year-1 (50% allowance + §179 increase) should be 420000, got ${goY1}`);

  // A GO Zone asset placed in service in 2010 is past the §179 window (ends 2008) but still within
  // the §168 allowance window (to 2011). It must get the 50% allowance but NOT the §179 increase:
  // entered $550,000 §179 is capped at the 2010 base limit of $500,000 (not $600,000).
  // §179 500000 + bonus 50%×(600000−500000)=50000 + SL ((600000−500000−50000)/5)×0.5=5000 = 555000.
  const goLate = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({
      asset_id: `GOL-${suffix}`, description: 'GO Zone late', category: 'Computer Equipment',
      acquisition_cost: 600000, in_service_date: '2010-06-01', useful_life_months: 60, special_zone: 'go_zone',
      books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'straight_line', convention: 'half_year', useful_life_months: 60, section_179_amount: 550000, cost: 600000 }]
    })
  });
  const goLateY1 = round((await request(`/api/assets/${goLate.asset.id}/depreciation?startYear=2010&endYear=2010`, { headers: auth }))
    .schedules.filter((r) => r.book === 'FEDERAL')[0].depreciation);
  if (goLateY1 !== 555000) throw new Error(`GO Zone 2010 asset: §179 increase expired but allowance applies (year-1 555000), got ${goLateY1}`);

  // Kansas Disaster Zone (Qualified Recovery Assistance): 50% §168 allowance + $100,000 §179 increase,
  // §168 and §179 windows coincide (2007-05-05 to 2008-12-31). Same shape as the GO Zone 2007 case.
  const kz = ezList.zones.find((z) => z.code === 'kansas_disaster');
  if (!kz || kz.allowance_percent !== 50 || kz.section_179_increase !== 100000 || kz.section_179_threshold_increase !== 600000) {
    throw new Error('Kansas Disaster Zone was not seeded with the 50% allowance + §179 increase/threshold');
  }
  const kzAsset = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({
      asset_id: `KZ-${suffix}`, description: 'Kansas recovery equipment', category: 'Computer Equipment',
      acquisition_cost: 600000, in_service_date: '2007-06-01', useful_life_months: 60, special_zone: 'kansas_disaster',
      books: [{ book_type: 'FEDERAL', active: true, depreciation_method: 'straight_line', convention: 'half_year', useful_life_months: 60, section_179_amount: 200000, cost: 600000 }]
    })
  });
  const kzY1 = round((await request(`/api/assets/${kzAsset.asset.id}/depreciation?startYear=2007&endYear=2007`, { headers: auth }))
    .schedules.filter((r) => r.book === 'FEDERAL')[0].depreciation);
  // §179 200000 (limit raised 125k→225k) + 50% allowance 200000 + SL 20000 = 420000.
  if (kzY1 !== 420000) throw new Error(`Kansas Disaster Zone year-1 (50% allowance + §179 increase) should be 420000, got ${kzY1}`);

  // Disposal: preview gain/loss, then record a full disposal.
  const disposalAsset = await request('/api/assets', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      asset_id: `D-${suffix}`,
      description: 'Disposal test press',
      category: 'Computer Equipment',
      acquisition_cost: 10000,
      acquired_date: '2022-01-05',
      in_service_date: '2022-01-05',
      useful_life_months: 60,
      depreciation_method: 'straight_line'
    })
  });
  const preview = await request(`/api/assets/${disposalAsset.asset.id}/disposal/preview`, {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ book_type: 'GAAP', disposal_date: '2026-06-01', disposal_price: 3000, disposal_expense: 200 })
  });
  if (typeof preview.preview.gain_loss !== 'number' || typeof preview.preview.net_book_value !== 'number') {
    throw new Error('Disposal preview did not return gain/loss figures');
  }
  const disposed = await request(`/api/assets/${disposalAsset.asset.id}/disposals`, {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ book_type: 'GAAP', disposal_date: '2026-06-01', disposal_price: 3000, disposal_expense: 200, method: 'sale', property_type: '1245' })
  });
  if (disposed.asset.status !== 'disposed' || !disposed.disposal?.id) {
    throw new Error('Full disposal did not mark the asset disposed');
  }

  // Impairment adjustment
  const adjustment = await request(`/api/assets/${disposalAsset.asset.id}/adjustments`, {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ type: 'impairment', amount: 500, book_type: 'GAAP', reason: 'Flood damage' })
  });
  if (!adjustment.adjustment?.id) throw new Error('Adjustment was not recorded');

  // Revaluation adjustment (feeds the revaluation reports)
  const reval = await request(`/api/assets/${disposalAsset.asset.id}/adjustments`, {
    method: 'POST', headers: auth,
    body: JSON.stringify({ type: 'revaluation', amount: 800, book_type: 'GAAP', reason: 'Market revaluation' })
  });
  if (!reval.adjustment?.id) throw new Error('Revaluation adjustment was not recorded');

  // Abandonment disposal (counts as a write-off) on a fresh asset
  const abandonAsset = await request('/api/assets', {
    method: 'POST', headers: auth,
    body: JSON.stringify({
      asset_id: `WO-${suffix}`, description: 'Abandoned unit', category: 'Computer Equipment',
      acquisition_cost: 5000, acquired_date: '2023-01-01', in_service_date: '2023-01-01', useful_life_months: 60, depreciation_method: 'straight_line'
    })
  });
  const abandoned = await request(`/api/assets/${abandonAsset.asset.id}/disposals`, {
    method: 'POST', headers: auth,
    body: JSON.stringify({ book_type: 'GAAP', disposal_date: '2026-06-15', disposal_price: 0, disposal_expense: 0, method: 'abandonment' })
  });
  if (!abandoned.disposal?.id) throw new Error('Abandonment disposal was not recorded');

  // New report families: catalog presence, summaries, journals, transactions, history, dossier.
  const newCatalogKeys = (await request('/api/reports/catalog', { headers: auth })).reports.map((r) => r.key);
  for (const key of ['asset_journal', 'fixed_asset_journal', 'journal_revaluation', 'journal_writeoff', 'txn_revaluations',
    'txn_writeoffs', 'txn_purchases', 'txn_disposals', 'asset_summary', 'depreciation_summary', 'ytd_depreciation_summary',
    'purchase_summary', 'disposal_summary', 'revaluation_summary', 'writeoff_summary', 'asset_history', 'future_depreciation',
    'journal_depreciation_monthly', 'journal_disposals_monthly']) {
    if (!newCatalogKeys.includes(key)) throw new Error(`Report catalog missing ${key}`);
  }
  const revalSummary = await runReportApi('revaluation_summary', { year: 2026 });
  if (!revalSummary.report.rows.some((r) => /revaluation/i.test(r.type))) throw new Error('Revaluation summary did not include the revaluation');
  const writeoffTxn = await runReportApi('txn_writeoffs', { year: 2026 });
  const woSources = writeoffTxn.report.rows.map((r) => r.source);
  if (!woSources.includes('Impairment') || !woSources.includes('Abandonment')) {
    throw new Error('Write-off transactions must include both impairment and abandonment');
  }
  const woJournal = await runReportApi('journal_writeoff', { year: 2026 });
  if (Math.round(woJournal.report.totals.debit) !== Math.round(woJournal.report.totals.credit) || woJournal.report.totals.debit <= 0) {
    throw new Error('Write-off journal entry must be balanced and non-zero');
  }
  const assetSummaryReport = await runReportApi('asset_summary', { book: 'GAAP', year: 2026 });
  if (!assetSummaryReport.report.rows.length || typeof assetSummaryReport.report.totals.count !== 'number') {
    throw new Error('Asset summary did not aggregate by category');
  }
  const historyReport = await runReportApi('asset_history', { asset_id: disposalAsset.asset.id });
  if (!historyReport.report.rows.some((r) => /disposal/i.test(r.event))) throw new Error('Asset history did not include the disposal event');
  const journalReport = await runReportApi('asset_journal', { asset_id: disposalAsset.asset.id });
  if (journalReport.report.meta?.layout !== 'dossier' || !journalReport.report.meta.sections.length) {
    throw new Error('Asset journal did not return a dossier with sections');
  }
  const journalPdf = await fetch(`${baseUrl}/api/reports/export`, {
    method: 'POST', headers: { ...auth, 'Content-Type': 'application/json' },
    body: JSON.stringify({ type: 'asset_journal', options: { asset_id: disposalAsset.asset.id }, format: 'pdf' })
  });
  const journalPdfBuf = Buffer.from(await journalPdf.arrayBuffer());
  if (!journalPdf.ok || journalPdf.headers.get('content-type') !== 'application/pdf' || journalPdfBuf.length < 500) {
    throw new Error('Asset journal PDF export failed');
  }

  // Lease accounting + amortization schedule
  const lease = await request('/api/leases', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      asset_id: assetId,
      lessor: 'Equip Leasing Co',
      contract_value: 24000,
      start_date: '2026-01-01',
      end_date: '2028-01-01',
      discount_rate: 6,
      payment_frequency: 'monthly'
    })
  });
  const schedule = await request(`/api/leases/${lease.lease.id}/schedule`, { headers: auth });
  if (!schedule.schedule?.rows?.length || !(schedule.schedule.summary.present_value > 0)) {
    throw new Error('Lease amortization schedule was not generated');
  }
  if (schedule.schedule.rows.length !== schedule.schedule.summary.periods) {
    throw new Error('Lease schedule period count mismatch');
  }

  // Parts inventory: part record, stock locations (aisle/bin), reservations, movements, photos.
  const workLocation = await request('/api/contacts', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ type: 'work_location', organization: `Main Warehouse ${suffix}` })
  });
  const partResult = await request('/api/parts', {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({
      part_number: `HYD-${suffix}`, name: 'Hydraulic filter', category: 'Filters',
      unit_of_measure: 'each', unit_cost: 18.5, reorder_point: 5, reorder_quantity: 20,
      measurements: '120mm x 80mm', supplier_contact_id: vendorContact.contact.id
    })
  });
  const partId = partResult.part.id;
  if (partResult.part.supplier_name === null) throw new Error('Part supplier name was not resolved from contacts');

  const withStock = await request(`/api/parts/${partId}/stock`, {
    method: 'POST',
    headers: auth,
    body: JSON.stringify({ location_contact_id: workLocation.contact.id, aisle: 'A12', bin: 'B3', quantity: 10, reserved: 2 })
  });
  const stockRow = withStock.part.stock[0];
  if (!stockRow || stockRow.aisle !== 'A12' || stockRow.bin !== 'B3' || stockRow.available !== 8) {
    throw new Error('Part stock location (aisle/bin/available) was not stored correctly');
  }
  if (withStock.part.on_hand !== 10 || withStock.part.reserved !== 2) {
    throw new Error('Part on-hand/reserved aggregation failed');
  }

  // Receive 5 more, then reserve 3, then issue 4.
  const received = await request(`/api/parts/${partId}/transactions`, {
    method: 'POST', headers: auth, body: JSON.stringify({ stock_id: stockRow.id, type: 'receive', quantity: 5, notes: 'PO-1' })
  });
  if (received.part.on_hand !== 15) throw new Error('Receive movement did not increase on-hand');
  const reserved = await request(`/api/parts/${partId}/transactions`, {
    method: 'POST', headers: auth, body: JSON.stringify({ stock_id: stockRow.id, type: 'reserve', quantity: 3, asset_id: assetId })
  });
  if (reserved.part.reserved !== 5) throw new Error('Reserve movement did not increase reserved');
  const issued = await request(`/api/parts/${partId}/transactions`, {
    method: 'POST', headers: auth, body: JSON.stringify({ stock_id: stockRow.id, type: 'issue', quantity: 4 })
  });
  if (issued.part.on_hand !== 11 || issued.part.available !== 6) throw new Error('Issue movement did not reduce on-hand/available');
  if (issued.part.transactions.length !== 3) throw new Error('Part transaction history was not recorded');

  // Photo attach
  const photoPart = await request(`/api/parts/${partId}/photos`, {
    method: 'POST', headers: auth, body: JSON.stringify({ name: 'filter.png', mime: 'image/png', data: pngDataUrl })
  });
  if (!photoPart.part.photos.length) throw new Error('Part photo was not attached');

  // Low-stock filter: drop below reorder point and confirm it surfaces.
  await request(`/api/parts/${partId}/transactions`, {
    method: 'POST', headers: auth, body: JSON.stringify({ stock_id: stockRow.id, type: 'adjust', quantity: 3 })
  });
  const lowStock = await request('/api/parts?low_stock=true', { headers: auth });
  if (!lowStock.parts.some((p) => p.id === partId && p.low_stock)) {
    throw new Error('Low-stock filter did not surface the depleted part');
  }
  const partList = await request(`/api/parts?search=HYD-${suffix}`, { headers: auth });
  if (!partList.parts.some((p) => p.id === partId)) throw new Error('Part search did not return the created part');

  // Parts categories: seeded defaults present; create, rename (cascades to parts), usage count, delete.
  const seededPartCats = await request('/api/part-categories', { headers: auth });
  for (const expected of ['Electrical', 'Plumbing', 'Fire Safety', 'Other']) {
    if (!seededPartCats.categories.some((c) => c.name === expected)) throw new Error(`Seeded part category "${expected}" missing`);
  }
  const newPartCat = await request('/api/part-categories', {
    method: 'POST', headers: auth, body: JSON.stringify({ name: `Part Cat ${suffix}` })
  });
  if (!newPartCat.category?.id) throw new Error('Part category was not created');
  await request(`/api/parts/${partId}`, { method: 'PUT', headers: auth, body: JSON.stringify({ category: `Part Cat ${suffix}` }) });
  const renamedPartCat = await request(`/api/part-categories/${newPartCat.category.id}`, {
    method: 'PUT', headers: auth, body: JSON.stringify({ name: `Part Cat ${suffix} (renamed)` })
  });
  if (renamedPartCat.renamed_parts < 1) throw new Error('Part category rename did not cascade to parts');
  const renamedPartDetail = await request(`/api/parts/${partId}`, { headers: auth });
  if (renamedPartDetail.part.category !== `Part Cat ${suffix} (renamed)`) throw new Error('Part category was not updated by rename');
  const partCatList = await request('/api/part-categories', { headers: auth });
  if (!partCatList.categories.some((c) => c.id === newPartCat.category.id && c.part_count === 1)) throw new Error('Part category usage count was not reported');
  const partCatDelete = await request(`/api/part-categories/${newPartCat.category.id}`, { method: 'DELETE', headers: auth });
  if (!partCatDelete.deleted || partCatDelete.part_count !== 1) throw new Error('Part category delete did not report usage impact');

  // Alert webhook: each new alert is delivered to an external endpoint as a JSON POST.
  const webhookHits = [];
  const hookSecret = 'whsec_smoke';
  const hookServer = http.createServer((req, res) => {
    let raw = '';
    req.on('data', (chunk) => { raw += chunk; });
    req.on('end', () => {
      webhookHits.push({ headers: req.headers, raw, body: JSON.parse(raw || '{}') });
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end('{"ok":true}');
    });
  });
  await new Promise((resolve) => hookServer.listen(4100, '127.0.0.1', resolve));
  try {
    const hookUrl = 'http://127.0.0.1:4100/hook';
    const savedHook = await request('/api/notifications/settings', {
      method: 'PUT', headers: auth,
      body: JSON.stringify({ webhook_enabled: true, webhook_url: hookUrl, webhook_secret: hookSecret })
    });
    if (savedHook.settings.webhook_url !== hookUrl || savedHook.settings.webhook_secret_set !== true || savedHook.settings.webhook_secret !== undefined) {
      throw new Error('Webhook settings did not persist or leaked the secret');
    }

    // Test event + HMAC signature verification.
    const hookTest = await request('/api/notifications/webhook-test', { method: 'POST', headers: auth, body: JSON.stringify({}) });
    if (!hookTest.ok) throw new Error('Webhook test event was not delivered');
    const testHit = webhookHits.find((h) => h.body.event === 'test');
    if (!testHit) throw new Error('Webhook receiver did not get the test event');
    const expectedSig = `sha256=${crypto.createHmac('sha256', hookSecret).update(testHit.raw).digest('hex')}`;
    if (testHit.headers['x-deprecore-signature'] !== expectedSig) {
      throw new Error('Webhook HMAC signature was missing or incorrect');
    }

    // A fresh overdue task guarantees a new open alert; the cycle should POST it.
    await request(`/api/assets/${assetId}/tasks`, {
      method: 'POST', headers: auth,
      body: JSON.stringify({ title: `Webhook overdue ${suffix}`, type: 'inspection', due_date: '2019-01-01' })
    });
    const cycle = await request('/api/alerts/run', { method: 'POST', headers: auth });
    if (!cycle.webhooked || !(cycle.delivered > 0)) throw new Error('Alert cycle did not deliver alerts to the webhook');
    const alertHit = webhookHits.find((h) => h.body.event === 'alert');
    if (!alertHit || !alertHit.body.type || !alertHit.body.severity) throw new Error('Webhook alert payload was incomplete');
  } finally {
    await new Promise((resolve) => hookServer.close(resolve));
  }

  // Microsoft Teams integration: posts alert digests to a Teams webhook (Adaptive Card / MessageCard).
  const teamsHits = [];
  const teamsServer = http.createServer((req, res) => {
    let raw = '';
    req.on('data', (chunk) => { raw += chunk; });
    req.on('end', () => {
      teamsHits.push({ raw, body: JSON.parse(raw || '{}') });
      res.writeHead(200, { 'Content-Type': 'application/json' });
      res.end('1');
    });
  });
  await new Promise((resolve) => teamsServer.listen(4300, '127.0.0.1', resolve));
  try {
    const teamsUrl = 'http://127.0.0.1:4300/teams';
    const savedTeams = await request('/api/teams/settings', {
      method: 'PUT', headers: auth,
      body: JSON.stringify({ teams_enabled: true, teams_webhook_url: teamsUrl, teams_format: 'adaptive', teams_min_severity: 'info' })
    });
    if (savedTeams.settings.teams_webhook_url !== teamsUrl || savedTeams.settings.teams_format !== 'adaptive' || !savedTeams.settings.teams_enabled) {
      throw new Error('Teams settings did not persist');
    }

    // Test card uses the Adaptive Card (Workflows) envelope.
    const teamsTest = await request('/api/teams/test', { method: 'POST', headers: auth, body: JSON.stringify({}) });
    if (!teamsTest.ok) throw new Error('Teams test card was not delivered');
    const adaptiveHit = teamsHits.find((h) => h.body.type === 'message');
    if (!adaptiveHit || !Array.isArray(adaptiveHit.body.attachments) ||
        adaptiveHit.body.attachments[0].contentType !== 'application/vnd.microsoft.card.adaptive' ||
        adaptiveHit.body.attachments[0].content.type !== 'AdaptiveCard') {
      throw new Error('Teams adaptive card envelope was malformed');
    }

    // Disable the (now-closed) webhook so the next cycle is handled by Teams, then raise a fresh alert.
    await request('/api/notifications/settings', { method: 'PUT', headers: auth, body: JSON.stringify({ webhook_enabled: false }) });
    await request(`/api/assets/${assetId}/tasks`, {
      method: 'POST', headers: auth,
      body: JSON.stringify({ title: `Teams overdue ${suffix}`, type: 'inspection', due_date: '2019-02-02' })
    });
    const teamsCycle = await request('/api/alerts/run', { method: 'POST', headers: auth });
    if (!teamsCycle.teamsPosted || !(teamsCycle.teamsDelivered > 0)) throw new Error('Alert cycle did not post alerts to Teams');
    const digestHit = teamsHits.find((h) => h.body.type === 'message' && JSON.stringify(h.body).includes(`Teams overdue ${suffix}`));
    if (!digestHit) throw new Error('Teams did not receive the alert digest card');

    // Legacy MessageCard format produces a different envelope.
    await request('/api/teams/settings', { method: 'PUT', headers: auth, body: JSON.stringify({ teams_format: 'messagecard' }) });
    const mcTest = await request('/api/teams/test', { method: 'POST', headers: auth, body: JSON.stringify({}) });
    if (!mcTest.ok) throw new Error('Teams MessageCard test was not delivered');
    if (!teamsHits.some((h) => h.body['@type'] === 'MessageCard')) throw new Error('Teams MessageCard payload was not produced');

    // Disable Teams so it doesn't interfere with later alert assertions.
    await request('/api/teams/settings', { method: 'PUT', headers: auth, body: JSON.stringify({ teams_enabled: false }) });
  } finally {
    await new Promise((resolve) => teamsServer.close(resolve));
  }

  // ServiceNow integration: incident push (Table API) + CMDB asset pull, against a mock instance.
  const snHits = [];
  const snServer = http.createServer((req, res) => {
    let raw = '';
    req.on('data', (chunk) => { raw += chunk; });
    req.on('end', () => {
      snHits.push({ method: req.method, url: req.url, auth: req.headers.authorization || '', raw });
      res.setHeader('Content-Type', 'application/json');
      if (req.url.startsWith('/api/now/table/incident') && req.method === 'POST') {
        res.writeHead(201);
        res.end(JSON.stringify({ result: { sys_id: 'sys-inc-123', number: 'INC0099999' } }));
      } else if (req.url.includes('/api/now/table/cmdb_ci_hardware')) {
        res.writeHead(200);
        res.end(JSON.stringify({
          result: [{
            sys_id: 'ci-abc-1', name: 'Mock Server 01', asset_tag: `CMDB-${suffix}`, serial_number: `SN-${suffix}`,
            sys_class_name: 'cmdb_ci_server', manufacturer: 'Dell', location: 'Data Center 1', cost: '2500', purchase_date: '2025-01-15'
          }]
        }));
      } else {
        res.writeHead(200);
        res.end(JSON.stringify({ result: [] }));
      }
    });
  });
  await new Promise((resolve) => snServer.listen(4200, '127.0.0.1', resolve));
  try {
    const snUrl = 'http://127.0.0.1:4200';
    const savedSn = await request('/api/servicenow/settings', {
      method: 'PUT', headers: auth,
      body: JSON.stringify({
        servicenow_instance_url: snUrl, servicenow_username: 'svc_deprecore', servicenow_password: 'snpass',
        servicenow_ticket_enabled: true, servicenow_cmdb_table: 'cmdb_ci_hardware'
      })
    });
    if (savedSn.settings.servicenow_instance_url !== snUrl || savedSn.settings.servicenow_password_set !== true || savedSn.settings.servicenow_password !== undefined) {
      throw new Error('ServiceNow settings did not persist or leaked the password');
    }
    if (!savedSn.settings.servicenow_cmdb_map || !savedSn.settings.servicenow_cmdb_map.serial_number) {
      throw new Error('ServiceNow default CMDB field map was not returned');
    }

    // Connection test (GET incident with a basic-auth header).
    const snTest = await request('/api/servicenow/test', { method: 'POST', headers: auth, body: JSON.stringify({}) });
    if (!snTest.ok) throw new Error('ServiceNow connection test failed');
    if (!snHits.some((h) => h.method === 'GET' && h.auth.startsWith('Basic '))) {
      throw new Error('ServiceNow request did not include basic auth');
    }

    // Submit an alert as an incident.
    await request(`/api/assets/${assetId}/tasks`, {
      method: 'POST', headers: auth,
      body: JSON.stringify({ title: `ServiceNow overdue ${suffix}`, type: 'inspection', due_date: '2018-01-01' })
    });
    const snScan = await request('/api/alerts/scan', { method: 'POST', headers: auth });
    const targetAlert = snScan.alerts.find((a) => a.status === 'open' && !a.external_ticket_number);
    if (!targetAlert) throw new Error('No open alert available to ticket');
    const ticketResult = await request(`/api/alerts/${targetAlert.id}/ticket`, { method: 'POST', headers: auth, body: JSON.stringify({}) });
    if (ticketResult.ticket.number !== 'INC0099999') throw new Error('ServiceNow incident number was not returned');
    if (!snHits.some((h) => h.method === 'POST' && h.url.startsWith('/api/now/table/incident'))) {
      throw new Error('ServiceNow incident POST was not sent');
    }
    // Re-submitting returns the existing ticket rather than duplicating.
    const dupTicket = await request(`/api/alerts/${targetAlert.id}/ticket`, { method: 'POST', headers: auth, body: JSON.stringify({}) });
    if (!dupTicket.ticket.duplicate) throw new Error('Re-ticketing should return the existing incident');
    const ticketedList = await request('/api/alerts', { headers: auth });
    if (!ticketedList.alerts.some((a) => a.id === targetAlert.id && a.external_ticket_number === 'INC0099999')) {
      throw new Error('Alert was not linked to its ServiceNow incident');
    }

    // CMDB pull: create/update assets from configuration items.
    const cmdbSync = await request('/api/servicenow/cmdb-sync', { method: 'POST', headers: auth, body: JSON.stringify({}) });
    if (cmdbSync.total !== 1 || (cmdbSync.created + cmdbSync.updated) < 1) throw new Error('CMDB sync did not import the configuration item');
    const cmdbAssets = await request(`/api/assets?search=CMDB-${suffix}`, { headers: auth });
    const cmdbAsset = cmdbAssets.assets.find((a) => a.asset_id === `CMDB-${suffix}`);
    if (!cmdbAsset || cmdbAsset.serial_number !== `SN-${suffix}` || Number(cmdbAsset.acquisition_cost) !== 2500) {
      throw new Error('CMDB-sourced asset fields were not populated');
    }
    // A second sync should update the same asset, not duplicate it.
    const reSync = await request('/api/servicenow/cmdb-sync', { method: 'POST', headers: auth, body: JSON.stringify({}) });
    if (reSync.created !== 0 || reSync.updated !== 1) throw new Error('Re-syncing a CMDB CI should update, not duplicate');
  } finally {
    await new Promise((resolve) => snServer.close(resolve));
  }

  // ---- Password access control, lockout & audit trail -----------------------
  // Helper that returns status + body instead of throwing, so we can assert on rejections.
  async function rawRequest(p, options = {}) {
    const response = await fetch(`${baseUrl}${p}`, {
      ...options,
      headers: { 'Content-Type': 'application/json', ...(options.headers || {}) }
    });
    const body = await response.json().catch(() => ({}));
    return { status: response.status, body };
  }

  // Tighten the policy: long, complex, short reuse window, fast lockout. expiry_days 0 keeps the
  // admin session from being forced to change mid-test.
  const policyResp = await request('/api/password-policy', {
    method: 'PUT', headers: auth,
    body: JSON.stringify({ policy: {
      min_length: 14, require_upper: true, require_lower: true, require_number: true, require_symbol: true,
      disallow_identifiers: true, history_count: 3, expiry_days: 0, min_age_hours: 0,
      lockout_threshold: 3, lockout_window_minutes: 5
    } })
  });
  if (policyResp.policy.min_length !== 14 || policyResp.policy.lockout_threshold !== 3) {
    throw new Error('Password policy did not persist');
  }
  if (!Array.isArray(policyResp.rules) || !policyResp.rules.length) throw new Error('Policy rules were not described');

  const secEmail = `sec-${suffix}@example.com`;
  const strongPassword = 'Str0ng!Passphrase#2024';

  // A weak password is rejected by the policy.
  const weak = await rawRequest('/api/users', {
    method: 'POST', headers: auth,
    body: JSON.stringify({ name: 'Pat Quinn', email: secEmail, password: 'short', role: 'viewer' })
  });
  if (weak.status !== 400) throw new Error(`Weak password should be rejected (got ${weak.status})`);

  // A compliant password succeeds.
  const created = await request('/api/users', {
    method: 'POST', headers: auth,
    body: JSON.stringify({ name: 'Pat Quinn', email: secEmail, password: strongPassword, role: 'viewer' })
  });
  const secUserId = created.user.id;

  // The new user can sign in (admin-set real password does not force a change).
  const secLogin = await request('/api/auth/login', {
    method: 'POST', body: JSON.stringify({ email: secEmail, password: strongPassword })
  });
  if (secLogin.mustChangePassword) throw new Error('Admin-set password should not force a change');
  const secAuth = { Authorization: `Bearer ${secLogin.token}` };

  // Reusing the current password via self-service change is blocked by password history.
  const reuse = await rawRequest('/api/profile/password', {
    method: 'PUT', headers: secAuth,
    body: JSON.stringify({ current_password: strongPassword, new_password: strongPassword })
  });
  if (reuse.status !== 400) throw new Error(`Password reuse should be rejected (got ${reuse.status})`);

  // A fresh compliant password is accepted.
  const newPassword = 'Rotated!Secret#2025';
  const changed = await rawRequest('/api/profile/password', {
    method: 'PUT', headers: secAuth,
    body: JSON.stringify({ current_password: strongPassword, new_password: newPassword })
  });
  if (changed.status !== 200) throw new Error(`Valid password change failed (got ${changed.status} ${JSON.stringify(changed.body)})`);

  // Account lockout: the threshold is 3, so the third bad attempt locks the account.
  let lockedStatus = null;
  for (let i = 0; i < 3; i += 1) {
    const attempt = await rawRequest('/api/auth/login', {
      method: 'POST', body: JSON.stringify({ email: secEmail, password: 'wrong-password' })
    });
    lockedStatus = attempt.status;
  }
  if (lockedStatus !== 423) throw new Error(`Account should lock after 3 failures (got ${lockedStatus})`);

  // Even the correct password is refused while locked.
  const whileLocked = await rawRequest('/api/auth/login', {
    method: 'POST', body: JSON.stringify({ email: secEmail, password: newPassword })
  });
  if (whileLocked.status !== 423) throw new Error('Correct password should be refused while locked');

  // An admin can unlock; the user can then sign in again.
  await request(`/api/users/${secUserId}/unlock`, { method: 'POST', headers: auth });
  const afterUnlock = await rawRequest('/api/auth/login', {
    method: 'POST', body: JSON.stringify({ email: secEmail, password: newPassword })
  });
  if (afterUnlock.status !== 200) throw new Error(`Login after unlock failed (got ${afterUnlock.status})`);

  // Audit trail: failed logins and the lockout were recorded and are queryable.
  const failedLogs = await request('/api/audit-logs?action=login_failed', { headers: auth });
  if (failedLogs.total < 3) throw new Error(`Expected >=3 login_failed audit rows, found ${failedLogs.total}`);
  if (!failedLogs.facets || !failedLogs.facets.actions.includes('login_failed')) {
    throw new Error('Audit facets did not include login_failed');
  }
  const lockLogs = await request('/api/audit-logs?action=account_locked', { headers: auth });
  if (lockLogs.total < 1) throw new Error('account_locked event was not audited');

  // CSV export returns text/csv with a header row.
  const csvResp = await fetch(`${baseUrl}/api/audit-logs/export?action=login_failed`, { headers: auth });
  if (!(csvResp.headers.get('content-type') || '').includes('text/csv')) throw new Error('Audit export was not text/csv');
  const csvText = await csvResp.text();
  if (!csvText.startsWith('id,timestamp,actor,action')) throw new Error('Audit CSV header was malformed');

  // ---- Multi-company scoping -------------------------------------------------
  const companyHeaders = (id) => ({ ...auth, 'X-Company-Id': String(id) });
  // Company A is the seeded default; create company B.
  const companyA = (await request('/api/entities', { headers: auth })).entities[0].id;
  const companyBEntity = (await request('/api/entities', {
    method: 'POST', headers: auth,
    body: JSON.stringify({ name: `Company B ${suffix}`, base_currency: 'USD', federal_tax_id: '12-3456789', state_tax_id: 'ST-001', local_tax_id: 'LOC-9' })
  })).entity;
  const companyB = companyBEntity.id;
  if (!companyB || companyB === companyA) throw new Error('Second company was not created');
  if (companyBEntity.federal_tax_id !== '12-3456789' || companyBEntity.state_tax_id !== 'ST-001' || companyBEntity.local_tax_id !== 'LOC-9') {
    throw new Error('Company federal/state/local tax ids did not persist');
  }

  // A new company auto-seeds its own book set (distinct ids from company A's).
  const booksA = (await request('/api/books', { headers: companyHeaders(companyA) })).books;
  const booksB = (await request('/api/books', { headers: companyHeaders(companyB) })).books;
  if (!booksB.some((b) => b.code === 'GAAP')) throw new Error('New company did not get a seeded book set');
  if (booksA.find((b) => b.code === 'GAAP').id === booksB.find((b) => b.code === 'GAAP').id) {
    throw new Error('Company B GAAP book is not a distinct per-company record');
  }

  // Create an asset under each company via the X-Company-Id header.
  const assetB = (await request('/api/assets', {
    method: 'POST', headers: companyHeaders(companyB),
    body: JSON.stringify({ asset_id: `B-${suffix}`, description: 'Company B asset', category: 'Computer Equipment', acquisition_cost: 5000, in_service_date: '2024-01-01' })
  })).asset;
  const assetAonly = (await request('/api/assets', {
    method: 'POST', headers: companyHeaders(companyA),
    body: JSON.stringify({ asset_id: `A-${suffix}`, description: 'Company A only', category: 'Computer Equipment', acquisition_cost: 1000, in_service_date: '2024-01-01' })
  })).asset;

  // Listings are scoped to the active company.
  const listA = (await request('/api/assets', { headers: companyHeaders(companyA) })).assets;
  const listB = (await request('/api/assets', { headers: companyHeaders(companyB) })).assets;
  if (listB.some((a) => a.id === assetAonly.id)) throw new Error('Company B sees company A assets');
  if (!listB.some((a) => a.id === assetB.id)) throw new Error('Company B is missing its own asset');
  if (listA.some((a) => a.id === assetB.id)) throw new Error('Company A sees company B assets');

  // Cross-company asset access is blocked (company A cannot fetch company B's asset).
  const crossFetch = await fetch(`${baseUrl}/api/assets/${assetB.id}`, { headers: companyHeaders(companyA) });
  if (crossFetch.status !== 404) throw new Error('Cross-company asset fetch should 404');

  // PM plans are per-company.
  await request('/api/pm-plans', { method: 'POST', headers: companyHeaders(companyB), body: JSON.stringify({ name: `B Plan ${suffix}`, frequency_value: 3, frequency_unit: 'months' }) });
  const plansB = (await request('/api/pm-plans', { headers: companyHeaders(companyB) })).plans;
  const plansA = (await request('/api/pm-plans', { headers: companyHeaders(companyA) })).plans;
  if (!plansB.some((p) => p.name === `B Plan ${suffix}`)) throw new Error('Company B PM plan missing');
  if (plansA.some((p) => p.name === `B Plan ${suffix}`)) throw new Error('Company A sees company B PM plan');

  // Dashboards differ per company.
  const dashA = (await request('/api/dashboard', { headers: companyHeaders(companyA) })).stats;
  const dashB = (await request('/api/dashboard', { headers: companyHeaders(companyB) })).stats;
  if (dashB.asset_count !== 1) throw new Error(`Company B dashboard should count exactly its 1 asset (got ${dashB.asset_count})`);
  if (dashA.asset_count <= dashB.asset_count) throw new Error('Company A (seeded data) should have more assets than the new company B');

  // Dispose company B: it leaves the active list but its data is preserved; then restore it.
  await request(`/api/entities/${companyB}/dispose`, { method: 'POST', headers: auth, body: JSON.stringify({ reason: 'smoke test' }) });
  const activeAfter = (await request('/api/entities', { headers: auth })).entities;
  if (activeAfter.some((e) => e.id === companyB)) throw new Error('Disposed company still appears in the active list');
  const allAfter = (await request('/api/entities?includeDisposed=1', { headers: auth })).entities;
  const disposedRow = allAfter.find((e) => e.id === companyB);
  if (!disposedRow || disposedRow.status !== 'disposed') throw new Error('Disposed company missing from includeDisposed list');
  await request(`/api/entities/${companyB}/restore`, { method: 'POST', headers: auth, body: JSON.stringify({}) });
  const restoredAssets = (await request('/api/assets', { headers: companyHeaders(companyB) })).assets;
  if (!restoredAssets.some((a) => a.id === assetB.id)) throw new Error('Company B asset was lost across dispose/restore');

  // ---- Phase 2: reference data / contacts / templates are per-company ---------
  // A new company auto-seeds the standard categories.
  const seededCatsB = (await request('/api/asset-categories', { headers: companyHeaders(companyB) })).categories;
  if (!seededCatsB.some((c) => c.name === 'Computer Equipment')) throw new Error('New company did not auto-seed reference categories');

  await request('/api/asset-categories', { method: 'POST', headers: companyHeaders(companyB), body: JSON.stringify({ name: `B Category ${suffix}` }) });
  const catsA = (await request('/api/asset-categories', { headers: companyHeaders(companyA) })).categories;
  const catsB = (await request('/api/asset-categories', { headers: companyHeaders(companyB) })).categories;
  if (!catsB.some((c) => c.name === `B Category ${suffix}`)) throw new Error('Company B category missing');
  if (catsA.some((c) => c.name === `B Category ${suffix}`)) throw new Error('Company A sees company B category');

  // Contacts are per-company.
  await request('/api/contacts', { method: 'POST', headers: companyHeaders(companyB), body: JSON.stringify({ type: 'vendor', organization: `B Vendor ${suffix}` }) });
  const contactsA = (await request('/api/contacts', { headers: companyHeaders(companyA) })).contacts;
  const contactsB = (await request('/api/contacts', { headers: companyHeaders(companyB) })).contacts;
  if (!contactsB.some((c) => c.organization === `B Vendor ${suffix}`)) throw new Error('Company B contact missing');
  if (contactsA.some((c) => c.organization === `B Vendor ${suffix}`)) throw new Error('Company A sees company B contact');

  // Templates are per-company.
  await request('/api/templates', { method: 'POST', headers: companyHeaders(companyB), body: JSON.stringify({ name: `B Template ${suffix}`, defaults: {}, custom_fields: [] }) });
  const tplA = (await request('/api/templates', { headers: companyHeaders(companyA) })).templates;
  const tplB = (await request('/api/templates', { headers: companyHeaders(companyB) })).templates;
  if (!tplB.some((t) => t.name === `B Template ${suffix}`)) throw new Error('Company B template missing');
  if (tplA.some((t) => t.name === `B Template ${suffix}`)) throw new Error('Company A sees company B template');

  // ---- Per-company user access control ---------------------------------------
  const limitedEmail = `limited-${suffix}@example.com`;
  const limitedPassword = 'Acc3ssControl!2026X';
  await request('/api/users', {
    method: 'POST', headers: companyHeaders(companyA),
    body: JSON.stringify({ name: 'Limited Person', email: limitedEmail, password: limitedPassword, role: 'viewer', company_ids: [companyA] })
  });
  const limitedLogin = await request('/api/auth/login', { method: 'POST', body: JSON.stringify({ email: limitedEmail, password: limitedPassword }) });
  const limitedAuth = { Authorization: `Bearer ${limitedLogin.token}` };

  // The switcher only lists company A for this user.
  const limitedCompanies = (await request('/api/entities', { headers: limitedAuth })).entities;
  if (limitedCompanies.length !== 1 || limitedCompanies[0].id !== companyA) throw new Error('Limited user can see companies beyond their assignment');

  // Forging X-Company-Id to company B falls back to company A (no leak of B's data).
  const limitedBView = (await request('/api/assets', { headers: { ...limitedAuth, 'X-Company-Id': String(companyB) } })).assets;
  if (limitedBView.some((a) => a.id === assetB.id)) throw new Error('Limited user accessed another company by forging the header');
  if (!limitedBView.some((a) => a.id === assetAonly.id)) throw new Error('Limited user did not fall back to their permitted company');

  // Admins still see every active company.
  const adminCompanies = (await request('/api/entities', { headers: auth })).entities;
  if (!adminCompanies.some((e) => e.id === companyA) || !adminCompanies.some((e) => e.id === companyB)) {
    throw new Error('Admin should see all active companies');
  }

  // ---- Parts + assignments are per-company -----------------------------------
  // The same part number is allowed in different companies (per-company uniqueness).
  const partB = (await request('/api/parts', { method: 'POST', headers: companyHeaders(companyB), body: JSON.stringify({ part_number: `PN-${suffix}`, name: 'B Part' }) })).part;
  const partA = (await request('/api/parts', { method: 'POST', headers: companyHeaders(companyA), body: JSON.stringify({ part_number: `PN-${suffix}`, name: 'A Part' }) })).part;
  if (!partA || !partB || partA.id === partB.id) throw new Error('Per-company part-number uniqueness failed');
  const partsA = (await request('/api/parts', { headers: companyHeaders(companyA) })).parts;
  const partsB = (await request('/api/parts', { headers: companyHeaders(companyB) })).parts;
  if (partsA.some((p) => p.id === partB.id)) throw new Error('Company A sees company B part');
  if (!partsB.some((p) => p.id === partB.id)) throw new Error('Company B part missing');
  const crossPart = await fetch(`${baseUrl}/api/parts/${partB.id}`, { headers: companyHeaders(companyA) });
  if (crossPart.status !== 404) throw new Error('Cross-company part fetch should 404');

  // Assignments scope to the assigned asset's company.
  await request('/api/assignments', { method: 'POST', headers: companyHeaders(companyB), body: JSON.stringify({ asset_id: assetB.id }) });
  const assignsB = (await request('/api/assignments', { headers: companyHeaders(companyB) })).assignments;
  const assignsA = (await request('/api/assignments', { headers: companyHeaders(companyA) })).assignments;
  if (!assignsB.some((a) => a.asset_id === assetB.id)) throw new Error('Company B assignment missing');
  if (assignsA.some((a) => a.asset_id === assetB.id)) throw new Error('Company A sees company B assignment');
  // Assigning another company's asset is rejected.
  const crossAssign = await fetch(`${baseUrl}/api/assignments`, {
    method: 'POST', headers: { ...companyHeaders(companyA), 'Content-Type': 'application/json' }, body: JSON.stringify({ asset_id: assetB.id })
  });
  if (crossAssign.status !== 404) throw new Error('Assigning an asset from another company should 404');

  // The employee directory is per-company.
  const empB = (await request('/api/employees', { method: 'POST', headers: companyHeaders(companyB), body: JSON.stringify({ name: 'B Worker', employee_number: `EMP-${suffix}` }) })).employee;
  if (!empB || empB.entity_id !== companyB) throw new Error('New employee was not scoped to the active company');
  const empsA = (await request('/api/employees', { headers: companyHeaders(companyA) })).employees;
  const empsB = (await request('/api/employees', { headers: companyHeaders(companyB) })).employees;
  if (empsA.some((e) => e.id === empB.id)) throw new Error('Company A sees company B employee');
  if (!empsB.some((e) => e.id === empB.id)) throw new Error('Company B employee missing');
  // Company B's employee cannot be attached to a company A assignment (resolves to no employee).
  const crossEmpAssign = (await request('/api/assignments', {
    method: 'POST', headers: companyHeaders(companyA), body: JSON.stringify({ asset_id: assetAonly.id, employee_id: empB.id })
  })).assignment;
  if (crossEmpAssign.employee_id) throw new Error('A company A assignment accepted a company B employee');

  // ---- GL journal entries: CRUD + audit + import (conflict/merge) + export ----
  const glEntry = (await request('/api/books/GAAP/gl-entries', {
    method: 'POST', headers: companyHeaders(companyA),
    body: JSON.stringify({ entry_date: '2026-03-01', account: '1500', account_type: 'Asset', description: 'Equipment', reference: 'INV-1', debit: 1000, credit: 0, external_id: `GL-${suffix}-100` })
  })).entry;
  if (!glEntry || glEntry.entity_id !== companyA) throw new Error('GL entry was not created/scoped');
  const glListed = (await request('/api/books/GAAP/gl-entries?year=2026', { headers: companyHeaders(companyA) }));
  if (!glListed.entries.some((e) => e.id === glEntry.id) || glListed.summary.debit < 1000) throw new Error('GL entry did not list with totals');

  // Update it (full before/after must be audited).
  await request(`/api/books/GAAP/gl-entries/${glEntry.id}`, {
    method: 'PUT', headers: companyHeaders(companyA), body: JSON.stringify({ debit: 1200, description: 'Equipment (revised)' })
  });
  const glAudit = await request('/api/audit-logs?entity_type=gl_entry', { headers: auth });
  if (glAudit.total < 2) throw new Error('GL changes were not audited');
  const updateLog = glAudit.rows.find((r) => r.action === 'update' && r.entity_id === String(glEntry.id));
  if (!updateLog || !updateLog.before_json || !updateLog.after_json) throw new Error('GL update did not capture full before/after');

  // Import preview: one row matches the existing entry's external id (update), one is brand new.
  const csv = `Date,Account,Account Type,Description,Reference,Debit,Credit,External Id\n` +
    `2026-03-01,1500,Asset,Equipment changed,INV-1,2000,0,GL-${suffix}-100\n` +
    `2026-03-05,6400,Expense,Depreciation,INV-2,0,250,GL-${suffix}-200\n`;
  const previewForm = new FormData();
  previewForm.append('file', new Blob([csv], { type: 'text/csv' }), 'gl.csv');
  const glPreview = await (await fetch(`${baseUrl}/api/books/GAAP/gl-entries/import/preview`, { method: 'POST', headers: companyHeaders(companyA), body: previewForm })).json();
  if (glPreview.summary.new !== 1 || glPreview.summary.update !== 1) {
    throw new Error(`Import preview misclassified rows: ${JSON.stringify(glPreview.summary)}`);
  }

  // Commit with merge: insert the new row, update the matched one.
  const glCommit = await request('/api/books/GAAP/gl-entries/import/commit', {
    method: 'POST', headers: companyHeaders(companyA), body: JSON.stringify({ rows: glPreview.rows, strategy: 'merge' })
  });
  if (glCommit.inserted !== 1 || glCommit.updated !== 1) throw new Error(`Import commit applied wrong counts: ${JSON.stringify(glCommit)}`);

  // Export CSV + Excel.
  const glCsv = await fetch(`${baseUrl}/api/books/GAAP/gl-entries/export?year=2026&format=csv`, { headers: companyHeaders(companyA) });
  if (!(glCsv.headers.get('content-type') || '').includes('text/csv')) throw new Error('GL CSV export was not text/csv');
  if (!(await glCsv.text()).startsWith('Date,Account')) throw new Error('GL CSV header was malformed');
  const glXlsx = await fetch(`${baseUrl}/api/books/GAAP/gl-entries/export?year=2026&format=xlsx`, { headers: companyHeaders(companyA) });
  if (!(glXlsx.headers.get('content-type') || '').includes('spreadsheet')) throw new Error('GL Excel export had the wrong content-type');

  // Per-entry change history (create + update events with before/after).
  const glHistory = (await request(`/api/books/GAAP/gl-entries/${glEntry.id}/history`, { headers: companyHeaders(companyA) })).history;
  if (glHistory.length < 2 || !glHistory.some((h) => h.action === 'update' && h.before && h.after)) {
    throw new Error('GL entry history did not return the create + update timeline');
  }
  // History is company-scoped (company B can't read company A's entry history).
  const crossHistory = await fetch(`${baseUrl}/api/books/GAAP/gl-entries/${glEntry.id}/history`, { headers: companyHeaders(companyB) });
  if (crossHistory.status !== 404) throw new Error('Cross-company GL history should 404');

  // Isolation: company B's GAAP book sees none of company A's entries.
  const glB = (await request('/api/books/GAAP/gl-entries', { headers: companyHeaders(companyB) })).entries;
  if (glB.some((e) => e.id === glEntry.id)) throw new Error('Company B sees company A GL entries');

  // ---- GL chart of accounts + posting defaults -------------------------------
  const seededChart = await request('/api/gl-accounts', { headers: companyHeaders(companyA) });
  if (seededChart.accounts.length < 5 || !seededChart.defaults.asset_account) throw new Error('Chart of accounts / posting defaults were not seeded');

  const newAcct = (await request('/api/gl-accounts', {
    method: 'POST', headers: companyHeaders(companyA),
    body: JSON.stringify({ code: `1501-${suffix}`, name: 'Equipment — Co A', type: 'asset', normal_balance: 'debit' })
  })).account;
  if (!newAcct || newAcct.entity_id !== companyA) throw new Error('GL account was not created/scoped');
  await request(`/api/gl-accounts/${newAcct.id}`, { method: 'PUT', headers: companyHeaders(companyA), body: JSON.stringify({ name: 'Equipment — revised' }) });

  // Point the asset-cost posting default at the new account and confirm the ledger posts to it.
  await request('/api/gl-account-defaults', { method: 'PUT', headers: companyHeaders(companyA), body: JSON.stringify({ asset_account: `1501-${suffix}` }) });
  const ledgerA = (await request('/api/books/GAAP/ledger?year=2026', { headers: companyHeaders(companyA) })).ledger;
  if (!ledgerA.accounts.some((a) => a.account === `1501-${suffix}` && a.type === 'Asset cost')) {
    throw new Error('Ledger did not use the configured default asset account');
  }

  // GL account + defaults changes are audited.
  const acctAudit = await request('/api/audit-logs?entity_type=gl_account', { headers: auth });
  if (acctAudit.total < 2) throw new Error('GL account changes were not audited');
  if ((await request('/api/audit-logs?entity_type=gl_account_defaults', { headers: auth })).total < 1) {
    throw new Error('GL account-default change was not audited');
  }

  // Isolation: company B has its own seeded chart, not company A's custom account.
  const acctsB = (await request('/api/gl-accounts', { headers: companyHeaders(companyB) })).accounts;
  if (!acctsB.length) throw new Error('Company B chart of accounts was not seeded');
  if (acctsB.some((a) => a.code === `1501-${suffix}`)) throw new Error('Company B sees company A GL account');

  // ---- Application (Winston) event logs --------------------------------------
  const appLog = await request('/api/app-logs', { headers: auth });
  if (!appLog.logs.length) throw new Error('Application logs were empty');
  if (!appLog.categories.includes('http')) throw new Error('Application logs did not capture HTTP events');
  const logSample = appLog.logs[0];
  if (!logSample.timestamp || !logSample.level || !logSample.category) throw new Error('Application log entry was missing fields');
  const httpLogs = await request('/api/app-logs?category=http', { headers: auth });
  if (!httpLogs.logs.length || httpLogs.logs.some((l) => l.category !== 'http')) throw new Error('Application log category filter failed');
  // Requires the admin.logs capability — a viewer-role user is refused.
  const logForbidden = await fetch(`${baseUrl}/api/app-logs`, { headers: limitedAuth });
  if (logForbidden.status !== 403) throw new Error('Application logs should require the admin.logs capability');

  // ---- Period close (month-end / year-end) -----------------------------------
  // Run the whole close in company A explicitly (bare `auth` resolves to the admin's first company,
  // which isn't deterministically company A). Seed a depreciating GAAP asset so depreciation is non-zero.
  const aHeaders = companyHeaders(companyA);
  await request('/api/assets', {
    method: 'POST', headers: aHeaders,
    body: JSON.stringify({
      asset_id: `CLZ-${suffix}`, description: 'Close-test machine', category: 'Computer Equipment',
      acquisition_cost: 12000, acquired_date: '2026-01-10', in_service_date: '2026-01-10', useful_life_months: 60,
      books: [{ book_type: 'GAAP', active: true, depreciation_method: 'straight_line', convention: 'half_year', useful_life_months: 60, cost: 12000 }]
    })
  });

  const startClose = await request('/api/close-periods/start', {
    method: 'POST', headers: aHeaders,
    body: JSON.stringify({ book: 'GAAP', period_type: 'month', fiscal_year: 2026, period_month: 3 })
  });
  const closeId = startClose.period.id;
  if (startClose.period.status !== 'in_progress') throw new Error('Close period did not start');

  const postedSource = `close:${closeId}`;
  const closeEntries = async () => (await request('/api/books/GAAP/gl-entries?year=2026', { headers: aHeaders })).entries.filter((e) => e.source === postedSource);

  // Post depreciation → balanced JE (Dr expense = Cr accumulated), tagged close:<id>.
  const dep = (await request(`/api/close-periods/${closeId}/post-depreciation`, { method: 'POST', headers: aHeaders })).summary;
  if (!(dep.debit > 0) || dep.debit !== dep.credit) throw new Error(`Depreciation JE not balanced: ${JSON.stringify(dep)}`);
  const firstPost = await closeEntries();
  if (!firstPost.length) throw new Error('Close did not post GL entries');

  // Idempotent re-post: same entry count, not doubled.
  await request(`/api/close-periods/${closeId}/post-depreciation`, { method: 'POST', headers: aHeaders });
  if ((await closeEntries()).length !== firstPost.length) throw new Error('Depreciation re-post was not idempotent');

  // Reconcile, then mark the reconcile step done (acknowledging any opening variance).
  const recon = (await request(`/api/close-periods/${closeId}/reconcile`, { method: 'POST', headers: aHeaders })).reconciliation;
  if (!recon.lines || recon.lines.length !== 3) throw new Error('Reconciliation result malformed');
  await request(`/api/close-periods/${closeId}/steps/reconcile`, { method: 'POST', headers: aHeaders, body: JSON.stringify({ done: true, acknowledged: true, notes: 'opening balance' }) });

  // Finalize → period closed.
  const finalized = (await request(`/api/close-periods/${closeId}/finalize`, { method: 'POST', headers: aHeaders, body: JSON.stringify({ acknowledge_variance: true }) })).period;
  if (finalized.status !== 'closed') throw new Error('Finalize did not close the period');

  // Lock: a backdated GL entry into the closed month is rejected (409).
  const lockedPost = await fetch(`${baseUrl}/api/books/GAAP/gl-entries`, {
    method: 'POST', headers: { ...aHeaders, 'Content-Type': 'application/json' },
    body: JSON.stringify({ entry_date: '2026-03-15', account: '6400', debit: 10, credit: 0 })
  });
  if (lockedPost.status !== 409) throw new Error(`Backdated GL post into a closed period should be 409, got ${lockedPost.status}`);

  // Reopen → close JEs reversed, status in_progress, and the backdated write now succeeds.
  const reopened = (await request(`/api/close-periods/${closeId}/reopen`, { method: 'POST', headers: aHeaders })).period;
  if (reopened.status !== 'in_progress') throw new Error('Reopen did not reset status');
  if ((await closeEntries()).length !== 0) throw new Error('Reopen did not reverse the close postings');
  await request('/api/books/GAAP/gl-entries', { method: 'POST', headers: aHeaders, body: JSON.stringify({ entry_date: '2026-03-15', account: '6400', debit: 10, credit: 0 }) });

  // Every close action is audited.
  const closeAudit = await request('/api/audit-logs?entity_type=close_period', { headers: auth });
  for (const action of ['start', 'post_depreciation', 'finalize', 'reopen']) {
    if (!closeAudit.rows.some((r) => r.action === action)) throw new Error(`Close action not audited: ${action}`);
  }

  // Isolation: company B cannot read company A's close period.
  const crossClose = await fetch(`${baseUrl}/api/close-periods/${closeId}`, { headers: companyHeaders(companyB) });
  if (crossClose.status !== 404) throw new Error('Cross-company close period should 404');

  // Capitalize WIP: a Construction-in-progress asset moves into service + posts a JE.
  const cip = (await request('/api/assets', {
    method: 'POST', headers: aHeaders,
    body: JSON.stringify({ asset_id: `WIP-${suffix}`, description: 'Build in progress', category: 'Computer Equipment', acquisition_cost: 5000, status: 'cip', acquired_date: '2026-02-01' })
  })).asset;
  const capped = await request(`/api/close-periods/${closeId}/wip/${cip.id}/capitalize`, { method: 'POST', headers: aHeaders, body: JSON.stringify({ in_service_date: '2026-06-01' }) });
  if (capped.asset.status !== 'in_service') throw new Error('WIP capitalization did not place the asset in service');

  // Year close: finalize runs the roll-forward to Retained Earnings.
  const yearClose = (await request('/api/close-periods/start', { method: 'POST', headers: aHeaders, body: JSON.stringify({ book: 'GAAP', period_type: 'year', fiscal_year: 2026 }) })).period;
  await request(`/api/close-periods/${yearClose.id}/post-depreciation`, { method: 'POST', headers: aHeaders });
  await request(`/api/close-periods/${yearClose.id}/reconcile`, { method: 'POST', headers: aHeaders });
  for (const key of ['final_month_close', 'reconcile', 'physical_inventory']) {
    await request(`/api/close-periods/${yearClose.id}/steps/${key}`, { method: 'POST', headers: aHeaders, body: JSON.stringify({ done: true, acknowledged: true }) });
  }
  await request(`/api/close-periods/${yearClose.id}/finalize`, { method: 'POST', headers: aHeaders, body: JSON.stringify({ acknowledge_variance: true }) });
  const reEntries = (await request('/api/books/GAAP/gl-entries?year=2026', { headers: aHeaders })).entries
    .filter((e) => e.source === `close:${yearClose.id}` && e.account_type === 'Retained earnings');
  if (!reEntries.length) throw new Error('Year-end roll-forward did not post a Retained Earnings entry');

  // ---- Dashboard designer: per-user layout + depreciation aggregator ----------
  const freshLayout = await request('/api/dashboard/layout', { headers: auth });
  if (freshLayout.layout !== null && !Array.isArray(freshLayout.layout)) throw new Error('Dashboard layout endpoint returned an unexpected shape');
  const myLayout = [
    { i: 'portfolio-stats-1', type: 'portfolio-stats', x: 0, y: 0, w: 12, h: 3 },
    { i: 'asset-scanner-1', type: 'asset-scanner', x: 0, y: 3, w: 4, h: 5 }
  ];
  await request('/api/dashboard/layout', { method: 'PUT', headers: auth, body: JSON.stringify({ layout: myLayout }) });
  const savedLayout = (await request('/api/dashboard/layout', { headers: auth })).layout;
  if (!Array.isArray(savedLayout) || savedLayout.length !== 2 || savedLayout[0].type !== 'portfolio-stats') throw new Error('Dashboard layout did not persist for the user');
  // Per-user isolation: a different user has an independent (empty) layout.
  const otherLayout = (await request('/api/dashboard/layout', { headers: limitedAuth })).layout;
  if (Array.isArray(otherLayout) && otherLayout.length) throw new Error('Dashboard layout leaked across users');

  // Depreciation aggregator (company A has a depreciating GAAP asset from the close block).
  const deprAgg = await request('/api/dashboard/depreciation?year=2026', { headers: aHeaders });
  if (!Array.isArray(deprAgg.byBook) || !deprAgg.byBook.some((b) => b.book === 'GAAP')) throw new Error('Dashboard depreciation aggregator missing byBook/GAAP');
  if (!Array.isArray(deprAgg.byCategory)) throw new Error('Dashboard depreciation aggregator missing byCategory');

  // ---- MACRS mid-quarter 40% auto-test (IRS Pub 946) --------------------------
  // Q4-heavy portfolio (80% of basis placed in service in Q4) → mid-quarter convention is mandatory and
  // must be applied automatically: the Q4 asset's first-year MACRS depreciation should be the mid-quarter
  // amount (8000 × 0.40 × 0.125 = 400), not the half-year amount (8000 × 0.40 × 0.5 = 1600).
  const macrsBook = (cost) => ({ book_type: 'FEDERAL', active: true, depreciation_method: 'macrs_gds_200db_5', convention: 'half_year', useful_life_months: 60, bonus_percent: 0, section_179_amount: 0, cost });
  await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `MQ1-${suffix}`, description: 'Mid-quarter Q1', category: 'Computer Equipment', acquisition_cost: 2000, acquired_date: '2029-02-15', in_service_date: '2029-02-15', useful_life_months: 60, books: [macrsBook(2000)] }) });
  await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `MQ4-${suffix}`, description: 'Mid-quarter Q4', category: 'Computer Equipment', acquisition_cost: 8000, acquired_date: '2029-11-15', in_service_date: '2029-11-15', useful_life_months: 60, books: [macrsBook(8000)] }) });
  const mqReport = (await request('/api/reports/run', { method: 'POST', headers: aHeaders, body: JSON.stringify({ type: 'depreciation_expense', options: { book: 'FEDERAL', year: 2029 } }) })).report;
  const mqRow = mqReport.rows.find((r) => r.asset_id === `MQ4-${suffix}`);
  if (!mqRow) throw new Error('Mid-quarter test asset missing from the depreciation report');
  if (Math.abs(mqRow.depreciation - 400) > 1) throw new Error(`Mid-quarter convention not auto-applied: expected ~400 (mid-quarter), got ${mqRow.depreciation}`);

  // Verify the §179 limits were updated to the IRS Pub 946 (2026) figures.
  const s179 = (await request('/api/section-179-limits', { headers: auth })).limits;
  const y2026 = s179.find((l) => l.tax_year === 2026);
  if (!y2026 || Number(y2026.base_limit) !== 2560000 || Number(y2026.phaseout_threshold) !== 4090000) {
    throw new Error(`2026 §179 limit should be 2,560,000 / 4,090,000, got ${JSON.stringify(y2026)}`);
  }

  // ---- §280F passenger-auto cap + listed-property >50% ADS test (IRS Pub 946) ---
  const macrsBook5 = (cost) => ({ book_type: 'FEDERAL', active: true, depreciation_method: 'macrs_gds_200db_5', convention: 'half_year', useful_life_months: 60, bonus_percent: 0, section_179_amount: 0, cost });
  // $90k luxury auto, 100% business, 2024 → year-1 MACRS would be 18,000 but §280F caps it to 12,400.
  await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `AUTO-${suffix}`, description: 'Luxury auto', category: 'Computer Equipment', acquisition_cost: 90000, acquired_date: '2024-06-01', in_service_date: '2024-06-01', useful_life_months: 60, business_use_percent: 100, passenger_auto: true, books: [macrsBook5(90000)] }) });
  // Listed property at 40% business use → forced ADS straight-line (basis 10k×40% / 5yr × half-year = 400).
  await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `LIST-${suffix}`, description: 'Listed low-use', category: 'Computer Equipment', acquisition_cost: 10000, acquired_date: '2024-06-01', in_service_date: '2024-06-01', useful_life_months: 60, business_use_percent: 40, listed_property: true, books: [macrsBook5(10000)] }) });
  const depr2024 = (await request('/api/reports/run', { method: 'POST', headers: aHeaders, body: JSON.stringify({ type: 'depreciation_expense', options: { book: 'FEDERAL', year: 2024 } }) })).report;
  const autoRow = depr2024.rows.find((r) => r.asset_id === `AUTO-${suffix}`);
  if (!autoRow || Math.abs(autoRow.depreciation - 12400) > 1) throw new Error(`§280F auto cap not applied: expected 12,400, got ${autoRow && autoRow.depreciation}`);
  const listRow = depr2024.rows.find((r) => r.asset_id === `LIST-${suffix}`);
  if (!listRow || Math.abs(listRow.depreciation - 400) > 1) throw new Error(`Listed ≤50% ADS not applied: expected 400, got ${listRow && listRow.depreciation}`);
  if (!(await request('/api/section-280f-limits', { headers: auth })).limits.some((l) => l.pis_year === 2024)) throw new Error('§280F limits not seeded');

  // ---- §179 taxable-income limitation + carryover ----------------------------
  const s179Book = (amt) => ({ book_type: 'FEDERAL', active: true, depreciation_method: 'macrs_gds_200db_7', useful_life_months: 84, section_179_amount: amt, cost: amt });
  await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `S179A-${suffix}`, description: '179 A', category: 'Computer Equipment', acquisition_cost: 1500000, acquired_date: '2031-03-01', in_service_date: '2031-03-01', useful_life_months: 84, books: [s179Book(1500000)] }) });
  await request('/api/assets', { method: 'POST', headers: aHeaders, body: JSON.stringify({ asset_id: `S179B-${suffix}`, description: '179 B', category: 'Computer Equipment', acquisition_cost: 1500000, acquired_date: '2031-04-01', in_service_date: '2031-04-01', useful_life_months: 84, books: [s179Book(1500000)] }) });
  // Elected 3,000,000; 2031 dollar limit 2,560,000; taxable income 1,000,000 → allowed 1,000,000, carry 2,000,000.
  const limSaved = (await request('/api/section-179/limitation', { method: 'PUT', headers: aHeaders, body: JSON.stringify({ book: 'FEDERAL', year: 2031, taxable_income: 1000000 }) })).summary;
  if (limSaved.elected !== 3000000) throw new Error(`§179 elected wrong: ${limSaved.elected}`);
  if (limSaved.dollar_allowed !== 2560000) throw new Error(`§179 dollar limit wrong: ${limSaved.dollar_allowed}`);
  if (limSaved.allowed_deduction !== 1000000) throw new Error(`§179 income limitation wrong: ${limSaved.allowed_deduction}`);
  if (limSaved.carryover_to_next !== 2000000) throw new Error(`§179 carryover wrong: ${limSaved.carryover_to_next}`);
  const lim2032 = (await request('/api/section-179/limitation?book=FEDERAL&year=2032', { headers: aHeaders })).summary;
  if (lim2032.prior_carryover !== 2000000) throw new Error(`§179 carryover did not chain to next year: ${lim2032.prior_carryover}`);

  console.log('Smoke test passed');
}

main()
  .catch((error) => {
    console.error(error.message);
    process.exitCode = 1;
  })
  .finally(() => {
    child.kill('SIGTERM');
    for (const suffix of ['', '-wal', '-shm']) {
      try {
        fs.rmSync(`${dbPath}${suffix}`, { force: true });
      } catch {
        // best-effort cleanup of the temp database
      }
    }
  });
