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

/**
 * CRM lead stages (SPEC.md §9).
 *
 * Stages mirror the admission funnel so lead-to-enrollment conversion is a
 * single query. Once an application exists, the lead's stage is advanced by the
 * application workflow rather than by hand, which is what keeps the funnel
 * numbers honest.
 *
 * LOST and DEFERRED both require a reason, so the "lost/deferred reasons"
 * analytic in SPEC.md §24 has real data behind it.
 */
export const LEAD_STAGES = [
  'NEW',
  'CONTACTED',
  'QUALIFIED',
  'APPLICATION_STARTED',
  'APPLICATION_SUBMITTED',
  'EXAM_SCHEDULED',
  'EXAM_COMPLETED',
  'INTERVIEW',
  'ACCEPTED',
  'ENROLLED',
  'DEFERRED',
  'LOST',
] as const;

export type LeadStage = (typeof LEAD_STAGES)[number];

/** Every lead transition needs the same permission; only labels differ. */
const rule = (label: string, requiresReason = false): TransitionRule => ({
  permission: 'lead.manage',
  label,
  ...(requiresReason ? { requiresReason: true } : {}),
});

const MARK_LOST = rule('Mark the lead as lost', true);
const DEFER = rule('Defer the lead', true);
const RE_ENGAGE = rule('Re-engage the lead');
const RECORD_ACCEPTANCE = rule('Record the acceptance');
const RECORD_INTERVIEW = rule('Record that an interview was scheduled');
const RECORD_APPLICATION_STARTED = rule('Record that an application was started');
/*
 * Reachable from every stage before it, not only from APPLICATION_STARTED.
 * A walk-in can enquire and apply in the same conversation, and staff
 * converting a fresh enquiry create a submitted application directly — with no
 * draft stage in between, because nobody is coming back to press submit. Making
 * the funnel insist on a stage that never happened would leave those leads
 * sitting at NEW while their application is already under review.
 */
const RECORD_APPLICATION_SUBMITTED = rule('Record the application submission');

const transitions: TransitionTable<LeadStage> = {
  NEW: {
    CONTACTED: rule('Mark the lead as contacted'),
    QUALIFIED: rule('Qualify the lead'),
    APPLICATION_STARTED: RECORD_APPLICATION_STARTED,
    APPLICATION_SUBMITTED: RECORD_APPLICATION_SUBMITTED,
    LOST: MARK_LOST,
  },
  CONTACTED: {
    QUALIFIED: rule('Qualify the lead'),
    APPLICATION_STARTED: RECORD_APPLICATION_STARTED,
    APPLICATION_SUBMITTED: RECORD_APPLICATION_SUBMITTED,
    DEFERRED: DEFER,
    LOST: MARK_LOST,
  },
  QUALIFIED: {
    APPLICATION_STARTED: RECORD_APPLICATION_STARTED,
    APPLICATION_SUBMITTED: RECORD_APPLICATION_SUBMITTED,
    DEFERRED: DEFER,
    LOST: MARK_LOST,
  },
  APPLICATION_STARTED: {
    APPLICATION_SUBMITTED: RECORD_APPLICATION_SUBMITTED,
    LOST: MARK_LOST,
  },
  APPLICATION_SUBMITTED: {
    EXAM_SCHEDULED: rule('Record the examination schedule'),
    INTERVIEW: RECORD_INTERVIEW,
    ACCEPTED: RECORD_ACCEPTANCE,
    LOST: MARK_LOST,
  },
  EXAM_SCHEDULED: {
    EXAM_COMPLETED: rule('Record the examination result'),
    LOST: MARK_LOST,
  },
  EXAM_COMPLETED: {
    INTERVIEW: RECORD_INTERVIEW,
    ACCEPTED: RECORD_ACCEPTANCE,
    LOST: MARK_LOST,
  },
  INTERVIEW: {
    ACCEPTED: RECORD_ACCEPTANCE,
    LOST: MARK_LOST,
  },
  ACCEPTED: {
    ENROLLED: rule('Record the enrollment'),
    DEFERRED: DEFER,
    LOST: MARK_LOST,
  },
  DEFERRED: {
    CONTACTED: RE_ENGAGE,
    LOST: MARK_LOST,
  },
  LOST: {
    // A lost lead can be revived; the original loss reason stays in history.
    CONTACTED: RE_ENGAGE,
  },
};

export const leadWorkflow: WorkflowDefinition<LeadStage> = {
  name: 'lead',
  initial: 'NEW',
  states: LEAD_STAGES,
  terminal: ['ENROLLED'],
  transitions,
};

/** Lead sources from SPEC.md §9. Seeded as data; new sources need no code. */
export const SEEDED_LEAD_SOURCES = [
  'WEBSITE',
  'FACEBOOK',
  'WALK_IN',
  'REFERRAL',
  'EVENT',
  'PHONE',
  'EMAIL',
  'OTHER',
] as const;

export type LeadSourceKey = (typeof SEEDED_LEAD_SOURCES)[number];

/**
 * Maps an application status onto the lead stage it implies, so converting or
 * advancing an application keeps the CRM funnel in step automatically.
 * Returning `null` means the application status implies no lead change.
 */
export const LEAD_STAGE_FOR_APPLICATION: Record<string, LeadStage | null> = {
  DRAFT: 'APPLICATION_STARTED',
  SUBMITTED: 'APPLICATION_SUBMITTED',
  UNDER_REVIEW: 'APPLICATION_SUBMITTED',
  SCREENING: 'APPLICATION_SUBMITTED',
  SCREENING_PASSED: 'APPLICATION_SUBMITTED',
  SCREENING_FAILED: null,
  EXAM_SCHEDULED: 'EXAM_SCHEDULED',
  EXAM_COMPLETED: 'EXAM_COMPLETED',
  FOR_EVALUATION: 'APPLICATION_SUBMITTED',
  EVALUATED: 'APPLICATION_SUBMITTED',
  REQUIREMENTS_PENDING: 'APPLICATION_SUBMITTED',
  REQUIREMENTS_COMPLETE: 'APPLICATION_SUBMITTED',
  FOR_INTERVIEW: 'INTERVIEW',
  PANEL_INTERVIEW_SCHEDULED: 'INTERVIEW',
  PANEL_INTERVIEW_COMPLETED: 'INTERVIEW',
  DEAN_INTERVIEW_SCHEDULED: 'INTERVIEW',
  DEAN_INTERVIEW_COMPLETED: 'INTERVIEW',
  WAITLISTED: null,
  ACCEPTED: 'ACCEPTED',
  DEFERRED: 'DEFERRED',
  ENROLLMENT_IN_PROGRESS: 'ACCEPTED',
  ENROLLED: 'ENROLLED',
  REJECTED: 'LOST',
  WITHDRAWN: 'LOST',
};
