import type { TransitionTable, WorkflowDefinition } from './machine.js';

/**
 * Document request lifecycle (SPEC.md §22).
 *
 * A request never auto-issues a document. RELEASED requires that an authorized
 * office has attached the prepared file, and that any fee has been settled —
 * both expressed as preconditions rather than assumed.
 */
export const DOCUMENT_REQUEST_STATUSES = [
  'SUBMITTED',
  'FOR_VERIFICATION',
  'FOR_PAYMENT',
  'PROCESSING',
  'READY_FOR_RELEASE',
  'RELEASED',
  'REJECTED',
  'CANCELLED',
] as const;

export type DocumentRequestStatus = (typeof DOCUMENT_REQUEST_STATUSES)[number];

const documentRequestTransitions: TransitionTable<DocumentRequestStatus> = {
  SUBMITTED: {
    FOR_VERIFICATION: {
      permission: 'document_request.process',
      label: 'Begin verifying the request',
    },
    REJECTED: {
      permission: 'document_request.process',
      requiresReason: true,
      label: 'Reject the request',
    },
    // The requester may cancel their own request while it is still untouched.
    CANCELLED: { requiresReason: true, label: 'Cancel the request' },
  },

  FOR_VERIFICATION: {
    FOR_PAYMENT: {
      permission: 'document_request.process',
      label: 'Send the request for payment',
    },
    PROCESSING: {
      permission: 'document_request.process',
      label: 'Begin processing the request',
    },
    REJECTED: {
      permission: 'document_request.process',
      requiresReason: true,
      label: 'Reject the request',
    },
    CANCELLED: { requiresReason: true, label: 'Cancel the request' },
  },

  FOR_PAYMENT: {
    PROCESSING: {
      permission: 'document_request.process',
      requires: ['PAYMENT_SETTLED'],
      label: 'Begin processing the request',
    },
    CANCELLED: { requiresReason: true, label: 'Cancel the request' },
  },

  PROCESSING: {
    READY_FOR_RELEASE: {
      permission: 'document_request.process',
      requires: ['DOCUMENT_ATTACHED'],
      label: 'Mark the document ready for release',
    },
    REJECTED: {
      permission: 'document_request.process',
      requiresReason: true,
      label: 'Reject the request',
    },
  },

  READY_FOR_RELEASE: {
    RELEASED: {
      permission: 'document_request.process',
      label: 'Release the document',
    },
  },
};

export const documentRequestWorkflow: WorkflowDefinition<DocumentRequestStatus> = {
  name: 'document request',
  initial: 'SUBMITTED',
  states: DOCUMENT_REQUEST_STATUSES,
  terminal: ['RELEASED', 'REJECTED', 'CANCELLED'],
  transitions: documentRequestTransitions,
};

/**
 * Clearance step lifecycle (SPEC.md §23).
 *
 * BLOCKED is a first-class state rather than an absence of clearance: the
 * office records why the student is blocked, so the student can see what to
 * resolve instead of visiting the office to find out.
 */
export const CLEARANCE_STEP_STATUSES = ['PENDING', 'CLEARED', 'BLOCKED', 'WAIVED'] as const;

export type ClearanceStepStatus = (typeof CLEARANCE_STEP_STATUSES)[number];

const clearanceStepTransitions: TransitionTable<ClearanceStepStatus> = {
  PENDING: {
    CLEARED: { permission: 'clearance.manage', label: 'Clear the student for this office' },
    BLOCKED: {
      permission: 'clearance.manage',
      requiresReason: true,
      label: 'Block clearance for this office',
    },
    WAIVED: {
      permission: 'clearance.manage',
      requiresReason: true,
      label: 'Waive this clearance step',
    },
  },

  BLOCKED: {
    CLEARED: { permission: 'clearance.manage', label: 'Clear the student for this office' },
    WAIVED: {
      permission: 'clearance.manage',
      requiresReason: true,
      label: 'Waive this clearance step',
    },
  },

  CLEARED: {
    // Reversible with a reason, e.g. an unreturned library book found later.
    BLOCKED: {
      permission: 'clearance.manage',
      requiresReason: true,
      label: 'Reinstate a block on this office',
    },
  },
};

export const clearanceStepWorkflow: WorkflowDefinition<ClearanceStepStatus> = {
  name: 'clearance step',
  initial: 'PENDING',
  states: CLEARANCE_STEP_STATUSES,
  terminal: [],
  transitions: clearanceStepTransitions,
};

/** Statuses that satisfy a clearance step. */
export const SATISFIED_CLEARANCE_STATUSES: readonly ClearanceStepStatus[] = ['CLEARED', 'WAIVED'];

export interface ClearanceStepInput {
  readonly status: ClearanceStepStatus;
  readonly isRequired: boolean;
}

export interface ClearanceSummary {
  readonly isComplete: boolean;
  readonly cleared: number;
  readonly requiredTotal: number;
  readonly blocked: number;
}

/** Whether every required clearance step has been cleared or waived. */
export function evaluateClearance(steps: readonly ClearanceStepInput[]): ClearanceSummary {
  const required = steps.filter((step) => step.isRequired);
  const cleared = required.filter((step) =>
    SATISFIED_CLEARANCE_STATUSES.includes(step.status),
  ).length;
  const blocked = required.filter((step) => step.status === 'BLOCKED').length;

  return {
    isComplete: required.length > 0 && cleared === required.length,
    cleared,
    requiredTotal: required.length,
    blocked,
  };
}
