/**
 * <list-item-ui> behavioral tests.
 *
 * Initial scope: FB-42 / §340 regression coverage — `render()`'s text
 * branch can now create the slot element when `this.text` is set
 * post-connect (e.g. via template-engine property binding), matching
 * the icon + description branches' create-if-missing pattern.
 *
 * Test setup note: happy-dom connects custom elements synchronously
 * during innerHTML parsing. To exercise the post-connect property-set
 * timing (template-engine path), tests use `document.createElement` +
 * `host.appendChild()` to connect with empty `text`, then set the
 * property AFTER connect to trigger render()'s recovery path.
 */

import { describe, it, expect, beforeAll, beforeEach } from 'vitest';

beforeAll(async () => {
  await import('./list.js');
});

describe('<list-item-ui> render() text branch — post-connect property binding (FB-42 / §340)', () => {
  let host;

  beforeEach(() => {
    host = document.createElement('div');
    document.body.appendChild(host);
  });

  it('creates [slot="text"] span when .text is set AFTER connectedCallback (template-engine timing)', async () => {
    const li = document.createElement('list-item-ui');
    host.appendChild(li);
    // connectedCallback fires here with this.text === '' — span NOT stamped
    await new Promise((r) => setTimeout(r, 30));
    expect(li.querySelector('[slot="text"]')).toBeNull();

    // Template engine sets the property post-connect
    li.text = 'Primary text via property binding';
    await new Promise((r) => setTimeout(r, 30));

    const textSpan = li.querySelector('[slot="text"]');
    expect(textSpan).not.toBeNull();
    expect(textSpan.textContent).toBe('Primary text via property binding');
  });

  it('updates existing [slot="text"] span when .text changes', async () => {
    const li = document.createElement('list-item-ui');
    li.setAttribute('text', 'initial');
    host.appendChild(li);
    await new Promise((r) => setTimeout(r, 30));

    const textSpan = li.querySelector('[slot="text"]');
    expect(textSpan).not.toBeNull();
    expect(textSpan.textContent).toBe('initial');

    li.text = 'updated';
    await new Promise((r) => setTimeout(r, 30));
    expect(li.querySelector('[slot="text"]').textContent).toBe('updated');
  });

  it('removes stamped [slot="text"] span when .text is cleared', async () => {
    const li = document.createElement('list-item-ui');
    li.setAttribute('text', 'will be removed');
    host.appendChild(li);
    await new Promise((r) => setTimeout(r, 30));
    expect(li.querySelector('[slot="text"]')).not.toBeNull();

    li.text = '';
    await new Promise((r) => setTimeout(r, 30));
    expect(li.querySelector('[slot="text"]')).toBeNull();
  });

  it('text branch parity with description branch — both create-if-missing post-connect', async () => {
    const li = document.createElement('list-item-ui');
    host.appendChild(li);
    await new Promise((r) => setTimeout(r, 30));
    expect(li.querySelector('[slot="text"]')).toBeNull();
    expect(li.querySelector('[slot="description"]')).toBeNull();

    li.text = 'primary';
    li.description = 'secondary';
    await new Promise((r) => setTimeout(r, 30));

    expect(li.querySelector('[slot="text"]')?.textContent).toBe('primary');
    expect(li.querySelector('[slot="description"]')?.textContent).toBe('secondary');
  });

  it('does not stamp [slot="text"] for unauthored consumer-provided text content', async () => {
    // If the consumer provides their own [slot="text"] child (e.g. for
    // richer markup), the render() branch must NOT touch it.
    const li = document.createElement('list-item-ui');
    const userSpan = document.createElement('span');
    userSpan.setAttribute('slot', 'text');
    userSpan.innerHTML = '<strong>bold</strong> user';
    li.appendChild(userSpan);
    host.appendChild(li);
    await new Promise((r) => setTimeout(r, 30));

    li.text = 'attempt to set';
    await new Promise((r) => setTimeout(r, 30));

    const span = li.querySelector('[slot="text"]');
    expect(span).toBe(userSpan); // identity preserved
    expect(span.innerHTML).toBe('<strong>bold</strong> user'); // markup preserved
  });
});
