/**
 * The construct registry — source of truth, seeded into the Construct table.
 *
 * Derived directly from notes/01-targets.md and notes/04-reference-tables.md.
 * The whole value of the source data is that it splits "math" into Concepts &
 * Procedures vs Mathematical Practices, and "ELA" into Reading & Listening vs
 * Writing & Research. Everything here reports out by construct for that reason.
 *
 * Rule 1: every construct declares at least two dimensions. A one-dimension
 * construct is a holistic score wearing a costume.
 */

export type Subject = 'MATH' | 'ELA' | 'SCIENCE' | 'SPIRITUAL'
export type Track = 'ACADEMIC' | 'SPIRITUAL'
export type DimensionScale = 'BINARY' | 'POINTS'

export interface DimensionDef {
  code: string
  label: string
  /** Goes verbatim into the grader prompt as the question this dimension answers. */
  question: string
  scale: DimensionScale
  maxValue: number
}

export interface ConstructDef {
  code: string
  label: string
  subject: Subject
  /** The CAASPP reporting area this maps onto, where one exists. */
  reportArea: string | null
  description: string
  /** Why it's in the project — which Target it serves. */
  rationale: string
  dimensions: DimensionDef[]
}

// ── Dimension sets ───────────────────────────────────────────────────────────
// Named exactly as notes/07-ai-grading.md Rule 1 names them. Do not rename
// without migrating GradingScore, or the history stops lining up.

/**
 * Math reasoning. These three exist because "Mathematical Practices" bundles
 * Problem Solving & Modeling with Communicating Reasoning, and the state report
 * cannot say which one is weak. These can.
 */
const MATH_REASONING_DIMENSIONS: DimensionDef[] = [
  {
    code: 'answer_correct',
    label: 'Answer',
    question: 'Did they get the right answer?',
    scale: 'BINARY',
    maxValue: 1,
  },
  {
    code: 'strategy_valid',
    label: 'Strategy',
    question:
      'Did they choose an appropriate approach for this problem, even if they made an arithmetic slip carrying it out?',
    scale: 'POINTS',
    maxValue: 3,
  },
  {
    code: 'reasoning_quality',
    label: 'Reasoning',
    question:
      'Is the explanation complete and logically sound — does it actually justify the conclusion?',
    scale: 'POINTS',
    maxValue: 3,
  },
]

/**
 * ELA. Target 2 exists specifically to find out whether Abel's gap is
 * comprehension or written production. A holistic score answers neither.
 */
const ELA_DIMENSIONS: DimensionDef[] = [
  {
    code: 'comprehension_correct',
    label: 'Comprehension',
    question: 'Is the underlying idea right — did they understand the text?',
    scale: 'POINTS',
    maxValue: 3,
  },
  {
    code: 'expression_quality',
    label: 'Expression',
    question:
      'Is it clearly written, organised, and mechanically sound? This is the one place surface features are legitimately part of the score.',
    scale: 'POINTS',
    maxValue: 3,
  },
]

/**
 * The spiritual track scores TWO things and stops there: did he get it right,
 * and does he understand what it means.
 *
 * What is deliberately NOT scored: how he lives, what he believes, how sincere
 * he seems, whether he applies it. Those are not things this app should be
 * putting a number on. A low number on remainders is a study plan; a low number
 * on a child's walk with God is something else entirely, and no parent asked
 * for that. The measurement stops at knowledge and comprehension.
 */
const SPIRITUAL_DIMENSIONS: DimensionDef[] = [
  {
    code: 'recall_correct',
    label: 'Knows it',
    question:
      'Does he have the facts right — the people, the events, the order, what the passage or concept actually says?',
    scale: 'POINTS',
    maxValue: 3,
  },
  {
    code: 'understanding_quality',
    label: 'Understands it',
    question:
      'Can he say what it MEANS in his own words, rather than repeating a phrase back? Judge the understanding shown, never the belief behind it.',
    scale: 'POINTS',
    maxValue: 3,
  },
]

/** Science probes: does the idea hold up, and can they reason from evidence. */
const SCIENCE_DIMENSIONS: DimensionDef[] = [
  {
    code: 'comprehension_correct',
    label: 'Understanding',
    question: 'Is the science idea correct?',
    scale: 'POINTS',
    maxValue: 3,
  },
  {
    code: 'reasoning_quality',
    label: 'Reasoning',
    question:
      'Do they reason from the evidence or observation given, rather than asserting a remembered fact?',
    scale: 'POINTS',
    maxValue: 3,
  },
]

export const CONSTRUCTS: ConstructDef[] = [
  // ── Target 1 · highest priority. Abel Gr7 + Jeremiah Gr5. ──────────────────
  {
    code: 'MATH_PRACTICES',
    label: 'Mathematical Practices',
    subject: 'MATH',
    reportArea: 'Mathematical Practices',
    description:
      'Reading a non-routine problem, deciding what math it calls for, modelling a real situation, and defending an answer in words.',
    rationale:
      'Target 1. Abel and Jeremiah both came back Above standard on Concepts & Procedures and At-or-near on Mathematical Practices — two boys, two grades apart, identical split. Highest-leverage target in the project: one intervention, two kids.',
    dimensions: MATH_REASONING_DIMENSIONS,
  },
  {
    code: 'MATH_CONCEPTS_PROCEDURES',
    label: 'Concepts & Procedures (control)',
    subject: 'MATH',
    reportArea: 'Concepts and Procedures',
    description:
      'Straight computation and procedure. Present ONLY as the matched control half of a modelling pair — never as practice in its own right.',
    rationale:
      'Target 1 design note. Pair each modelling item with a computation-only item testing the same underlying skill: right on computation + wrong on modelling means the gap is comprehension and framing, not math. That distinction is exactly what the state report cannot make. Both boys are already Above standard here, so this is an ANTI-GOAL as a practice target — building drill on it is the single most likely way this project goes wrong.',
    dimensions: MATH_REASONING_DIMENSIONS,
  },

  // ── Target 2 · Abel's ELA. ────────────────────────────────────────────────
  {
    code: 'ELA_READING_COMPREHENSION',
    label: 'Reading comprehension',
    subject: 'ELA',
    reportArea: 'Reading and Listening',
    description:
      'Inference and central-idea work on grade-level nonfiction and dense argumentative text.',
    rationale:
      'Target 2. i-Ready puts Abel at the 91st percentile in reading; the state test puts his ELA at ~63rd. The two instruments genuinely disagree and neither can adjudicate it. Ask the SAME question as multiple choice and as constructed response on the SAME passage: strong on MC and weak on CR means the gap is written production, not comprehension — and the whole intervention changes.',
    dimensions: ELA_DIMENSIONS,
  },
  {
    code: 'ELA_WRITTEN_EXPRESSION',
    label: 'Written expression',
    subject: 'ELA',
    reportArea: 'Writing and Research',
    description:
      'Claim → evidence → reasoning structure, revision, and multi-source synthesis.',
    rationale:
      "Target 2. Abel's Writing & Research has never once reached Above standard in the three years it has been reported. The chronic one. Diagnose further whether it is mechanics, organisation, or stamina.",
    dimensions: ELA_DIMENSIONS,
  },
  {
    code: 'ELA_LISTENING',
    label: 'Listening',
    subject: 'ELA',
    reportArea: 'Reading and Listening',
    description:
      'Comprehension of material delivered by ear rather than by eye.',
    rationale:
      '"Reading and Listening" bundles a listening claim, and nothing in any boy\'s record tests that modality at all. Worth a small probe for Abel, and it is the one modality never tested on Samuel.',
    dimensions: ELA_DIMENSIONS,
  },

  // ── Target 3 · Samuel Gr9. ────────────────────────────────────────────────
  {
    code: 'MATH_ABOVE_LEVEL',
    label: 'Above-level math',
    subject: 'MATH',
    reportArea: null,
    description:
      'Non-routine problems past grade level: no taught algorithm, pitched at or beyond his actual course. AMC 8 / AMC 10 style.',
    rationale:
      'Target 3. Samuel scored 2845 against a Grade 8 ceiling of ~2860 and placed at Grade 9 on i-Ready while in Grade 8 — another grade-level test tells you nothing. He also has NO state test until Grade 11, so custom assessment is effectively the only signal on him for two years. Goal is challenge and information, not remediation; frame it to him that way.',
    dimensions: MATH_REASONING_DIMENSIONS,
  },

  // ── Target 3 + 4 · Science. ───────────────────────────────────────────────
  {
    code: 'SCI_LIFE',
    label: 'Life Sciences',
    subject: 'SCIENCE',
    reportArea: 'Life Sciences',
    description: 'Grade-band life science: structure/function, ecosystems, inheritance, energy flow.',
    rationale:
      "Target 3: Samuel's Life Sciences came back Near standard on BOTH CAST administrations (Grade 5 and Grade 8) — the only thing soft twice in his whole record. A targeted probe is cheap and would confirm or clear it. Target 4: part of Jeremiah's first science baseline.",
    dimensions: SCIENCE_DIMENSIONS,
  },
  {
    code: 'SCI_PHYSICAL',
    label: 'Physical Sciences',
    subject: 'SCIENCE',
    reportArea: 'Physical Sciences',
    description: 'Grade-band physical science: matter, forces and motion, energy transfer.',
    rationale:
      'Target 4. Jeremiah takes CAST for the first time in spring 2027 and has no science data at all — no baseline, no claim breakdown, nothing.',
    dimensions: SCIENCE_DIMENSIONS,
  },
  {
    code: 'SCI_EARTH_SPACE',
    label: 'Earth & Space Sciences',
    subject: 'SCIENCE',
    reportArea: 'Earth and Space Sciences',
    description: 'Grade-band earth and space science: systems, weather and climate, Earth in the solar system.',
    rationale: 'Target 4. Same as Life and Physical — completing Jeremiah\'s pre-2027 baseline.',
    dimensions: SCIENCE_DIMENSIONS,
  },
]

// ── Spiritual track ─────────────────────────────────────────────────────────
// Added 2026-08-28 at Eric's request. This is outside the original brief, which
// was built entirely around gaps the 2026 CAASPP results revealed — there is no
// external instrument here, no percentile, and no cut score. Eric decides what
// matters, via the Topic list he maintains.

CONSTRUCTS.push(
  {
    code: 'BIBLE_NARRATIVE',
    label: 'Bible stories',
    subject: 'SPIRITUAL',
    reportArea: null,
    description:
      'The narrative itself: who, what happened, in what order, and what the account shows.',
    rationale:
      'The ground floor. A boy cannot think about a concept drawn from a passage he does not know. Pitched per grade — Jeremiah meeting a story for the first time and Samuel tracing why it is told that way are different items.',
    dimensions: SPIRITUAL_DIMENSIONS,
  },
  {
    code: 'BIBLE_CONCEPTS',
    label: 'Bible concepts',
    subject: 'SPIRITUAL',
    reportArea: null,
    description:
      'The doctrinal and conceptual material: salvation, the Triune God, spirit and soul and body, the church, the Word.',
    rationale:
      "Follows the Lesson Book levels Eric named. The measured thing is whether he can say what a concept means in his own words rather than repeat a phrase — a boy can carry the right vocabulary a long way without it meaning anything to him yet, and that is exactly what this is for.",
    dimensions: SPIRITUAL_DIMENSIONS,
  },
  {
    code: 'MINISTRY_LESSONS',
    label: 'Life lessons',
    subject: 'SPIRITUAL',
    reportArea: null,
    description:
      'The practical lessons: calling on the Lord, prayer, reading the Word, fellowship, dealing with conscience.',
    rationale:
      'From the Life Lessons material. Scored on whether he understands what the practice IS and why it is taught — never on whether he does it, which is not the app\'s business.',
    dimensions: SPIRITUAL_DIMENSIONS,
  },
)

export const CONSTRUCTS_BY_CODE: Record<string, ConstructDef> = Object.fromEntries(
  CONSTRUCTS.map((c) => [c.code, c]),
)

/**
 * Which constructs each boy's sets may draw from, in priority order, straight
 * from notes/01-targets.md. The generator picks from this list; it cannot
 * invent a construct.
 */
/** Which track each construct belongs to. */
export const CONSTRUCT_TRACK: Record<string, Track> = Object.fromEntries(
  CONSTRUCTS.map((c) => [c.code, c.subject === 'SPIRITUAL' ? 'SPIRITUAL' : 'ACADEMIC']),
) as Record<string, Track>

/** All three boys take the whole spiritual track; grade level does the pitching. */
export const SPIRITUAL_CONSTRUCTS = ['BIBLE_NARRATIVE', 'BIBLE_CONCEPTS', 'MINISTRY_LESSONS']

export const STUDENT_CONSTRUCTS: Record<string, string[]> = {
  abel: [
    'MATH_PRACTICES',
    'MATH_CONCEPTS_PROCEDURES', // control half only
    'ELA_READING_COMPREHENSION',
    'ELA_WRITTEN_EXPRESSION',
    'ELA_LISTENING',
  ],
  jeremiah: [
    'MATH_PRACTICES',
    'MATH_CONCEPTS_PROCEDURES', // control half only
    'SCI_LIFE',
    'SCI_PHYSICAL',
    'SCI_EARTH_SPACE',
  ],
  samuel: ['MATH_ABOVE_LEVEL', 'ELA_READING_COMPREHENSION', 'ELA_LISTENING', 'SCI_LIFE'],
}

/**
 * Response mode per subject — open question 2b, answered 2026-08-28.
 * Handwritten photo for math (the work IS the artifact: steps, diagrams).
 * Typed for ELA (expression_quality is scored; handwriting would confound it).
 */
export function defaultResponseMode(subject: Subject): 'TYPED' | 'PHOTO' {
  return subject === 'MATH' ? 'PHOTO' : 'TYPED'
}

/** The constructs a given boy's set may draw from, for one track. */
export function constructsFor(slug: string, track: Track): string[] {
  if (track === 'SPIRITUAL') return SPIRITUAL_CONSTRUCTS
  return STUDENT_CONSTRUCTS[slug] ?? []
}

/** Items per construct per set. Small and targeted — fatigue destroys the signal. */
export const ITEMS_PER_CONSTRUCT = { min: 8, max: 12 } as const
