import { describe, it, expect } from 'vitest';
import { extractJson, parseAIProgram } from '@/lib/ai/programSchema';

describe('extractJson', () => {
  it('extracts a bare JSON object', () => {
    expect(extractJson('{"a":1}')).toBe('{"a":1}');
  });
  it('strips ```json fences', () => {
    expect(extractJson('```json\n{"a":1}\n```')).toBe('{"a":1}');
  });
  it('strips bare ``` fences', () => {
    expect(extractJson('```\n{"a":1}\n```')).toBe('{"a":1}');
  });
  it('finds first balanced object after preamble', () => {
    const raw =
      'Here is your program:\n\n{"name":"X","weeks":[{"weekNumber":1,"days":[]}]}\n\nHope that helps!';
    expect(extractJson(raw)).toBe(
      '{"name":"X","weeks":[{"weekNumber":1,"days":[]}]}',
    );
  });
  it('handles braces inside strings', () => {
    const raw = '{"notes":"use {brackets} sparingly","x":1}';
    expect(extractJson(raw)).toBe(raw);
  });
  it('returns null when no object present', () => {
    expect(extractJson('no json at all')).toBeNull();
  });
});

describe('parseAIProgram', () => {
  const valid = {
    name: 'Test',
    type: 'hypertrophy',
    durationWeeks: 4,
    weeks: [
      {
        weekNumber: 1,
        days: [
          {
            dayOfWeek: 1,
            exercises: [
              {
                exerciseId: 'cabc',
                exerciseName: 'Bench Press',
                order: 0,
                sets: 4,
                repsMin: 6,
                repsMax: 10,
              },
            ],
          },
        ],
      },
    ],
  };

  it('accepts a valid program', () => {
    const r = parseAIProgram(JSON.stringify(valid));
    expect(r.ok).toBe(true);
    if (r.ok) {
      expect(r.program.name).toBe('Test');
      expect(r.program.weeks[0].days[0].exercises[0].exerciseName).toBe(
        'Bench Press',
      );
    }
  });

  it('accepts null exerciseId for unresolved exercises', () => {
    const variant = structuredClone(valid);
    variant.weeks[0].days[0].exercises[0] = {
      ...variant.weeks[0].days[0].exercises[0],
      exerciseId: null as unknown as string,
    };
    const r = parseAIProgram(JSON.stringify(variant));
    expect(r.ok).toBe(true);
  });

  it('rejects when no JSON found', () => {
    const r = parseAIProgram('the model just said hello');
    expect(r.ok).toBe(false);
    if (!r.ok) expect(r.reason).toMatch(/Could not find/);
  });

  it('rejects malformed JSON', () => {
    // Unbalanced braces: extractJson never finds a closing `}`, so
    // the failure mode is "Could not find a JSON object" rather than
    // a parse error per se. Either way, ok=false.
    const r = parseAIProgram('{ "name": "x", "weeks": [');
    expect(r.ok).toBe(false);
  });

  it('rejects JSON with a parse-level syntax error inside balanced braces', () => {
    const r = parseAIProgram('{ "name": "x", }');
    expect(r.ok).toBe(false);
    if (!r.ok) expect(r.reason).toMatch(/parse error/i);
  });

  it('rejects when shape is wrong (missing weeks)', () => {
    const bad = { name: 'X', type: 'hypertrophy', durationWeeks: 4 };
    const r = parseAIProgram(JSON.stringify(bad));
    expect(r.ok).toBe(false);
    if (!r.ok) expect(r.reason).toMatch(/shape/);
  });

  it('rejects when dayOfWeek is out of range', () => {
    const variant = structuredClone(valid);
    variant.weeks[0].days[0].dayOfWeek = 7; // 0-6 only
    const r = parseAIProgram(JSON.stringify(variant));
    expect(r.ok).toBe(false);
  });

  it('handles a model response wrapped in markdown commentary', () => {
    const wrapped =
      "Sure! Here's your program:\n\n```json\n" +
      JSON.stringify(valid) +
      '\n```\n\nLet me know if you want changes.';
    const r = parseAIProgram(wrapped);
    expect(r.ok).toBe(true);
  });
});
