// Fictional local policy simulator. No HTTP server, credentials, timers, or external requests.
import assert from 'node:assert/strict';

function recoveryDecision({ status, problemType, transportFailure = false, method = 'POST',
  replayGuaranteed = false, attempt = 1, maximumAttempts = 3, retryAfterSeconds }) {
  const replaySafe = ['GET', 'HEAD'].includes(method) || replayGuaranteed;
  if (!Number.isInteger(attempt) || attempt < 1 || !Number.isInteger(maximumAttempts) || maximumAttempts < 1) {
    throw new Error('Invalid attempt budget');
  }
  if (status >= 200 && status < 300 && !transportFailure) {
    return { action: status === 202 ? 'inspect-acceptance' : 'verify-result' };
  }
  if (transportFailure && !replaySafe) return { action: 'reconcile-unknown-outcome' };
  if (status === 401) return { action: 'repair-credential' };
  const rateLimited = status === 429 || (status === 403 && problemType === 'https://example.com/problems/rate-limit');
  if (status === 403 && !rateLimited) return { action: 'check-permission' };
  if (status === 404) return { action: 'check-resource-and-access' };
  if (status === 409 && problemType === 'https://example.com/problems/replay-conflict') return { action: 'recover-original-operation' };
  if (status === 409 && problemType === 'https://example.com/problems/stale-version') return { action: 'refresh-state-and-review' };
  if (status === 409) return { action: 'resolve-conflict' };
  if ([400, 422].includes(status)) return { action: 'correct-request' };
  if (transportFailure || rateLimited || status === 503) {
    if (!replaySafe) return { action: 'reconcile-unknown-outcome' };
    if (attempt >= maximumAttempts) return { action: 'stop-budget-exhausted' };
    const supplied = Number.isFinite(retryAfterSeconds) && retryAfterSeconds >= 0;
    const delaySeconds = supplied ? retryAfterSeconds : 2 ** (attempt - 1);
    // Longer required waits defer work; never shorten a server's minimum wait.
    if (delaySeconds > 60) return { action: 'defer-for-later', delaySeconds };
    return { action: 'retry-same-operation', delaySeconds };
  }
  return { action: 'inspect-unclassified-response' };
}

const problem = {
  type: 'https://example.com/problems/invalid-event',
  title: 'Invalid event',
  status: 422,
  detail: 'name must be a non-empty string',
  instance: '/requests/example-001',
  errors: [{ pointer: '/name', code: 'required' }],
};
const cases = [
  [{ status: 422 }, 'correct-request'],
  [{ status: 401 }, 'repair-credential'],
  [{ status: 403 }, 'check-permission'],
  [{ status: 404 }, 'check-resource-and-access'],
  [{ status: 409 }, 'resolve-conflict'],
  [{ status: 409, problemType: 'https://example.com/problems/replay-conflict' }, 'recover-original-operation'],
  [{ status: 409, problemType: 'https://example.com/problems/stale-version' }, 'refresh-state-and-review'],
  [{ status: 403, problemType: 'https://example.com/problems/rate-limit', method: 'GET', retryAfterSeconds: 5 }, 'retry-same-operation'],
  [{ transportFailure: true }, 'reconcile-unknown-outcome'],
  [{ status: 503 }, 'reconcile-unknown-outcome'],
  [{ status: 503, replayGuaranteed: true }, 'retry-same-operation'],
  [{ status: 429, method: 'GET', retryAfterSeconds: 12 }, 'retry-same-operation'],
  [{ status: 429, method: 'GET', retryAfterSeconds: 120 }, 'defer-for-later'],
  [{ status: 503, method: 'GET', attempt: 3 }, 'stop-budget-exhausted'],
  [{ status: 202 }, 'inspect-acceptance'],
  [{ status: 200 }, 'verify-result'],
  [{ status: 418 }, 'inspect-unclassified-response'],
];
for (const [input, expected] of cases) assert.equal(recoveryDecision(input).action, expected);
assert.equal(recoveryDecision({ status: 429, method: 'GET', retryAfterSeconds: 12 }).delaySeconds, 12);
assert.equal(recoveryDecision({ status: 429, method: 'GET', retryAfterSeconds: 120 }).delaySeconds, 120);
assert.equal(recoveryDecision({ status: 503, method: 'GET', attempt: 2 }).delaySeconds, 2);
assert.equal(problem.status, 422);
assert.equal(problem.errors[0].pointer, '/name');
assert.throws(() => recoveryDecision({ status: 503, attempt: 0 }));
const statuses = [503, 202];
const trace = statuses.map((status, index) => recoveryDecision({ status, replayGuaranteed: true, attempt: index + 1 }));
assert.deepEqual(trace.map(x => x.action), ['retry-same-operation', 'inspect-acceptance']);
process.stdout.write(JSON.stringify({ fictional: true, node: process.version, scenarioChecks: cases.length,
  additionalAssertions: 7, trace, problemExample: problem }, null, 2) + '\n');
