/**
 * table-toolbar-ui tests
 *
 * Pinning the §381 (v0.6.10 slice F) one-shot dev-mode warning for
 * unknown opt-out attributes. Triggered by claims-ui FB-04: consumers
 * set `searchable` / `exportable` / `sortable` thinking they toggle
 * visibility; canonical attrs are `no-search`/`no-filter`/`no-sort`/
 * `no-columns`. Previous behavior: silent no-op.
 *
 * Contract:
 *   1. Unknown attrs matching /^(search|export|sort|filter|column)/i
 *      → console.warn fires once per element with the attr names listed
 *   2. Canonical attrs (no-search/no-filter/no-sort/no-columns) → no warn
 *   3. Unrelated attrs (id, class, role, aria-*, data-*) → no warn
 *   4. Multiple unknown attrs in one declaration → single warn (one warn
 *      per element, all unknown attrs in the message)
 *   5. Repeated reconnects of the same element → still one warn total
 */

import { describe, it, expect, beforeEach, vi } from 'vitest';
import '../../core/element.js';
import './table-toolbar.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('table-toolbar-ui — unknown opt-out attribute warning (§381 / FB-04)', () => {
  beforeEach(() => { document.body.innerHTML = ''; });

  it('warns when an unknown `searchable` attribute is set', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    mount('<table-toolbar-ui searchable></table-toolbar-ui>');
    await tick();
    expect(warn).toHaveBeenCalledTimes(1);
    const msg = warn.mock.calls[0][0];
    expect(msg).toContain('[table-toolbar-ui]');
    expect(msg).toContain('"searchable"');
    expect(msg).toContain('no-search');
    warn.mockRestore();
  });

  it('warns when an unknown `exportable` attribute is set', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    mount('<table-toolbar-ui exportable></table-toolbar-ui>');
    await tick();
    expect(warn).toHaveBeenCalledTimes(1);
    const msg = warn.mock.calls[0][0];
    expect(msg).toContain('"exportable"');
    warn.mockRestore();
  });

  it('warns when an unknown `sortable` attribute is set', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    mount('<table-toolbar-ui sortable></table-toolbar-ui>');
    await tick();
    expect(warn).toHaveBeenCalledTimes(1);
    expect(warn.mock.calls[0][0]).toContain('"sortable"');
    warn.mockRestore();
  });

  it('warns when an unknown `filterable` attribute is set', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    mount('<table-toolbar-ui filterable></table-toolbar-ui>');
    await tick();
    expect(warn).toHaveBeenCalledTimes(1);
    expect(warn.mock.calls[0][0]).toContain('"filterable"');
    warn.mockRestore();
  });

  it('warns when an unknown `columns-visible` attribute is set', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    mount('<table-toolbar-ui columns-visible></table-toolbar-ui>');
    await tick();
    expect(warn).toHaveBeenCalledTimes(1);
    expect(warn.mock.calls[0][0]).toContain('"columns-visible"');
    warn.mockRestore();
  });

  it('does NOT warn on canonical no-search', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    mount('<table-toolbar-ui no-search></table-toolbar-ui>');
    await tick();
    expect(warn).not.toHaveBeenCalled();
    warn.mockRestore();
  });

  it('does NOT warn on canonical no-filter / no-sort / no-columns', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    mount('<table-toolbar-ui no-filter no-sort no-columns></table-toolbar-ui>');
    await tick();
    expect(warn).not.toHaveBeenCalled();
    warn.mockRestore();
  });

  it('does NOT warn on unrelated attributes (id, class, role, aria-*, data-*)', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    mount('<table-toolbar-ui id="x" class="y" role="toolbar" aria-label="z" data-test="ok"></table-toolbar-ui>');
    await tick();
    expect(warn).not.toHaveBeenCalled();
    warn.mockRestore();
  });

  it('does NOT warn on canonical attrs even when placeholder is also set', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    mount('<table-toolbar-ui no-search no-sort placeholder="Find..."></table-toolbar-ui>');
    await tick();
    expect(warn).not.toHaveBeenCalled();
    warn.mockRestore();
  });

  it('emits a single warn that names all unknown opt-out attrs', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    mount('<table-toolbar-ui searchable exportable sortable></table-toolbar-ui>');
    await tick();
    expect(warn).toHaveBeenCalledTimes(1);
    const msg = warn.mock.calls[0][0];
    expect(msg).toContain('"searchable"');
    expect(msg).toContain('"exportable"');
    expect(msg).toContain('"sortable"');
    warn.mockRestore();
  });

  it('warns at most once per element across reconnects', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    const el = mount('<table-toolbar-ui searchable></table-toolbar-ui>');
    await tick();
    expect(warn).toHaveBeenCalledTimes(1);
    // Disconnect + reconnect.
    el.remove();
    document.body.appendChild(el);
    await tick();
    expect(warn).toHaveBeenCalledTimes(1); // unchanged
    warn.mockRestore();
  });

  it('mixes canonical + unknown — names only the unknown in the warning', async () => {
    const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
    mount('<table-toolbar-ui no-search searchable></table-toolbar-ui>');
    await tick();
    expect(warn).toHaveBeenCalledTimes(1);
    const msg = warn.mock.calls[0][0];
    expect(msg).toContain('"searchable"');
    expect(msg).not.toContain('"no-search"');
    warn.mockRestore();
  });
});
