import { createHash, randomUUID } from 'node:crypto';
import type { ExtensionAPI, ExtensionContext } from '@earendil-works/pi-coding-agent';

const STATE_ENTRY_TYPE = 'mosaic-goal-state';
const CONTEXT_MESSAGE_TYPE = 'mosaic-goal-context';
const CONTINUATION_MESSAGE_TYPE = 'mosaic-goal-continuation';
const GOAL_REPORT_TOOL = 'mosaic_goal_report';
const STATUS_KEY = 'mosaic-goal';
const STATE_VERSION = 1 as const;
const MAX_STATEMENT_LENGTH = 8_000;
const MAX_SUMMARY_LENGTH = 2_000;
const MAX_EVIDENCE_ITEMS = 20;
const MAX_EVIDENCE_LENGTH = 1_000;
const MAX_NEXT_STEP_LENGTH = 2_000;
const DEFAULT_MAX_TURNS = 40;
const DEFAULT_MAX_NO_PROGRESS = 6;
const REQUIRED_VERIFICATION_PASSES = 2;
const DEFERRED_CONTINUATION_MS = 10;
const REDACTED_SECRET = '[REDACTED-SECRET]';
const PRIVATE_KEY_PATTERN =
  /-----BEGIN(?: [A-Z0-9]+)* PRIVATE KEY(?: BLOCK)?-----[\s\S]*?(?:-----END(?: [A-Z0-9]+)* PRIVATE KEY(?: BLOCK)?-----|$)/g;
const CREDENTIAL_URL_PATTERN = /\b([A-Za-z][A-Za-z0-9+.-]*:\/\/)[^\s/:]+:[^\s/@]+@/g;
const AUTHORIZATION_PATTERN =
  /(\b(?:authorization|proxy-authorization)\s*[:=]\s*)(bearer|basic)\s+[^\s,;]+/gi;
const STANDALONE_AUTH_PATTERN = /\b(bearer|basic)\s+[A-Za-z0-9._~+/=-]{16,}/gi;
const JWT_PATTERN = /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\b/g;
const KNOWN_SECRET_PATTERN =
  /\b(?:AKIA[0-9A-Z]{16}|ASIA[0-9A-Z]{16}|AIza[0-9A-Za-z_-]{35}|gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|glpat-[A-Za-z0-9_-]{20,}|npm_[A-Za-z0-9]{20,}|sk-(?:ant-(?:api\d{2}-)?|proj-)?[A-Za-z0-9_-]{20,}|(?:sk|rk)_(?:live|test)_[A-Za-z0-9]{16,}|xox[baprs]-[A-Za-z0-9-]{10,}|hf_[A-Za-z0-9]{20,})\b/g;
const SENSITIVE_ASSIGNMENT_PATTERN =
  /((?:["']?(?:[a-z0-9]+[_-])*(?:api[_-]?key|access[_-]?token|auth(?:orization)?[_-]?token|client[_-]?secret|password|passwd|secret(?:[_-]?access[_-]?key)?|private[_-]?key|database[_-]?url|token|cookie|set[_-]?cookie)["']?)\s*)((?:=|:)\s*)("[^"\r\n]*"|'[^'\r\n]*'|[^\s,;]+)/gi;

const GOAL_PHASES = [
  'active',
  'verifying',
  'paused',
  'blocked',
  'achieved',
  'cancelled',
  'exhausted',
] as const;
const REPORT_STATUSES = ['continue', 'achieved', 'blocked'] as const;
const CHECK_SOURCES = ['command', 'turn', 'compact', 'restore', 'report'] as const;

type GoalPhase = (typeof GOAL_PHASES)[number];
type GoalReportStatus = (typeof REPORT_STATUSES)[number];
type GoalCheckSource = (typeof CHECK_SOURCES)[number];

interface GoalReport {
  status: GoalReportStatus;
  summary: string;
  evidence: string[];
  nextStep?: string;
  fingerprint: string;
  reportedAt: string;
}

interface GoalState {
  version: typeof STATE_VERSION;
  goalId: string;
  statement: string;
  phase: GoalPhase;
  startedAt: string;
  updatedAt: string;
  turnCount: number;
  reportCount: number;
  verificationPasses: number;
  requiredVerificationPasses: number;
  noProgressReports: number;
  maxTurns: number;
  maxNoProgressReports: number;
  compactionCount: number;
  lastCheckSource: GoalCheckSource;
  lastCheckAt: string;
  lastCheckOutcome: string;
  lastProgressFingerprint?: string;
  lastReport?: GoalReport;
  stopReason?: string;
}

interface GoalReportInput {
  status: GoalReportStatus;
  summary: string;
  evidence: string[];
  nextStep?: string;
}

interface GoalLimits {
  maxTurns: number;
  maxNoProgressReports: number;
}

interface GoalCommand {
  action: 'set' | 'status' | 'pause' | 'resume' | 'cancel' | 'help';
  value: string;
}

const GoalReportParameters = {
  type: 'object',
  properties: {
    status: {
      type: 'string',
      enum: REPORT_STATUSES,
      description:
        'continue while work remains, achieved only with completion evidence, or blocked',
    },
    summary: {
      type: 'string',
      minLength: 1,
      maxLength: MAX_SUMMARY_LENGTH,
      description: 'Concise progress or completion assessment',
    },
    evidence: {
      type: 'array',
      items: { type: 'string', minLength: 1, maxLength: MAX_EVIDENCE_LENGTH },
      maxItems: MAX_EVIDENCE_ITEMS,
      description: 'Concrete observations, commands, tests, or artifacts supporting the status',
    },
    nextStep: {
      type: 'string',
      minLength: 1,
      maxLength: MAX_NEXT_STEP_LENGTH,
      description: 'The next concrete action when work remains',
    },
  },
  required: ['status', 'summary', 'evidence'],
  additionalProperties: false,
} as const;

function nowIso(): string {
  return new Date().toISOString();
}

function shouldRedactSensitiveAssignment(separator: string, rawValue: string): boolean {
  if (separator.trim() === '=') return true;
  const quoted =
    (rawValue.startsWith('"') && rawValue.endsWith('"')) ||
    (rawValue.startsWith("'") && rawValue.endsWith("'"));
  if (quoted || rawValue.includes('://')) return true;
  if (/^[a-f0-9]{20,}$/i.test(rawValue)) return true;
  return (
    rawValue.length >= 20 &&
    /[A-Za-z]/.test(rawValue) &&
    /\d/.test(rawValue) &&
    /[-_./+=]/.test(rawValue)
  );
}

function redactSensitiveText(value: string): string {
  let redacted = value.replace(PRIVATE_KEY_PATTERN, REDACTED_SECRET);
  redacted = redacted.replace(
    CREDENTIAL_URL_PATTERN,
    (_match: string, prefix: string): string => `${prefix}${REDACTED_SECRET}@`,
  );
  redacted = redacted.replace(
    AUTHORIZATION_PATTERN,
    (_match: string, prefix: string, scheme: string): string =>
      `${prefix}${scheme} ${REDACTED_SECRET}`,
  );
  redacted = redacted.replace(
    STANDALONE_AUTH_PATTERN,
    (_match: string, scheme: string): string => `${scheme} ${REDACTED_SECRET}`,
  );
  redacted = redacted.replace(JWT_PATTERN, REDACTED_SECRET);
  redacted = redacted.replace(KNOWN_SECRET_PATTERN, REDACTED_SECRET);
  return redacted.replace(
    SENSITIVE_ASSIGNMENT_PATTERN,
    (_match: string, prefix: string, separator: string, rawValue: string): string =>
      shouldRedactSensitiveAssignment(separator, rawValue)
        ? `${prefix}${separator}${REDACTED_SECRET}`
        : _match,
  );
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === 'object' && value !== null && !Array.isArray(value);
}

function isOneOf<const TValue extends string>(
  value: unknown,
  allowed: readonly TValue[],
): value is TValue {
  return (
    typeof value === 'string' && allowed.some((candidate: TValue): boolean => candidate === value)
  );
}

function parseBoundedInteger(
  value: string | undefined,
  fallback: number,
  minimum: number,
  maximum: number,
): number {
  if (value === undefined || !/^\d+$/.test(value.trim())) return fallback;
  const parsed = Number.parseInt(value, 10);
  if (!Number.isSafeInteger(parsed) || parsed < minimum || parsed > maximum) return fallback;
  return parsed;
}

function readGoalLimits(env: NodeJS.ProcessEnv): GoalLimits {
  return {
    maxTurns: parseBoundedInteger(env['MOSAIC_GOAL_MAX_TURNS'], DEFAULT_MAX_TURNS, 1, 500),
    maxNoProgressReports: parseBoundedInteger(
      env['MOSAIC_GOAL_MAX_NO_PROGRESS'],
      DEFAULT_MAX_NO_PROGRESS,
      1,
      100,
    ),
  };
}

function parseStringArray(value: unknown): string[] | undefined {
  if (!Array.isArray(value) || value.length > MAX_EVIDENCE_ITEMS) return undefined;
  const result: string[] = [];
  for (const item of value) {
    if (typeof item !== 'string') return undefined;
    const normalized = item.trim();
    if (normalized.length === 0 || normalized.length > MAX_EVIDENCE_LENGTH) return undefined;
    result.push(normalized);
  }
  return result;
}

function parseGoalReport(value: unknown): GoalReport | undefined {
  if (!isRecord(value)) return undefined;
  if (!isOneOf(value['status'], REPORT_STATUSES)) return undefined;
  if (
    typeof value['summary'] !== 'string' ||
    value['summary'].length === 0 ||
    value['summary'].length > MAX_SUMMARY_LENGTH
  ) {
    return undefined;
  }
  const evidence = parseStringArray(value['evidence']);
  if (evidence === undefined) return undefined;
  if (typeof value['fingerprint'] !== 'string' || !/^[a-f0-9]{64}$/.test(value['fingerprint'])) {
    return undefined;
  }
  if (
    typeof value['reportedAt'] !== 'string' ||
    value['reportedAt'].length === 0 ||
    value['reportedAt'].length > 64
  ) {
    return undefined;
  }
  const nextStepValue = value['nextStep'];
  let nextStep: string | undefined;
  if (nextStepValue !== undefined) {
    if (
      typeof nextStepValue !== 'string' ||
      nextStepValue.length === 0 ||
      nextStepValue.length > MAX_NEXT_STEP_LENGTH
    ) {
      return undefined;
    }
    nextStep = nextStepValue;
  }
  return {
    status: value['status'],
    summary: value['summary'],
    evidence,
    ...(nextStep === undefined ? {} : { nextStep }),
    fingerprint: value['fingerprint'],
    reportedAt: value['reportedAt'],
  };
}

function readIntegerField(
  value: Record<string, unknown>,
  field: string,
  minimum: number,
  maximum: number = Number.MAX_SAFE_INTEGER,
): number | undefined {
  const candidate = value[field];
  if (
    !Number.isSafeInteger(candidate) ||
    typeof candidate !== 'number' ||
    candidate < minimum ||
    candidate > maximum
  ) {
    return undefined;
  }
  return candidate;
}

function parseGoalState(value: unknown): GoalState | undefined {
  if (!isRecord(value) || value['version'] !== STATE_VERSION) return undefined;
  if (
    typeof value['goalId'] !== 'string' ||
    value['goalId'].length === 0 ||
    value['goalId'].length > 128
  ) {
    return undefined;
  }
  if (
    typeof value['statement'] !== 'string' ||
    value['statement'].length === 0 ||
    value['statement'].length > MAX_STATEMENT_LENGTH
  ) {
    return undefined;
  }
  if (!isOneOf(value['phase'], GOAL_PHASES)) return undefined;
  if (!isOneOf(value['lastCheckSource'], CHECK_SOURCES)) return undefined;
  if (
    typeof value['startedAt'] !== 'string' ||
    value['startedAt'].length > 64 ||
    typeof value['updatedAt'] !== 'string' ||
    value['updatedAt'].length > 64
  ) {
    return undefined;
  }
  if (
    typeof value['lastCheckAt'] !== 'string' ||
    value['lastCheckAt'].length > 64 ||
    typeof value['lastCheckOutcome'] !== 'string' ||
    value['lastCheckOutcome'].length > 256
  ) {
    return undefined;
  }

  const turnCount = readIntegerField(value, 'turnCount', 0, 500);
  const reportCount = readIntegerField(value, 'reportCount', 0, 100_000);
  const verificationPasses = readIntegerField(
    value,
    'verificationPasses',
    0,
    REQUIRED_VERIFICATION_PASSES,
  );
  const requiredVerificationPasses = readIntegerField(
    value,
    'requiredVerificationPasses',
    REQUIRED_VERIFICATION_PASSES,
    REQUIRED_VERIFICATION_PASSES,
  );
  const noProgressReports = readIntegerField(value, 'noProgressReports', 0, 100);
  const maxTurns = readIntegerField(value, 'maxTurns', 1, 500);
  const maxNoProgressReports = readIntegerField(value, 'maxNoProgressReports', 1, 100);
  const compactionCount = readIntegerField(value, 'compactionCount', 0, 100_000);
  if (
    turnCount === undefined ||
    reportCount === undefined ||
    verificationPasses === undefined ||
    requiredVerificationPasses === undefined ||
    noProgressReports === undefined ||
    maxTurns === undefined ||
    maxNoProgressReports === undefined ||
    compactionCount === undefined
  ) {
    return undefined;
  }

  const lastReportValue = value['lastReport'];
  const lastReport = lastReportValue === undefined ? undefined : parseGoalReport(lastReportValue);
  if (lastReportValue !== undefined && lastReport === undefined) return undefined;
  const lastProgressFingerprintValue = value['lastProgressFingerprint'];
  let lastProgressFingerprint: string | undefined;
  if (lastProgressFingerprintValue !== undefined) {
    if (
      typeof lastProgressFingerprintValue !== 'string' ||
      !/^[a-f0-9]{64}$/.test(lastProgressFingerprintValue)
    ) {
      return undefined;
    }
    lastProgressFingerprint = lastProgressFingerprintValue;
  }
  const stopReasonValue = value['stopReason'];
  let stopReason: string | undefined;
  if (stopReasonValue !== undefined) {
    if (typeof stopReasonValue !== 'string' || stopReasonValue.length > MAX_SUMMARY_LENGTH) {
      return undefined;
    }
    stopReason = stopReasonValue;
  }

  return {
    version: STATE_VERSION,
    goalId: value['goalId'],
    statement: value['statement'],
    phase: value['phase'],
    startedAt: value['startedAt'],
    updatedAt: value['updatedAt'],
    turnCount,
    reportCount,
    verificationPasses,
    requiredVerificationPasses,
    noProgressReports,
    maxTurns,
    maxNoProgressReports,
    compactionCount,
    lastCheckSource: value['lastCheckSource'],
    lastCheckAt: value['lastCheckAt'],
    lastCheckOutcome: value['lastCheckOutcome'],
    ...(lastProgressFingerprint === undefined ? {} : { lastProgressFingerprint }),
    ...(lastReport === undefined ? {} : { lastReport }),
    ...(stopReason === undefined ? {} : { stopReason }),
  };
}

function copyReport(report: GoalReport): GoalReport {
  return {
    ...report,
    evidence: [...report.evidence],
  };
}

function copyState(state: GoalState): GoalState {
  return {
    ...state,
    ...(state.lastReport === undefined ? {} : { lastReport: copyReport(state.lastReport) }),
  };
}

function parseGoalReportInput(value: unknown): GoalReportInput {
  if (!isRecord(value) || !isOneOf(value['status'], REPORT_STATUSES)) {
    throw new Error('Goal report status must be continue, achieved, or blocked.');
  }
  if (typeof value['summary'] !== 'string') throw new Error('Goal report summary is required.');
  const summary = value['summary'].trim();
  if (summary.length === 0 || summary.length > MAX_SUMMARY_LENGTH) {
    throw new Error(`Goal report summary must be 1-${MAX_SUMMARY_LENGTH} characters.`);
  }
  const evidence = parseStringArray(value['evidence']);
  if (evidence === undefined) throw new Error('Goal report evidence is invalid.');
  if (value['status'] === 'achieved' && evidence.length === 0) {
    throw new Error('An achieved goal report requires concrete evidence.');
  }
  const rawNextStep = value['nextStep'];
  if (rawNextStep !== undefined && typeof rawNextStep !== 'string') {
    throw new Error('Goal report nextStep must be text.');
  }
  const nextStep = typeof rawNextStep === 'string' ? rawNextStep.trim() : undefined;
  if (nextStep !== undefined && (nextStep.length === 0 || nextStep.length > MAX_NEXT_STEP_LENGTH)) {
    throw new Error(`Goal report nextStep must be 1-${MAX_NEXT_STEP_LENGTH} characters.`);
  }
  return redactGoalReportInput({
    status: value['status'],
    summary,
    evidence,
    ...(nextStep === undefined ? {} : { nextStep }),
  });
}

function reportFingerprint(report: GoalReportInput): string {
  const normalized = JSON.stringify({
    summary: report.summary.trim().toLowerCase(),
    evidence: report.evidence.map((item: string): string => item.trim().toLowerCase()),
    nextStep: report.nextStep?.trim().toLowerCase() ?? '',
  });
  return createHash('sha256').update(normalized).digest('hex');
}

function redactGoalReportInput(report: GoalReportInput): GoalReportInput {
  return {
    status: report.status,
    summary: redactSensitiveText(report.summary),
    evidence: report.evidence.map(redactSensitiveText),
    ...(report.nextStep === undefined ? {} : { nextStep: redactSensitiveText(report.nextStep) }),
  };
}

function redactGoalReport(report: GoalReport): GoalReport {
  const redactedInput = redactGoalReportInput(report);
  return {
    ...redactedInput,
    fingerprint: reportFingerprint(redactedInput),
    reportedAt: report.reportedAt,
  };
}

function redactGoalState(state: GoalState): GoalState {
  const lastReport =
    state.lastReport === undefined ? undefined : redactGoalReport(state.lastReport);
  const lastProgressFingerprint =
    lastReport !== undefined && state.lastProgressFingerprint === state.lastReport?.fingerprint
      ? lastReport.fingerprint
      : state.lastProgressFingerprint;
  return {
    ...state,
    statement: redactSensitiveText(state.statement),
    ...(lastReport === undefined ? {} : { lastReport }),
    ...(lastProgressFingerprint === undefined ? {} : { lastProgressFingerprint }),
    ...(state.stopReason === undefined
      ? {}
      : { stopReason: redactSensitiveText(state.stopReason) }),
  };
}

function goalStateContainsSensitiveText(state: GoalState): boolean {
  const textValues = [state.statement];
  if (state.stopReason !== undefined) textValues.push(state.stopReason);
  if (state.lastReport !== undefined) {
    textValues.push(state.lastReport.summary, ...state.lastReport.evidence);
    if (state.lastReport.nextStep !== undefined) textValues.push(state.lastReport.nextStep);
  }
  return textValues.some((value: string): boolean => redactSensitiveText(value) !== value);
}

function isContinuingPhase(phase: GoalPhase): boolean {
  return phase === 'active' || phase === 'verifying';
}

function canReplaceGoal(state: GoalState | undefined): boolean {
  return state === undefined || state.phase === 'achieved' || state.phase === 'cancelled';
}

function parseGoalCommand(args: string): GoalCommand {
  const trimmed = args.trim();
  if (trimmed.length === 0) return { action: 'help', value: '' };
  const separator = trimmed.indexOf(' ');
  const first = (separator === -1 ? trimmed : trimmed.slice(0, separator)).toLowerCase();
  const value = separator === -1 ? '' : trimmed.slice(separator + 1).trim();
  if (first === 'set') return { action: 'set', value };
  if (first === 'status') return { action: 'status', value };
  if (first === 'pause') return { action: 'pause', value };
  if (first === 'resume') return { action: 'resume', value };
  if (first === 'cancel' || first === 'clear') return { action: 'cancel', value };
  if (first === 'help') return { action: 'help', value };
  return { action: 'set', value: trimmed };
}

function formatStatus(state: GoalState | undefined): string {
  if (state === undefined) return 'No Mosaic goal is set. Use /goal set <statement>.';
  const lines = [
    `Goal ${state.goalId}`,
    `Phase: ${state.phase}`,
    `Turns: ${state.turnCount}/${state.maxTurns}`,
    `Verification: ${state.verificationPasses}/${state.requiredVerificationPasses}`,
    `No-progress reports: ${state.noProgressReports}/${state.maxNoProgressReports}`,
    `Compactions checked: ${state.compactionCount}`,
    `Goal: ${state.statement}`,
  ];
  if (state.lastReport !== undefined) {
    lines.push(`Latest report: ${state.lastReport.status} — ${state.lastReport.summary}`);
    if (state.lastReport.evidence.length > 0) {
      lines.push(
        'Evidence:',
        ...state.lastReport.evidence.map((item: string): string => `- ${item}`),
      );
    }
    if (state.lastReport.nextStep !== undefined) {
      lines.push(`Next step: ${state.lastReport.nextStep}`);
    }
  }
  if (state.stopReason !== undefined) lines.push(`Stopped: ${state.stopReason}`);
  return lines.join('\n');
}

function buildGoalContract(state: GoalState): string {
  const latest = state.lastReport;
  const verificationInstruction =
    state.phase === 'verifying'
      ? 'This is a verification pass. Re-inspect the actual result and rerun relevant checks; do not rely only on the prior claim.'
      : 'Continue making concrete progress toward the goal.';
  const lines = [
    '[MOSAIC GOAL LOOP v1]',
    `Goal ID: ${state.goalId}`,
    `Goal: ${state.statement}`,
    `Phase: ${state.phase}`,
    `Budget: turn ${state.turnCount}/${state.maxTurns}; repeated no-progress reports ${state.noProgressReports}/${state.maxNoProgressReports}.`,
    verificationInstruction,
    '',
    'Completion protocol:',
    `- Before ending the work cycle, call ${GOAL_REPORT_TOOL} as the only tool call in the final assistant response.`,
    '- Use status=continue whenever any requirement remains and provide the next concrete step.',
    '- Use status=achieved only when concrete evidence covers the entire stated goal.',
    '- Use status=blocked only for a genuine blocker that prevents meaningful progress.',
    `- Achievement requires ${state.requiredVerificationPasses} consecutive evidence-bearing reports; the first claim starts a separate verification pass.`,
    '- Never include secrets, tokens, credentials, private keys, or raw sensitive output in a report.',
    '- Do not ask routine permission to continue. The operator can pause or cancel with /goal.',
  ];
  if (latest !== undefined) {
    lines.push('', `Previous report: ${latest.status} — ${latest.summary}`);
    if (latest.nextStep !== undefined) lines.push(`Previous next step: ${latest.nextStep}`);
  }
  return lines.join('\n');
}

function continuationText(state: GoalState, reason: string): string {
  if (state.phase === 'verifying') {
    return `Goal ${state.goalId} requires a verification pass after ${reason}. Recheck the complete goal and report fresh evidence with ${GOAL_REPORT_TOOL}.`;
  }
  return `Goal remains active after ${reason}. Continue from the latest evidence and finish by calling ${GOAL_REPORT_TOOL}.`;
}

function toolResultIncludesGoalReport(toolResults: unknown): boolean {
  if (!Array.isArray(toolResults)) return false;
  return toolResults.some(
    (result: unknown): boolean => isRecord(result) && result['toolName'] === GOAL_REPORT_TOOL,
  );
}

function toolResultCount(toolResults: unknown): number {
  return Array.isArray(toolResults) ? toolResults.length : 0;
}

export default function registerGoalExtension(pi: ExtensionAPI): void {
  const limits = readGoalLimits(process.env);
  let state: GoalState | undefined;
  let continuationQueued = false;
  let deferredTimer: ReturnType<typeof setTimeout> | undefined;
  let lifecycleGeneration = 0;
  let reportRollbackState: GoalState | undefined;

  function updateStatus(ctx: ExtensionContext): void {
    if (state === undefined || state.phase === 'cancelled') {
      ctx.ui.setStatus(STATUS_KEY, undefined);
      return;
    }
    ctx.ui.setStatus(STATUS_KEY, `🎯 ${state.phase} ${state.turnCount}/${state.maxTurns}`);
  }

  function persist(nextState: GoalState, ctx: ExtensionContext): void {
    const redactedState = redactGoalState(nextState);
    state = copyState(redactedState);
    pi.appendEntry(STATE_ENTRY_TYPE, copyState(redactedState));
    updateStatus(ctx);
  }

  function clearDeferredTimer(): void {
    if (deferredTimer !== undefined) clearTimeout(deferredTimer);
    deferredTimer = undefined;
  }

  function queueContinuation(
    ctx: ExtensionContext,
    reason: string,
    trackDuplicate: boolean = true,
  ): void {
    if (state === undefined || !isContinuingPhase(state.phase)) return;
    if (ctx.hasPendingMessages()) return;
    if (trackDuplicate && continuationQueued) return;
    if (trackDuplicate) continuationQueued = true;
    const options = ctx.isIdle()
      ? { triggerTurn: true as const }
      : { triggerTurn: true as const, deliverAs: 'followUp' as const };
    pi.sendMessage(
      {
        customType: CONTINUATION_MESSAGE_TYPE,
        content: continuationText(state, reason),
        display: true,
      },
      options,
    );
  }

  function scheduleIdleContinuation(ctx: ExtensionContext, reason: string): void {
    clearDeferredTimer();
    const scheduledGeneration = lifecycleGeneration;
    deferredTimer = setTimeout((): void => {
      deferredTimer = undefined;
      if (scheduledGeneration !== lifecycleGeneration || !ctx.isIdle()) return;
      queueContinuation(ctx, reason);
    }, DEFERRED_CONTINUATION_MS);
  }

  function restoreState(ctx: ExtensionContext): void {
    state = undefined;
    let rejectedSensitiveState = false;
    for (const entry of ctx.sessionManager.getBranch()) {
      if (entry.type !== 'custom' || entry.customType !== STATE_ENTRY_TYPE) continue;
      const restoredState = parseGoalState(entry.data);
      if (restoredState === undefined) {
        state = undefined;
      } else if (goalStateContainsSensitiveText(restoredState)) {
        state = undefined;
        rejectedSensitiveState = true;
      } else if (rejectedSensitiveState) {
        state = undefined;
      } else {
        state = redactGoalState(restoredState);
      }
    }
    continuationQueued = false;
    updateStatus(ctx);
    if (rejectedSensitiveState) {
      ctx.ui.notify(
        'Mosaic goal state was not restored because persisted text matched a credential pattern. Remove the affected Pi session if it may contain a real secret, then set a new goal.',
        'warning',
      );
    }
  }

  function createGoal(statement: string, ctx: ExtensionContext): void {
    if (!canReplaceGoal(state)) {
      ctx.ui.notify(
        'A Mosaic goal already exists. Use /goal cancel before replacing it.',
        'warning',
      );
      return;
    }
    if (statement.length === 0 || statement.length > MAX_STATEMENT_LENGTH) {
      ctx.ui.notify(
        `Usage: /goal set <statement> (${MAX_STATEMENT_LENGTH.toLocaleString()} characters maximum).`,
        'warning',
      );
      return;
    }
    const timestamp = nowIso();
    const redactedStatement = redactSensitiveText(statement);
    persist(
      {
        version: STATE_VERSION,
        goalId: randomUUID(),
        statement: redactedStatement,
        phase: 'active',
        startedAt: timestamp,
        updatedAt: timestamp,
        turnCount: 0,
        reportCount: 0,
        verificationPasses: 0,
        requiredVerificationPasses: REQUIRED_VERIFICATION_PASSES,
        noProgressReports: 0,
        maxTurns: limits.maxTurns,
        maxNoProgressReports: limits.maxNoProgressReports,
        compactionCount: 0,
        lastCheckSource: 'command',
        lastCheckAt: timestamp,
        lastCheckOutcome: 'set',
      },
      ctx,
    );
    ctx.ui.notify(`Mosaic goal started: ${redactedStatement}`, 'info');
    if (ctx.isIdle()) queueContinuation(ctx, 'goal start', false);
  }

  pi.registerCommand('goal', {
    description: 'Set or control a persistent Mosaic goal loop',
    handler: async (args, ctx): Promise<void> => {
      const command = parseGoalCommand(args);
      if (command.action === 'help') {
        ctx.ui.notify(
          [
            'Mosaic goal commands:',
            '/goal set <statement> (or /goal <statement>)',
            '/goal status',
            '/goal pause [reason]',
            '/goal resume',
            '/goal cancel',
          ].join('\n'),
          'info',
        );
        return;
      }
      if (command.action === 'status') {
        ctx.ui.notify(formatStatus(state), 'info');
        return;
      }
      if (command.action === 'set') {
        createGoal(command.value, ctx);
        return;
      }
      if (state === undefined) {
        ctx.ui.notify('No Mosaic goal is set.', 'warning');
        return;
      }
      const timestamp = nowIso();
      if (command.action === 'pause') {
        if (!isContinuingPhase(state.phase)) {
          ctx.ui.notify(`Goal cannot be paused from phase ${state.phase}.`, 'warning');
          return;
        }
        persist(
          {
            ...state,
            phase: 'paused',
            updatedAt: timestamp,
            lastCheckSource: 'command',
            lastCheckAt: timestamp,
            lastCheckOutcome: 'paused',
            stopReason: command.value || 'Paused by operator.',
          },
          ctx,
        );
        clearDeferredTimer();
        continuationQueued = false;
        if (!ctx.isIdle()) ctx.abort();
        ctx.ui.notify('Mosaic goal paused.', 'info');
        return;
      }
      if (command.action === 'cancel') {
        persist(
          {
            ...state,
            phase: 'cancelled',
            updatedAt: timestamp,
            lastCheckSource: 'command',
            lastCheckAt: timestamp,
            lastCheckOutcome: 'cancelled',
            stopReason: 'Cancelled by operator.',
          },
          ctx,
        );
        clearDeferredTimer();
        continuationQueued = false;
        if (!ctx.isIdle()) ctx.abort();
        ctx.ui.notify('Mosaic goal cancelled.', 'info');
        return;
      }
      if (command.action === 'resume') {
        if (state.phase !== 'paused' && state.phase !== 'blocked' && state.phase !== 'exhausted') {
          ctx.ui.notify(`Goal cannot be resumed from phase ${state.phase}.`, 'warning');
          return;
        }
        persist(
          {
            ...state,
            phase: 'active',
            updatedAt: timestamp,
            turnCount: 0,
            verificationPasses: 0,
            noProgressReports: 0,
            lastProgressFingerprint: undefined,
            lastCheckSource: 'command',
            lastCheckAt: timestamp,
            lastCheckOutcome: 'resumed',
            stopReason: undefined,
          },
          ctx,
        );
        continuationQueued = false;
        ctx.ui.notify('Mosaic goal resumed with fresh bounded counters.', 'info');
        if (ctx.isIdle()) queueContinuation(ctx, 'operator resume', false);
      }
    },
  });

  pi.registerTool({
    name: GOAL_REPORT_TOOL,
    label: 'Mosaic Goal Report',
    description:
      'Report structured progress for the active Mosaic /goal loop. Call it as the sole final tool when a work cycle is ready to stop, continue, verify, or block.',
    promptSnippet: 'Report evidence-backed status for the active Mosaic goal loop',
    promptGuidelines: [
      'When a Mosaic goal is active, call mosaic_goal_report as the sole tool in the final assistant response for each work cycle.',
      'Use mosaic_goal_report status=achieved only when concrete evidence covers the entire active goal.',
    ],
    parameters: GoalReportParameters,
    async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
      if (state === undefined || !isContinuingPhase(state.phase)) {
        throw new Error(
          'No active Mosaic goal can accept a report. Use /goal set or /goal resume.',
        );
      }
      const input = parseGoalReportInput(params);
      const fingerprint = reportFingerprint(input);
      const timestamp = nowIso();
      const report: GoalReport = {
        ...input,
        fingerprint,
        reportedAt: timestamp,
      };
      reportRollbackState = copyState(state);

      let nextState: GoalState;
      if (input.status === 'continue') {
        const noProgressReports =
          fingerprint === state.lastProgressFingerprint ? state.noProgressReports + 1 : 1;
        const exhausted = noProgressReports >= state.maxNoProgressReports;
        nextState = {
          ...state,
          phase: exhausted ? 'exhausted' : 'active',
          updatedAt: timestamp,
          reportCount: state.reportCount + 1,
          verificationPasses: 0,
          noProgressReports,
          lastProgressFingerprint: fingerprint,
          lastReport: report,
          lastCheckSource: 'report',
          lastCheckAt: timestamp,
          lastCheckOutcome: exhausted ? 'no-progress-limit' : 'continue',
          ...(exhausted
            ? {
                stopReason: `Repeated no-progress report limit reached (${state.maxNoProgressReports}).`,
              }
            : { stopReason: undefined }),
        };
      } else if (input.status === 'achieved') {
        const verificationPasses = state.phase === 'verifying' ? state.verificationPasses + 1 : 1;
        const achieved = verificationPasses >= state.requiredVerificationPasses;
        nextState = {
          ...state,
          phase: achieved ? 'achieved' : 'verifying',
          updatedAt: timestamp,
          reportCount: state.reportCount + 1,
          verificationPasses,
          noProgressReports: 0,
          lastProgressFingerprint: fingerprint,
          lastReport: report,
          lastCheckSource: 'report',
          lastCheckAt: timestamp,
          lastCheckOutcome: achieved ? 'verified-achieved' : 'provisional-achieved',
          stopReason: undefined,
        };
      } else {
        nextState = {
          ...state,
          phase: 'blocked',
          updatedAt: timestamp,
          reportCount: state.reportCount + 1,
          verificationPasses: 0,
          noProgressReports: 0,
          lastProgressFingerprint: fingerprint,
          lastReport: report,
          lastCheckSource: 'report',
          lastCheckAt: timestamp,
          lastCheckOutcome: 'blocked',
          stopReason: input.summary,
        };
      }

      persist(nextState, ctx);
      if (nextState.phase === 'achieved') {
        ctx.ui.notify(`Mosaic goal verified.\n${formatStatus(nextState)}`, 'info');
      } else if (nextState.phase === 'verifying') {
        ctx.ui.notify(
          'Goal achievement is provisional; one verification pass is required.',
          'info',
        );
      } else if (nextState.phase === 'blocked' || nextState.phase === 'exhausted') {
        ctx.ui.notify(`Mosaic goal stopped in phase ${nextState.phase}.`, 'warning');
      }

      return {
        content: [
          {
            type: 'text',
            text:
              nextState.phase === 'achieved'
                ? 'Goal verification complete.'
                : `Goal report recorded; phase is ${nextState.phase}.`,
          },
        ],
        details: { state: copyState(nextState), report: copyReport(report) },
        terminate: true,
      };
    },
  });

  pi.on('context', async (event) => {
    const messages = event.messages.filter(
      (message) =>
        message.role !== 'custom' ||
        (message.customType !== CONTEXT_MESSAGE_TYPE &&
          message.customType !== CONTINUATION_MESSAGE_TYPE),
    );
    if (state === undefined || !isContinuingPhase(state.phase)) {
      return messages.length === event.messages.length ? undefined : { messages };
    }
    messages.push({
      role: 'custom',
      customType: CONTEXT_MESSAGE_TYPE,
      content: buildGoalContract(state),
      display: false,
      timestamp: Date.now(),
    });
    return { messages };
  });

  pi.on('turn_end', async (event, ctx) => {
    if (state === undefined || state.phase === 'paused' || state.phase === 'cancelled') return;
    const hasGoalReport = toolResultIncludesGoalReport(event.toolResults);
    if (!isContinuingPhase(state.phase) && !hasGoalReport) return;

    const timestamp = nowIso();
    let nextState = {
      ...state,
      updatedAt: timestamp,
      turnCount: state.turnCount + 1,
      lastCheckSource: 'turn' as const,
      lastCheckAt: timestamp,
      lastCheckOutcome: hasGoalReport ? 'reported' : 'checked-unreported',
    };

    if (
      hasGoalReport &&
      toolResultCount(event.toolResults) !== 1 &&
      reportRollbackState !== undefined
    ) {
      nextState = {
        ...reportRollbackState,
        phase: 'active',
        updatedAt: timestamp,
        turnCount: reportRollbackState.turnCount + 1,
        verificationPasses: 0,
        lastCheckSource: 'turn',
        lastCheckAt: timestamp,
        lastCheckOutcome: 'mixed-goal-report-rejected',
        stopReason: undefined,
      };
      ctx.ui.notify(
        `${GOAL_REPORT_TOOL} must be the only tool call in its final response; the mixed report was ignored.`,
        'warning',
      );
    }
    reportRollbackState = undefined;

    if (isContinuingPhase(nextState.phase) && nextState.turnCount >= nextState.maxTurns) {
      nextState = {
        ...nextState,
        phase: 'exhausted',
        lastCheckOutcome: 'max-turn-limit',
        stopReason: `Maximum autonomous turn limit reached (${nextState.maxTurns}).`,
      };
      persist(nextState, ctx);
      ctx.ui.notify('Mosaic goal exhausted its autonomous turn limit.', 'warning');
      ctx.abort();
      return;
    }
    persist(nextState, ctx);
  });

  pi.on('agent_start', async () => {
    continuationQueued = false;
    clearDeferredTimer();
  });

  pi.on('agent_settled', async (_event, ctx) => {
    if (state === undefined || !isContinuingPhase(state.phase)) return;
    queueContinuation(ctx, state.phase === 'verifying' ? 'the provisional claim' : 'agent settle');
  });

  pi.on('session_compact', async (event, ctx) => {
    if (state === undefined) return;
    const timestamp = nowIso();
    const wasContinuing = isContinuingPhase(state.phase);
    persist(
      {
        ...state,
        phase: wasContinuing ? 'active' : state.phase,
        updatedAt: timestamp,
        verificationPasses: wasContinuing ? 0 : state.verificationPasses,
        compactionCount: state.compactionCount + 1,
        lastCheckSource: 'compact',
        lastCheckAt: timestamp,
        lastCheckOutcome: `checked-${event.reason}`,
        ...(wasContinuing ? { stopReason: undefined } : {}),
      },
      ctx,
    );
    if (wasContinuing) scheduleIdleContinuation(ctx, `${event.reason} compaction`);
  });

  pi.on('session_start', async (_event, ctx) => {
    lifecycleGeneration += 1;
    clearDeferredTimer();
    restoreState(ctx);
    if (state !== undefined && isContinuingPhase(state.phase)) {
      const timestamp = nowIso();
      persist(
        {
          ...state,
          updatedAt: timestamp,
          lastCheckSource: 'restore',
          lastCheckAt: timestamp,
          lastCheckOutcome: 'session-start',
        },
        ctx,
      );
      scheduleIdleContinuation(ctx, 'session restore');
    }
  });

  pi.on('session_tree', async (_event, ctx) => {
    lifecycleGeneration += 1;
    clearDeferredTimer();
    restoreState(ctx);
    if (state !== undefined && isContinuingPhase(state.phase)) {
      const timestamp = nowIso();
      persist(
        {
          ...state,
          updatedAt: timestamp,
          lastCheckSource: 'restore',
          lastCheckAt: timestamp,
          lastCheckOutcome: 'tree-navigation',
        },
        ctx,
      );
      scheduleIdleContinuation(ctx, 'tree navigation');
    }
  });

  pi.on('session_shutdown', async (_event, ctx) => {
    lifecycleGeneration += 1;
    clearDeferredTimer();
    continuationQueued = false;
    ctx.ui.setStatus(STATUS_KEY, undefined);
  });
}
