/**
 * compareLogic encodes the expert-reviewed comprehension rules for the
 * compare screen: report-card summarisation (safeguarding never counted),
 * three-regime Ofsted display, one consistent admissions chip metric,
 * CI-based progress banding, verdict chips and dot-strip geometry.
 */

import {
  OFSTED_LEGACY_GRADES,
  admissionsForPhase,
  ofstedDisplay,
  progressBand,
  rcAreaLabel,
  stripPositions,
  summariseAdmissions,
  summariseReportCard,
  verdict,
} from '@/lib/compareLogic';
import type { OfstedInspection, SchoolAdmissions } from '@/lib/types';

function ofsted(partial: Partial<OfstedInspection>): OfstedInspection {
  return {
    framework: null,
    inspection_date: null,
    inspection_type: null,
    overall_effectiveness: null,
    quality_of_education: null,
    behaviour_attitudes: null,
    personal_development: null,
    leadership_management: null,
    early_years_provision: null,
    previous_overall: null,
    rc_safeguarding_met: null,
    rc_inclusion: null,
    rc_curriculum_teaching: null,
    rc_achievement: null,
    rc_attendance_behaviour: null,
    rc_personal_development: null,
    rc_leadership_governance: null,
    rc_early_years: null,
    rc_sixth_form: null,
    ...partial,
  };
}

const REPORT_CARD = {
  rc_achievement: { code: 2, label: 'Strong standard' },
  rc_curriculum_teaching: { code: 2, label: 'Strong standard' },
  rc_personal_development: { code: 2, label: 'Strong standard' },
  rc_leadership_governance: { code: 2, label: 'Strong standard' },
  rc_inclusion: { code: 3, label: 'Expected standard' },
  rc_early_years: { code: 3, label: 'Expected standard' },
  rc_attendance_behaviour: { code: 4, label: 'Needs attention' },
};

describe('summariseReportCard', () => {
  it('counts graded areas best-first and NAMES problem areas', () => {
    const s = summariseReportCard(
      ofsted({ report_card: REPORT_CARD, rc_safeguarding_met: true }),
    );
    expect(s.counts).toEqual([
      { label: 'Strong standard', count: 4 },
      { label: 'Expected standard', count: 2 },
    ]);
    expect(s.problems).toEqual([
      { areaLabel: 'Attendance & behaviour', label: 'Needs attention' },
    ]);
    expect(s.safeguarding).toBe('met');
    expect(s.allClear).toBe(false);
  });

  it('never counts safeguarding as a graded area', () => {
    const s = summariseReportCard(
      ofsted({
        report_card: { rc_achievement: { code: 3, label: 'Expected standard' } },
        rc_safeguarding_met: true,
      }),
    );
    const total = s.counts.reduce((n, c) => n + c.count, 0);
    expect(total).toBe(1);
  });

  it('is allClear when everything is Expected standard or better and safeguarding met', () => {
    const s = summariseReportCard(
      ofsted({
        report_card: {
          rc_achievement: { code: 3, label: 'Expected standard' },
          rc_inclusion: { code: 1, label: 'Exceptional' },
        },
        rc_safeguarding_met: true,
      }),
    );
    expect(s.allClear).toBe(true);
    expect(s.counts[0]).toEqual({ label: 'Exceptional', count: 1 });
  });

  it('passes labels through from the API — never invents wording', () => {
    const s = summariseReportCard(
      ofsted({ report_card: { rc_inclusion: { code: 4, label: 'Needs attention' } } }),
    );
    expect(JSON.stringify(s)).not.toContain('Attention needed');
  });
});

describe('ofstedDisplay', () => {
  it('prefers the report card over any legacy grade', () => {
    const d = ofstedDisplay(
      ofsted({ overall_effectiveness: 2, report_card: REPORT_CARD }),
    );
    expect(d.kind).toBe('report_card');
  });

  it('distinguishes graded from carried-forward grades', () => {
    const graded = ofstedDisplay(
      ofsted({ overall_effectiveness: 1, grade_source: 'graded' }),
    );
    expect(graded).toMatchObject({ kind: 'graded', gradeLabel: 'Outstanding', carriedForward: false });

    const carried = ofstedDisplay(
      ofsted({ overall_effectiveness: 2, grade_source: 'ungraded_carried_forward' }),
    );
    expect(carried).toMatchObject({ kind: 'carried_forward', gradeLabel: 'Good', carriedForward: true });
  });

  it('handles missing data', () => {
    expect(ofstedDisplay(null).kind).toBe('none');
    expect(ofstedDisplay(ofsted({})).kind).toBe('none');
  });

  it('identifies transitional inspections without overall grades', () => {
    const transitional = ofstedDisplay(
      ofsted({ overall_effectiveness: null, inspection_date: '2024-11-05' }),
    );
    expect(transitional.kind).toBe('transitional');
  });

  it('uses the four legacy grade words', () => {
    expect(OFSTED_LEGACY_GRADES).toEqual({
      1: 'Outstanding',
      2: 'Good',
      3: 'Requires improvement',
      4: 'Inadequate',
    });
  });
});

describe('rcAreaLabel', () => {
  it('maps rc keys to the mockups’ area labels', () => {
    expect(rcAreaLabel('rc_attendance_behaviour')).toBe('Attendance & behaviour');
    expect(rcAreaLabel('rc_curriculum_teaching')).toBe('Curriculum & teaching');
    expect(rcAreaLabel('rc_leadership_governance')).toBe('Leadership & governance');
  });
});

describe('summariseAdmissions', () => {
  function admissions(partial: Partial<SchoolAdmissions>): SchoolAdmissions {
    return {
      year: 202627,
      places_offered: null,
      total_applications: null,
      first_preference_offer_pct: null,
      oversubscribed: null,
      ...partial,
    };
  }

  it('97% → good chip with the mockup wording', () => {
    const s = summariseAdmissions(
      admissions({ first_preference_offer_pct: 96.98, total_applications: 457, places_offered: 180 }),
    );
    expect(s.chip).toEqual({ tone: 'good', text: '97% of first choices offered' });
    expect(s.interest).toBe('Named on 457 forms · 180 places');
  });

  it('73% → warn chip "Over 1 in 4 first choices missed out"', () => {
    const s = summariseAdmissions(admissions({ first_preference_offer_pct: 73.4 }));
    expect(s.chip).toEqual({ tone: 'warn', text: 'Over 1 in 4 first choices missed out' });
  });

  it('60% → "About 1 in 3 first choices missed out"', () => {
    const s = summariseAdmissions(admissions({ first_preference_offer_pct: 60 }));
    expect(s.chip).toEqual({ tone: 'warn', text: 'About 1 in 3 first choices missed out' });
  });

  it('44% (selective-scale demand) → "More than half of first choices missed out"', () => {
    const s = summariseAdmissions(admissions({ first_preference_offer_pct: 43.69 }));
    expect(s.chip).toEqual({ tone: 'warn', text: 'More than half of first choices missed out' });
  });

  it('100% → "All first choices offered"', () => {
    const s = summariseAdmissions(admissions({ first_preference_offer_pct: 100 }));
    expect(s.chip).toEqual({ tone: 'good', text: 'All first choices offered' });
  });

  it('no data → null chip and interest', () => {
    const s = summariseAdmissions(null);
    expect(s.chip).toBeNull();
    expect(s.interest).toBeNull();
  });
});

describe('admissionsForPhase', () => {
  const row = (year: number, school_phase: string | null): SchoolAdmissions =>
    ({ year, school_phase, places_offered: 100, total_applications: 200, first_preference_offer_pct: 80 }) as SchoolAdmissions;

  it('returns the latest round matching the active phase', () => {
    const data = {
      admissions: row(202627, 'Secondary'),
      admissions_history: [row(202526, 'Secondary'), row(202526, 'Primary'), row(202425, 'Primary')],
    };
    expect(admissionsForPhase(data, true)?.year).toBe(202627);
    expect(admissionsForPhase(data, false)?.year).toBe(202526);
    expect(admissionsForPhase(data, false)?.school_phase).toBe('Primary');
  });

  it("never substitutes the other phase's round (all-through with Year 7 data only)", () => {
    const data = {
      admissions: row(202627, 'Secondary'),
      admissions_history: [row(202526, 'Secondary')],
    };
    expect(admissionsForPhase(data, false)).toBeNull();
    expect(admissionsForPhase(data, true)?.year).toBe(202627);
  });

  it('uses untagged legacy rows only when no row carries a phase', () => {
    const untagged = { admissions: row(202627, null), admissions_history: [row(202526, null)] };
    expect(admissionsForPhase(untagged, false)?.year).toBe(202627);
    expect(admissionsForPhase(untagged, true)?.year).toBe(202627);

    const mixed = { admissions: row(202627, 'Secondary'), admissions_history: [row(202526, null)] };
    expect(admissionsForPhase(mixed, false)).toBeNull();
  });

  it('handles missing data', () => {
    expect(admissionsForPhase(null, false)).toBeNull();
    expect(admissionsForPhase({ admissions: null, admissions_history: [] }, true)).toBeNull();
  });
});

describe('progressBand', () => {
  it('CI entirely above zero → above', () => {
    expect(progressBand(1.2, 0.4, 2.0)).toBe('above');
  });
  it('CI entirely below zero → below', () => {
    expect(progressBand(-1.2, -2.0, -0.4)).toBe('below');
  });
  it('CI straddling zero → average', () => {
    expect(progressBand(0.3, -0.5, 1.1)).toBe('average');
  });
  it('missing CI → null (no naive thresholding)', () => {
    expect(progressBand(1.2, null, null)).toBeNull();
    expect(progressBand(null, null, null)).toBeNull();
  });
});

describe('verdict', () => {
  it('above / close / below with a 2pp tolerance', () => {
    expect(verdict(87, 62)).toBe('above');
    expect(verdict(61, 62)).toBe('close');
    expect(verdict(40, 62)).toBe('below');
  });
});

describe('stripPositions', () => {
  it('maps a custom domain', () => {
    const pts = stripPositions([106], 100, 120);
    expect(pts[0].pos).toBe(30);
  });

  it('flips a colliding label above', () => {
    const pts = stripPositions([91, 92], 0, 100);
    const sorted = [...pts].sort((a, b) => a.value - b.value);
    expect(sorted[0].labelAbove).toBe(false);
    expect(sorted[1].labelAbove).toBe(true);
  });

  it('skips nulls and keeps school indices', () => {
    const pts = stripPositions([50, null, 70], 0, 100);
    expect(pts).toHaveLength(2);
    expect(pts.map((p) => p.schoolIndex)).toEqual([0, 2]);
  });

  it('clamps out-of-domain values', () => {
    const pts = stripPositions([95], 100, 120);
    expect(pts[0].pos).toBe(0);
  });
});

describe('latestValues', () => {
  const data = {
    '1': {
      yearly_data: [
        { year: 202324, rwm_expected_pct: 75 },
        { year: 202425, rwm_expected_pct: 87 },
      ],
    },
    '2': {
      yearly_data: [
        { year: 202324, rwm_expected_pct: 82 },
        { year: 202425, rwm_expected_pct: null },
      ],
    },
  };

  it('takes the latest non-null value per school in urn order', async () => {
    const { latestValues } = await import('@/lib/compareLogic');
    expect(latestValues(data, [1, 2], 'rwm_expected_pct')).toEqual([87, 82]);
  });

  it('returns null for unknown schools and metrics', async () => {
    const { latestValues } = await import('@/lib/compareLogic');
    expect(latestValues(data, [3], 'rwm_expected_pct')).toEqual([null]);
    expect(latestValues(data, [1], 'nope')).toEqual([null]);
  });
});
