/**
 * ColorField — the shared color-input primitive (swatch + optional hex field).
 * Covers the render shapes and the hex-entry commit/normalize behavior; the
 * Theme Builder and Chart Builder integrations are exercised in their own tests.
 */

import { afterEach, beforeEach, describe, expect, test, vi } from 'vitest';
import { act } from 'react';
import { createRoot, type Root } from 'react-dom/client';
import { ColorField } from './ColorField';

(globalThis as { IS_REACT_ACT_ENVIRONMENT?: boolean }).IS_REACT_ACT_ENVIRONMENT = true;

let container: HTMLDivElement;
let root: Root;

beforeEach(() => {
  container = document.createElement('div');
  document.body.appendChild(container);
  act(() => {
    root = createRoot(container);
  });
});

afterEach(() => {
  act(() => root.unmount());
  container.remove();
});

/** Drive an input's value through the native setter so React's onChange fires. */
function setNativeValue(el: HTMLInputElement, value: string) {
  // eslint-disable-next-line @typescript-eslint/unbound-method -- invoked immediately via .call
  const setter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value')!.set!;
  setter.call(el, value);
  el.dispatchEvent(new Event('input', { bubbles: true }));
}

const swatch = () => container.querySelector<HTMLInputElement>('input[type="color"]')!;
const hexField = () => container.querySelector<HTMLInputElement>('input[type="text"]');

describe('ColorField', () => {
  test('renders just the swatch by default (no hex field)', () => {
    act(() => root.render(<ColorField value="#112233" label="Fill" onChange={() => {}} />));
    expect(swatch().getAttribute('aria-label')).toBe('Fill');
    expect(swatch().value).toBe('#112233');
    expect(hexField()).toBeNull();
  });

  test('falls back to #000000 in the picker for a non-hex value', () => {
    act(() => root.render(<ColorField value="steelblue" label="Fill" onChange={() => {}} />));
    expect(swatch().value).toBe('#000000');
  });

  test('the picker reports its raw value on change', () => {
    const onChange = vi.fn();
    act(() => root.render(<ColorField value="#112233" label="Fill" onChange={onChange} />));
    act(() => setNativeValue(swatch(), '#ff0000'));
    expect(onChange).toHaveBeenCalledWith('#ff0000');
  });

  test('hex mode commits a valid hex and normalizes an unprefixed one', () => {
    const onChange = vi.fn();
    act(() => root.render(<ColorField value="#112233" label="Fill" onChange={onChange} hex />));
    const hex = hexField()!;
    expect(hex.getAttribute('aria-label')).toBe('Fill hex value');

    act(() => setNativeValue(hex, '#abcdef'));
    expect(onChange).toHaveBeenLastCalledWith('#abcdef');

    act(() => setNativeValue(hex, 'ABCDEF'));
    expect(onChange).toHaveBeenLastCalledWith('#abcdef');
  });

  test('hex mode does not commit an incomplete value', () => {
    const onChange = vi.fn();
    act(() => root.render(<ColorField value="#112233" label="Fill" onChange={onChange} hex />));
    act(() => setNativeValue(hexField()!, '#abc'));
    expect(onChange).not.toHaveBeenCalled();
  });
});
