/**
 * toggle-scheme-ui §200 race-fix tests — v0.5.7 (FEEDBACK-10 §1).
 *
 * Verifies the post-connect attribute application is HONORED — not raced over
 * by the synchronous #initState() call in connected(). Per the bug class:
 *
 *   1. template.js scan() strips placeholder attributes pre-insertion
 *   2. replaceChildren() upgrades the element; connectedCallback fires
 *   3. #initState() reads this.scheme (default 'auto')
 *   4. template.js update() then setAttribute('scheme', 'dark')
 *   5. attributeChangedCallback syncs to property — but #initState already ran
 *
 * Pre-§200, step 4 was a no-op for the scheme cascade. Post-§200,
 * attributeChangedCallback re-runs #initState() (until user-touched).
 */

import { describe, it, expect, beforeEach } from 'vitest';
import '../../core/element.js';
import './toggle-scheme.js';

const tick = () => new Promise((r) => queueMicrotask(r));

function mount(html) {
  const wrap = document.createElement('div');
  wrap.innerHTML = html;
  document.body.appendChild(wrap);
  return wrap.firstElementChild;
}

describe('toggle-scheme-ui §200 race fix', () => {
  beforeEach(() => {
    document.body.innerHTML = '';
    document.documentElement.style.removeProperty('color-scheme');
  });

  it('honors post-connect scheme attribute application (reactive consumer)', async () => {
    // Simulate template.js's strip-then-restamp pattern: mount without scheme,
    // then set the attribute immediately afterward (mimics post-connect apply).
    const t = mount('<toggle-scheme-ui></toggle-scheme-ui>');
    await tick();
    t.setAttribute('scheme', 'dark');
    await tick();
    expect(t.activeScheme).toBe('dark');
    expect(document.documentElement.style.colorScheme).toBe('dark');
  });

  it('honors the initial scheme attribute (no race scenario)', async () => {
    const t = mount('<toggle-scheme-ui scheme="dark"></toggle-scheme-ui>');
    await tick();
    expect(t.activeScheme).toBe('dark');
  });

  it('user button press locks #userTouched — subsequent attr changes are ignored', async () => {
    const t = mount('<toggle-scheme-ui scheme="dark"></toggle-scheme-ui>');
    await tick();
    expect(t.activeScheme).toBe('dark');

    // User press: flips to light. #userTouched flips true.
    const btn = t.querySelector(':scope > button-ui');
    btn.dispatchEvent(new CustomEvent('press', { bubbles: true }));
    await tick();
    expect(t.activeScheme).toBe('light');

    // Consumer re-renders with scheme="dark" — should NOT clobber user choice.
    t.setAttribute('scheme', 'dark');
    await tick();
    expect(t.activeScheme).toBe('light'); // user choice survives
  });

  it('programmatic setScheme also locks #userTouched', async () => {
    const t = mount('<toggle-scheme-ui scheme="auto"></toggle-scheme-ui>');
    await tick();

    t.setScheme('dark');
    await tick();
    expect(t.activeScheme).toBe('dark');

    // Consumer reactive write should NOT override.
    t.setAttribute('scheme', 'light');
    await tick();
    expect(t.activeScheme).toBe('dark');
  });

  it('toggle() also locks #userTouched', async () => {
    const t = mount('<toggle-scheme-ui scheme="auto"></toggle-scheme-ui>');
    await tick();
    const initial = t.activeScheme;  // depends on prefers-color-scheme (likely light in test env)

    t.toggle();
    await tick();
    expect(t.activeScheme).not.toBe(initial);

    const afterToggle = t.activeScheme;
    t.setAttribute('scheme', initial === 'dark' ? 'dark' : 'light');
    await tick();
    expect(t.activeScheme).toBe(afterToggle); // user choice survives
  });

  it('removing the attribute (back to auto) is honored pre-touch', async () => {
    const t = mount('<toggle-scheme-ui scheme="dark"></toggle-scheme-ui>');
    await tick();
    expect(t.activeScheme).toBe('dark');

    t.removeAttribute('scheme');
    await tick();
    // After removal, attr is null; #initState falls to auto → resolves
    // prefers-color-scheme. Test env should resolve to light by default.
    expect(['light', 'dark']).toContain(t.activeScheme);
  });
});
