// @vitest-environment jsdom
/**
 * Component tests for ContextForm (settings batch A).
 *
 * Focus: the threshold-row serialization. Each row is { ratio, action }; editing
 * a ratio must coerce to Number and rewrite the WHOLE thresholds array (not just
 * one field), and editing an action must keep ratio intact. Also covers
 * limitTokens empty -> undefined.
 */
import '../../test/dom-setup';
import { describe, it, expect, vi } from 'vitest';
import { screen } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { renderWithProviders, renderStatefulForm } from '../../test/render-helpers';
import { ContextForm } from './ContextForm';

function render(config: any) {
  const onChange = vi.fn();
  renderWithProviders(<ContextForm config={config} onChange={onChange} overriddenByEnv={{}} />);
  return { onChange };
}

function renderStateful(config: any) {
  const onChange = vi.fn();
  const utils = renderStatefulForm(ContextForm, config, { onChangeSpy: onChange });
  return { onChange, ...utils };
}

const baseThresholds = [
  { ratio: 0.7, action: 'warn' },
  { ratio: 0.85, action: 'prompt' },
  { ratio: 0.95, action: 'force_transition' },
];

describe('ContextForm', () => {
  it('renders default thresholds when none provided', () => {
    render({ context: {} });
    const ratioInputs = screen.getAllByRole('spinbutton') as HTMLInputElement[];
    // 1 limitTokens + 3 threshold ratios.
    expect(ratioInputs).toHaveLength(4);
    // Three action selects.
    expect(screen.getAllByRole('combobox')).toHaveLength(3);
  });

  it('serializes a ratio edit by rewriting the full thresholds array with a numeric ratio', async () => {
    const { getConfig } = renderStateful({ context: { thresholds: baseThresholds } });
    const ratioInputs = screen.getAllByRole('spinbutton');
    // ratioInputs[0] is limitTokens; the first threshold ratio is index 1.
    await userEvent.clear(ratioInputs[1]);
    await userEvent.type(ratioInputs[1], '0.5');
    const value = getConfig().context.thresholds;
    expect(Array.isArray(value)).toBe(true);
    expect(value).toHaveLength(3);
    expect(value[0]).toEqual({ ratio: 0.5, action: 'warn' });
    expect(typeof value[0].ratio).toBe('number');
    // Other rows preserved.
    expect(value[1]).toEqual({ ratio: 0.85, action: 'prompt' });
  });

  it('serializes an action edit while preserving that row ratio', async () => {
    const { onChange } = render({ context: { thresholds: baseThresholds } });
    const selects = screen.getAllByRole('combobox');
    await userEvent.selectOptions(selects[1], 'force_transition');
    const [path, value] = onChange.mock.calls.at(-1)!;
    expect(path).toBe('context.thresholds');
    expect(value[1]).toEqual({ ratio: 0.85, action: 'force_transition' });
  });

  it('serializes limitTokens as a number, and undefined when emptied', async () => {
    const { onChange } = render({ context: { limitTokens: 8000 } });
    const limit = screen.getAllByRole('spinbutton')[0];
    await userEvent.clear(limit);
    expect(onChange).toHaveBeenLastCalledWith('context.limitTokens', undefined);
  });
});
