import React from 'react';
import { render, screen, fireEvent, act } from '@testing-library/react';

// recharts uses ResizeObserver (absent in jsdom); stub all used pieces as passthroughs.
// Coverage boundary: with recharts mocked out, these tests cover only the sidebar
// integration path (toggle, checkbox state, persistence). The legend onClick/hover
// wiring, line hide/strokeWidth, and tooltip ordering are exercised by the pure-helper
// unit tests (crossSubjectChart.test.js) and SubjectSidebar.test.jsx.
jest.mock('recharts', () => {
  const Pass = ({ children }) => <div>{children}</div>;
  return {
    ResponsiveContainer: Pass, LineChart: Pass, Line: () => null,
    XAxis: () => null, YAxis: () => null, CartesianGrid: () => null,
    Tooltip: () => null, Legend: () => null,
  };
});

import { ExperimentAnalysisCharts } from '../src/components/AnalysisCharts';

const ANIMALS = [
  { id: 'a1', animal_name: 'Mouse-A1', subject_info: { grp: 'Group A' } },
  { id: 'b1', animal_name: 'Mouse-B1', subject_info: { grp: 'Group B' } },
];
const STATUSES = [
  { animal_id: 'a1', date: '2026-01-01', analysis_summary: { total: 10, success_rate: 0.5, counts: { Success: 5 } } },
  { animal_id: 'b1', date: '2026-01-01', analysis_summary: { total: 8, success_rate: 0.25, counts: { Success: 2 } } },
];
const SUBJECT_TEMPLATE = [{ fieldId: 'grp', label: 'Group', active: true }];
const DAILY_TEMPLATE = [];

function renderChart() {
  render(
    <ExperimentAnalysisCharts
      animals={ANIMALS}
      experimentStatuses={STATUSES}
      subjectTemplate={SUBJECT_TEMPLATE}
      dailyTemplate={DAILY_TEMPLATE}
    />,
  );
}

it('toggles the subject sidebar open and closed', () => {
  renderChart();
  expect(screen.queryByText('Subjects')).toBeNull();
  fireEvent.click(screen.getByText('▸ Subjects'));
  expect(screen.getByText('Subjects')).toBeInTheDocument();
  expect(screen.getByLabelText('Mouse-A1')).toBeInTheDocument();
  expect(screen.getByLabelText('Mouse-B1')).toBeInTheDocument();
  fireEvent.click(screen.getByText('◂ Subjects'));
  expect(screen.queryByText('Subjects')).toBeNull();
});

it('unchecking a subject in the sidebar persists across re-render', () => {
  renderChart();
  fireEvent.click(screen.getByText('▸ Subjects'));
  const cb = screen.getByLabelText('Mouse-A1');
  expect(cb.checked).toBe(true);
  fireEvent.click(cb);
  expect(screen.getByLabelText('Mouse-A1').checked).toBe(false);
  expect(screen.getByText('Show all')).toBeInTheDocument();
});

it('hydrates hidden subjects and sidebar-open from plotConfig', () => {
  render(
    <ExperimentAnalysisCharts
      animals={ANIMALS}
      experimentStatuses={STATUSES}
      subjectTemplate={SUBJECT_TEMPLATE}
      dailyTemplate={DAILY_TEMPLATE}
      plotConfig={{ hiddenSubjects: ['a1'], sidebarOpen: true }}
    />,
  );
  expect(screen.getByLabelText('Mouse-A1').checked).toBe(false);
  expect(screen.getByLabelText('Mouse-B1').checked).toBe(true);
});

describe('debounced auto-save', () => {
  beforeEach(() => jest.useFakeTimers());
  afterEach(() => jest.useRealTimers());

  it('does not call onPersistConfig on initial render', () => {
    const onPersist = jest.fn();
    render(
      <ExperimentAnalysisCharts
        animals={ANIMALS} experimentStatuses={STATUSES}
        subjectTemplate={SUBJECT_TEMPLATE} dailyTemplate={DAILY_TEMPLATE}
        plotConfig={null} onPersistConfig={onPersist}
      />,
    );
    act(() => { jest.advanceTimersByTime(1500); });
    expect(onPersist).not.toHaveBeenCalled();
  });

  it('calls onPersistConfig ~1s after a change, with the updated config', () => {
    const onPersist = jest.fn().mockResolvedValue({});
    render(
      <ExperimentAnalysisCharts
        animals={ANIMALS} experimentStatuses={STATUSES}
        subjectTemplate={SUBJECT_TEMPLATE} dailyTemplate={DAILY_TEMPLATE}
        plotConfig={{ sidebarOpen: true }} onPersistConfig={onPersist}
      />,
    );
    fireEvent.click(screen.getByLabelText('Mouse-A1'));
    expect(onPersist).not.toHaveBeenCalled();
    act(() => { jest.advanceTimersByTime(1100); });
    expect(onPersist).toHaveBeenCalledTimes(1);
    expect(onPersist.mock.calls[0][0].hiddenSubjects).toContain('a1');
  });

  it('does not save and clears the saving hint when a change is reverted before the debounce fires', () => {
    const onPersist = jest.fn().mockResolvedValue({});
    render(
      <ExperimentAnalysisCharts
        animals={ANIMALS} experimentStatuses={STATUSES}
        subjectTemplate={SUBJECT_TEMPLATE} dailyTemplate={DAILY_TEMPLATE}
        plotConfig={{ sidebarOpen: true }} onPersistConfig={onPersist}
      />,
    );
    fireEvent.click(screen.getByLabelText('Mouse-A1')); // hide a1
    expect(screen.getByText('Saving…')).toBeInTheDocument();
    fireEvent.click(screen.getByLabelText('Mouse-A1')); // revert before the 1s timer
    act(() => { jest.advanceTimersByTime(1100); });
    expect(onPersist).not.toHaveBeenCalled();
    expect(screen.queryByText('Saving…')).toBeNull();
  });
});
