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

/**
 * Grade submission lifecycle (SPEC.md §20).
 *
 * The invariant that matters most: a student never sees a grade before an
 * authorized office publishes it. DRAFT and SUBMITTED grades are invisible to
 * students, enforced in the repository projection rather than in the UI.
 *
 * Submission locks the sheet. Any later correction goes through a
 * GradeChangeRequest, which preserves the previous value in GradeHistory —
 * grades are never silently overwritten (SPEC.md §33.11).
 */
export const GRADE_SUBMISSION_STATUSES = [
  'NOT_STARTED',
  'DRAFT',
  'SUBMITTED',
  'RETURNED',
  'PUBLISHED',
  'LOCKED',
] as const;

export type GradeSubmissionStatus = (typeof GRADE_SUBMISSION_STATUSES)[number];

const submissionTransitions: TransitionTable<GradeSubmissionStatus> = {
  NOT_STARTED: {
    DRAFT: { permission: 'grade.encode', label: 'Begin encoding grades' },
  },

  DRAFT: {
    SUBMITTED: {
      permission: 'grade.submit',
      requires: ['ALL_GRADES_ENTERED'],
      label: 'Submit the grade sheet',
    },
  },

  SUBMITTED: {
    PUBLISHED: {
      permission: 'grade.publish',
      requires: ['GRADES_SUBMITTED'],
      label: 'Publish the grades to students',
    },
    RETURNED: {
      permission: 'grade.publish',
      requiresReason: true,
      label: 'Return the grade sheet to the faculty member',
    },
  },

  RETURNED: {
    DRAFT: { permission: 'grade.encode', label: 'Resume encoding grades' },
  },

  PUBLISHED: {
    LOCKED: {
      permission: 'grade.publish',
      label: 'Lock the grade sheet for the term',
    },
  },
};

export const gradeSubmissionWorkflow: WorkflowDefinition<GradeSubmissionStatus> = {
  name: 'grade sheet',
  initial: 'NOT_STARTED',
  states: GRADE_SUBMISSION_STATUSES,
  terminal: ['LOCKED'],
  transitions: submissionTransitions,
};

/** Statuses at which a student may see the grade. Nothing else is visible. */
export const STUDENT_VISIBLE_GRADE_STATUSES: readonly GradeSubmissionStatus[] = [
  'PUBLISHED',
  'LOCKED',
];

export function isGradeVisibleToStudent(status: GradeSubmissionStatus): boolean {
  return STUDENT_VISIBLE_GRADE_STATUSES.includes(status);
}

/** A submitted sheet is immutable except through a change request. */
export function isGradeSheetLocked(status: GradeSubmissionStatus): boolean {
  return status === 'SUBMITTED' || status === 'PUBLISHED' || status === 'LOCKED';
}

/**
 * Grade change request lifecycle. Required whenever a grade is altered after
 * submission, so the old value, the proposed value, the reason, the requester
 * and the approver are all retained (SPEC.md §20).
 */
export const GRADE_CHANGE_STATUSES = ['PENDING', 'APPROVED', 'REJECTED', 'CANCELLED'] as const;

export type GradeChangeStatus = (typeof GRADE_CHANGE_STATUSES)[number];

const changeTransitions: TransitionTable<GradeChangeStatus> = {
  PENDING: {
    APPROVED: {
      permission: 'grade.change_approve',
      requiresReason: true,
      label: 'Approve the grade change',
    },
    REJECTED: {
      permission: 'grade.change_approve',
      requiresReason: true,
      label: 'Reject the grade change',
    },
    CANCELLED: {
      permission: 'grade.change_request',
      requiresReason: true,
      label: 'Cancel the grade change request',
    },
  },
};

export const gradeChangeWorkflow: WorkflowDefinition<GradeChangeStatus> = {
  name: 'grade change request',
  initial: 'PENDING',
  states: GRADE_CHANGE_STATUSES,
  terminal: ['APPROVED', 'REJECTED', 'CANCELLED'],
  transitions: changeTransitions,
};
