import React, { useEffect } from 'react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { render, fireEvent, screen, waitFor } from '@testing-library/react';
import { useEditor } from './useEditor';
import { getAnnotationDetail } from '../api/generated';

vi.mock('@tanstack/react-query', () => ({
  useQueryClient: () => ({ invalidateQueries: vi.fn() })
}));
vi.mock('../context/LanguageContext', () => ({
  useLanguage: () => ({ t: (key) => key })
}));
vi.mock('./useMessageDialog', () => ({
  useMessageDialog: () => ({ alert: vi.fn(), confirm: vi.fn().mockResolvedValue(true) })
}));
vi.mock('../api/generated', () => ({
  getAnnotationDetail: vi.fn(),
  saveAnnotation: vi.fn()
}));
vi.mock('../utils/editorUtils', () => ({
  calculateHash: vi.fn(),
  hasObjectChanges: () => false,
  hasActualContent: () => false
}));
vi.mock('../utils/modelUploadValidation', () => ({
  buildModelUploadGroups: vi.fn(),
  validateModelFileBinding: vi.fn().mockResolvedValue({ isValid: true })
}));
vi.mock('../utils/api', () => ({
  buildApiUrl: (path) => path,
  toAbsoluteApiUrl: (path) => path,
}));

let controls = null;

function TestEditor({ defaultAnnotatorName }) {
  const { state, actions } = useEditor({ defaultAnnotatorName });

  useEffect(() => {
    actions.refs.controlsRef.current = controls = {
      getAzimuthalAngle: vi.fn().mockReturnValue(0),
      getPolarAngle: vi.fn().mockReturnValue(0),
      setAzimuthalAngle: vi.fn(),
      setPolarAngle: vi.fn(),
      update: vi.fn(),
    };
  }, [actions]);

  return (
    <div>
      <span data-testid="annotationId">{state.annotationId || ''}</span>
      <span data-testid="modelName">{state.modelName}</span>
      <span data-testid="annotator">{state.annotatorName}</span>
      <span data-testid="formValues">{JSON.stringify(state.formValues)}</span>
      <button data-testid="setTrait" onClick={() => actions.handleInputChange('som-auricle-area-left-ear', 'Narrow')}>
        Set trait
      </button>
      <button data-testid="resetForm" onClick={() => actions.handleResetForm(true)}>
        Reset form
      </button>
      <button data-testid="loadHistory" onClick={() => actions.loadModelFromHistory('annotation-uuid')}>
        Load history
      </button>
      <button data-testid="ensureWritable" onClick={() => actions.ensureWritableAnnotation()}>
        Ensure writable
      </button>
      <button data-testid="rotateY" onClick={() => actions.rotateModel('y', 0.5)}>
        Rotate Y
      </button>
      <button data-testid="rotateX" onClick={() => actions.rotateModel('x', 0.3)}>
        Rotate X
      </button>
    </div>
  );
}

describe('useEditor hook', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    controls = null;
  });

  it('mirrors bilateral trait values when a mirrored trait is set', async () => {
    render(<TestEditor defaultAnnotatorName="Current User" />);

    fireEvent.click(screen.getByTestId('setTrait'));

    await waitFor(() => {
      expect(JSON.parse(screen.getByTestId('formValues').textContent)).toEqual({
        'som-auricle-area-left-ear': 'Narrow',
        'som-auricle-area-right-ear': 'Narrow',
      });
    });
  });

  it('clears form values when reset is requested and preserves empty state', async () => {
    render(<TestEditor defaultAnnotatorName="Current User" />);

    fireEvent.click(screen.getByTestId('setTrait'));
    await waitFor(() => expect(screen.getByTestId('formValues').textContent).not.toBe('{}'));

    fireEvent.click(screen.getByTestId('resetForm'));
    await waitFor(() => expect(screen.getByTestId('formValues').textContent).toBe('{}'));
  });

  it('converts a foreign annotation to a writable draft and clears the annotation id', async () => {
    getAnnotationDetail.mockResolvedValue({
      data: {
        atlasId: 'atlas-1',
        fileHash: 'hash-1',
        annotator: 'Original Annotator',
        modelId: 'model.obj',
        values: {},
      }
    });

    render(<TestEditor defaultAnnotatorName="Current User" />);

    fireEvent.click(screen.getByTestId('loadHistory'));
    await waitFor(() => expect(screen.getByTestId('annotator').textContent).toBe('Original Annotator'));
    expect(screen.getByTestId('annotationId').textContent).toBe('annotation-uuid');

    fireEvent.click(screen.getByTestId('ensureWritable'));
    await waitFor(() => {
      expect(screen.getByTestId('annotationId').textContent).toBe('');
      expect(screen.getByTestId('modelName').textContent).toContain('model (Current User)');
    });
  });

  it('exposes viewport rotation actions through the controls ref', async () => {
    render(<TestEditor defaultAnnotatorName="Current User" />);

    fireEvent.click(screen.getByTestId('rotateY'));
    fireEvent.click(screen.getByTestId('rotateX'));

    await waitFor(() => {
      expect(controls).not.toBeNull();
      expect(controls.setAzimuthalAngle).toHaveBeenCalledWith(0.5);
      expect(controls.setPolarAngle).toHaveBeenCalledWith(0.3);
      expect(controls.update).toHaveBeenCalledTimes(2);
    });
  });
});
