import { expect, fixture, html } from '@open-wc/testing';
import './nile-nav-tab';
import type { NileNavTab } from './nile-nav-tab';

const getLink = (el: NileNavTab) =>
  el.shadowRoot?.querySelector('nile-link.nav-tab') as HTMLElement | null;

const getInnerAnchor = (el: NileNavTab) => {
  const nl = getLink(el);
  return nl?.shadowRoot?.querySelector('a') ?? null;
};

describe('NileNavTab', () => {
  // ---- Rendering ----

  it('renders with shadow root and list shell', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el).to.exist;
    expect(el.shadowRoot).to.not.be.null;
    const shell = el.shadowRoot!.querySelector('.nav-tab-container');
    expect(shell).to.exist;
    expect(shell!.getAttribute('role')).to.equal('presentation');
    expect(shell!.getAttribute('part')?.split(/\s+/).sort()).to.deep.equal([
      'base',
      'tab',
    ]);
  });

  it('renders nile-link with nav-tab class and three slots', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    const link = getLink(el);
    expect(link).to.exist;
    expect(link!.classList.contains('nav-tab')).to.be.true;
    expect(el.shadowRoot!.querySelector('slot:not([name])')).to.exist;
    expect(el.shadowRoot!.querySelector('slot[name="prefix"]')).to.exist;
    expect(el.shadowRoot!.querySelector('slot[name="suffix"]')).to.exist;
  });

  it('shows default slot content in light DOM', async () => {
    const el = await fixture<NileNavTab>(
      html`<nile-nav-tab>Settings</nile-nav-tab>`
    );
    expect(el.textContent).to.contain('Settings');
  });

  it('does not render close button unless closable', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.shadowRoot!.querySelector('nile-icon-button')).to.be.null;
  });

  it('renders close button when closable', async () => {
    const el = await fixture<NileNavTab>(
      html`<nile-nav-tab closable>Tab</nile-nav-tab>`
    );
    const btn = el.shadowRoot!.querySelector('nile-icon-button');
    expect(btn).to.exist;
    expect(btn!.getAttribute('part')).to.equal('close-button');
  });

  it('exposes part base on nile-link via exportparts', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>A</nile-nav-tab>`);
    const link = getLink(el)!;
    expect(link.getAttribute('exportparts')).to.contain('base:link');
  });

  it('nile-link uses role none and hides link from a11y tree', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>T</nile-nav-tab>`);
    const link = getLink(el)!;
    expect(link.getAttribute('role')).to.equal('none');
    expect(link.getAttribute('aria-hidden')).to.equal('true');
    expect(link.getAttribute('tabindex')).to.equal('-1');
  });

  // ---- Properties & reflection ----

  it('defaults panel to empty string', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.panel).to.equal('');
  });

  it('defaults active, closable, disabled, and centered to false', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.active).to.be.false;
    expect(el.closable).to.be.false;
    expect(el.disabled).to.be.false;
    expect(el.centered).to.be.false;
  });

  it('reflects panel, active, disabled, and centered to attributes', async () => {
    const el = await fixture<NileNavTab>(html`
      <nile-nav-tab panel="p1" active disabled centered></nile-nav-tab>
    `);
    expect(el.getAttribute('panel')).to.equal('p1');
    expect(el.hasAttribute('active')).to.be.true;
    expect(el.hasAttribute('disabled')).to.be.true;
    expect(el.hasAttribute('centered')).to.be.true;
  });

  it('reflects link attribute when set in markup', async () => {
    const el = await fixture<NileNavTab>(html`
      <nile-nav-tab link="https://x.example/"></nile-nav-tab>
    `);
    expect(el.getAttribute('link')).to.equal('https://x.example/');
  });

  it('applies state classes on nile-link', async () => {
    const el = await fixture<NileNavTab>(html`
      <nile-nav-tab active closable disabled centered></nile-nav-tab>
    `);
    await el.updateComplete;
    const link = getLink(el)!;
    expect(link.classList.contains('nav-tab--active')).to.be.true;
    expect(link.classList.contains('nav-tab--closable')).to.be.true;
    expect(link.classList.contains('nav-tab--disabled')).to.be.true;
    expect(link.classList.contains('tab--centered')).to.be.true;
  });

  it('passes link href to inner anchor when link is set', async () => {
    const el = await fixture<NileNavTab>(html`
      <nile-nav-tab link="https://example.com/path"></nile-nav-tab>
    `);
    await el.updateComplete;
    const anchor = getInnerAnchor(el);
    expect(anchor).to.exist;
    expect(anchor!.getAttribute('href')).to.equal('https://example.com/path');
  });

  it('updates inner anchor href when link property changes', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    el.link = 'https://a.test/';
    await el.updateComplete;
    expect(getInnerAnchor(el)!.getAttribute('href')).to.equal('https://a.test/');
  });

  it('removes closable UI when closable set false', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab closable>T</nile-nav-tab>`);
    el.closable = false;
    await el.updateComplete;
    expect(el.shadowRoot!.querySelector('nile-icon-button')).to.be.null;
  });

  it('exposes static styles', async () => {
    const mod = await import('./nile-nav-tab');
    expect(mod.NileNavTab.styles).to.exist;
  });

  it('registers custom element', () => {
    expect(customElements.get('nile-nav-tab')).to.exist;
  });

  it('exports default class matching NileNavTab', async () => {
    const mod = await import('./nile-nav-tab');
    expect(mod.default).to.equal(mod.NileNavTab);
  });

  // ---- Accessibility ----

  it('sets role tab on host', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.getAttribute('role')).to.equal('tab');
  });

  it('syncs aria-selected with active', async () => {
    const inactive = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(inactive.getAttribute('aria-selected')).to.equal('false');

    const active = await fixture<NileNavTab>(
      html`<nile-nav-tab active></nile-nav-tab>`
    );
    expect(active.getAttribute('aria-selected')).to.equal('true');

    inactive.active = true;
    await inactive.updateComplete;
    expect(inactive.getAttribute('aria-selected')).to.equal('true');
  });

  it('syncs aria-disabled with disabled', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.getAttribute('aria-disabled')).to.equal('false');
    expect(el.hasAttribute('aria-hidden')).to.be.false;

    el.disabled = true;
    await el.updateComplete;
    expect(el.getAttribute('aria-disabled')).to.equal('true');
    expect(el.getAttribute('aria-hidden')).to.equal('true');

    el.disabled = false;
    await el.updateComplete;
    expect(el.hasAttribute('aria-hidden')).to.be.false;
  });

  it('sets host tabIndex 0 when active and not disabled', async () => {
    const el = await fixture<NileNavTab>(
      html`<nile-nav-tab active></nile-nav-tab>`
    );
    expect(el.tabIndex).to.equal(0);
  });

  it('sets host tabIndex -1 when inactive or disabled', async () => {
    const inactive = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(inactive.tabIndex).to.equal(-1);

    const disabled = await fixture<NileNavTab>(
      html`<nile-nav-tab active disabled></nile-nav-tab>`
    );
    expect(disabled.tabIndex).to.equal(-1);
  });

  it('sets aria-label from slotted text (normalized whitespace)', async () => {
    const el = await fixture<NileNavTab>(html`
      <nile-nav-tab>My tab label</nile-nav-tab>
    `);
    await el.updateComplete;
    expect(el.getAttribute('aria-label')).to.equal('My tab label');

    const el2 = await fixture<NileNavTab>(html`
      <nile-nav-tab>My
 tab</nile-nav-tab>
    `);
    await el2.updateComplete;
    expect(el2.getAttribute('aria-label')).to.equal('My tab');
  });

  it('does not overwrite author aria-label when data-auto-aria-label is absent', async () => {
    const el = await fixture<NileNavTab>(html`
      <nile-nav-tab aria-label="Open settings">Home</nile-nav-tab>
    `);
    await el.updateComplete;
    expect(el.getAttribute('aria-label')).to.equal('Open settings');
    expect(el.hasAttribute('data-auto-aria-label')).to.be.false;
  });

  it('auto-derived aria-label updates when text changes', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>A</nile-nav-tab>`);
    await el.updateComplete;
    expect(el.getAttribute('data-auto-aria-label')).to.equal('true');

    const defaultSlot = el.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');
    expect(defaultSlot).to.exist;
    const slot = defaultSlot as HTMLSlotElement;
    const slotChangedDone = new Promise<void>(resolve =>
      slot.addEventListener('slotchange', () => resolve(), { once: true })
    );
    el.textContent = 'B';
    await slotChangedDone;
    expect(el.getAttribute('aria-label')).to.equal('B');
  });

  it('removes aria-label when there is no visible label text', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    await el.updateComplete;
    expect(el.hasAttribute('aria-label')).to.be.false;
    expect(el.hasAttribute('data-auto-aria-label')).to.be.false;
  });

  it('does not leave aria-hidden on host', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.hasAttribute('aria-hidden')).to.be.false;
  });

  it('assigns auto-generated id when host id is empty', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.id).to.match(/^nile-nav-tab-/);
  });

  it('preserves explicit host id', async () => {
    const el = await fixture<NileNavTab>(
      html`<nile-nav-tab id="custom-tab"></nile-nav-tab>`
    );
    expect(el.id).to.equal('custom-tab');
  });

  it('assigns unique ids to separate instances', async () => {
    const wrap = await fixture(html`
      <div>
        <nile-nav-tab></nile-nav-tab>
        <nile-nav-tab></nile-nav-tab>
      </div>
    `);
    const tabs = wrap.querySelectorAll('nile-nav-tab');
    expect(tabs[0].id).to.not.equal(tabs[1].id);
  });

  // ---- Close & click behavior ----

  it('emits nile-close when close button is clicked', async () => {
    const el = await fixture<NileNavTab>(
      html`<nile-nav-tab closable>Tab</nile-nav-tab>`
    );
    let fired = false;
    el.addEventListener('nile-close', () => {
      fired = true;
    });
    const closeButton = el.shadowRoot?.querySelector('nile-icon-button');
    expect(closeButton).to.exist;
    closeButton?.dispatchEvent(
      new MouseEvent('click', { bubbles: true, composed: true })
    );
    expect(fired).to.be.true;
  });

  it('disables close button when tab is disabled', async () => {
    const el = await fixture<NileNavTab>(html`
      <nile-nav-tab closable disabled>Tab</nile-nav-tab>
    `);
    const btn = el.shadowRoot!.querySelector('nile-icon-button')!;
    expect(btn.hasAttribute('disabled')).to.be.true;
  });

  it('prevents default on primary click on nile-link when not disabled', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>Tab</nile-nav-tab>`);
    const link = getLink(el)!;
    const ev = new MouseEvent('click', {
      bubbles: true,
      composed: true,
      cancelable: true,
      button: 0,
    });
    link.dispatchEvent(ev);
    expect(ev.defaultPrevented).to.be.true;
  });

  it('does not prevent default for modified clicks on nile-link (ctrlKey)', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>Tab</nile-nav-tab>`);
    const link = getLink(el)!;
    const ev = new MouseEvent('click', {
      bubbles: true,
      composed: true,
      cancelable: true,
      button: 0,
      ctrlKey: true,
    });
    link.dispatchEvent(ev);
    expect(ev.defaultPrevented).to.be.false;
  });

  it('does not prevent default for metaKey modified click', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>Tab</nile-nav-tab>`);
    const link = getLink(el)!;
    const ev = new MouseEvent('click', {
      bubbles: true,
      composed: true,
      cancelable: true,
      button: 0,
      metaKey: true,
    });
    link.dispatchEvent(ev);
    expect(ev.defaultPrevented).to.be.false;
  });

  it('does not prevent default for non-primary button', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>Tab</nile-nav-tab>`);
    const link = getLink(el)!;
    const ev = new MouseEvent('click', {
      bubbles: true,
      composed: true,
      cancelable: true,
      button: 1,
    });
    link.dispatchEvent(ev);
    expect(ev.defaultPrevented).to.be.false;
  });

  it('blocks click when disabled', async () => {
    const el = await fixture<NileNavTab>(
      html`<nile-nav-tab disabled>Tab</nile-nav-tab>`
    );
    const link = getLink(el)!;
    const ev = new MouseEvent('click', {
      bubbles: true,
      composed: true,
      cancelable: true,
      button: 0,
    });
    link.dispatchEvent(ev);
    expect(ev.defaultPrevented).to.be.true;
  });

  // ---- DOM & lifecycle ----

  it('reports open shadow root mode', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.shadowRoot!.mode).to.equal('open');
  });

  it('uses localName nile-nav-tab', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.localName).to.equal('nile-nav-tab');
  });

  it('is connected when fixture completes', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.isConnected).to.be.true;
  });

  it('disconnects when removed from document', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    el.remove();
    expect(el.isConnected).to.be.false;
  });

  it('closest returns self for nile-nav-tab selector', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.closest('nile-nav-tab')).to.equal(el);
  });

  it('supports focus without throwing', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab active></nile-nav-tab>`);
    expect(() => el.focus()).to.not.throw();
  });

  it('updateComplete resolves after render', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    const done = await el.updateComplete;
    expect(done).to.be.true;
  });

  it('requestUpdate followed by updateComplete refreshes shadow DOM', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>A</nile-nav-tab>`);
    el.closable = true;
    el.requestUpdate();
    await el.updateComplete;
    expect(el.shadowRoot!.querySelector('nile-icon-button')).to.exist;
  });

  it('slot prefix content appears in light tree', async () => {
    const el = await fixture<NileNavTab>(html`
      <nile-nav-tab><span slot="prefix">P</span>Main</nile-nav-tab>
    `);
    expect(el.textContent).to.contain('P');
    expect(el.textContent).to.contain('Main');
  });

  it('slot suffix content appears in light tree', async () => {
    const el = await fixture<NileNavTab>(html`
      <nile-nav-tab>Main<span slot="suffix">S</span></nile-nav-tab>
    `);
    expect(el.textContent).to.contain('S');
  });

  it('updates panel via property and reflects attribute', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    el.panel = 'pane-a';
    await el.updateComplete;
    expect(el.getAttribute('panel')).to.equal('pane-a');
  });

  it('toggles centered class on link when centered changes', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    el.centered = true;
    await el.updateComplete;
    expect(getLink(el)!.classList.contains('tab--centered')).to.be.true;
    el.centered = false;
    await el.updateComplete;
    expect(getLink(el)!.classList.contains('tab--centered')).to.be.false;
  });

  it('removes nav-link active class when active cleared', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab active>X</nile-nav-tab>`);
    el.active = false;
    await el.updateComplete;
    expect(getLink(el)!.classList.contains('nav-tab--active')).to.be.false;
  });

  it('dispatches bubbled custom events from host', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    let hit = false;
    el.addEventListener('x-test', () => {
      hit = true;
    });
    el.dispatchEvent(new CustomEvent('x-test', { bubbles: true }));
    expect(hit).to.be.true;
  });

  it('exposes getBoundingClientRect on host', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>Tab</nile-nav-tab>`);
    const rect = el.getBoundingClientRect();
    expect(rect.height).to.be.a('number');
    expect(rect.width).to.be.a('number');
  });

  // ---- Extended coverage ----

  it('does not prevent default for shiftKey modified click on nile-link', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>Tab</nile-nav-tab>`);
    const link = getLink(el)!;
    const ev = new MouseEvent('click', { bubbles: true, composed: true, cancelable: true, button: 0, shiftKey: true });
    link.dispatchEvent(ev);
    expect(ev.defaultPrevented).to.be.false;
  });

  it('does not prevent default for altKey modified click on nile-link', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>Tab</nile-nav-tab>`);
    const link = getLink(el)!;
    const ev = new MouseEvent('click', { bubbles: true, composed: true, cancelable: true, button: 0, altKey: true });
    link.dispatchEvent(ev);
    expect(ev.defaultPrevented).to.be.false;
  });

  it('does not emit nile-close when closable but close not activated', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab closable>T</nile-nav-tab>`);
    let n = 0;
    el.addEventListener('nile-close', () => n++);
    const link = getLink(el);
    expect(link).to.exist;
    link?.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
    expect(n).to.equal(0);
  });

  it('close click uses stopPropagation so parent sees fewer bubbled clicks', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab closable>T</nile-nav-tab>`);
    let parentClicks = 0;
    el.addEventListener('click', () => parentClicks++);
    const closeButton = el.shadowRoot?.querySelector('nile-icon-button');
    expect(closeButton).to.exist;
    closeButton?.dispatchEvent(
      new MouseEvent('click', { bubbles: true, composed: true })
    );
    expect(parentClicks).to.equal(0);
  });

  it('syncs panel property when setAttribute panel is used', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    el.setAttribute('panel', 'via-attr');
    await el.updateComplete;
    expect(el.panel).to.equal('via-attr');
  });

  it('removing active attribute clears link active class', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab active>X</nile-nav-tab>`);
    el.removeAttribute('active');
    await el.updateComplete;
    expect(el.active).to.be.false;
    expect(getLink(el)!.classList.contains('nav-tab--active')).to.be.false;
  });

  it('tagName is uppercase NileNavTab-style', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.tagName.toLowerCase()).to.equal('nile-nav-tab');
  });

  it('creates via createElement and upgrades with shadow', async () => {
    const el = document.createElement('nile-nav-tab') as NileNavTab;
    document.body.appendChild(el);
    try{
      await el.updateComplete;
      expect(el.shadowRoot).to.not.be.null;
    } finally {
      el.remove();
    }
  });

  it('disabled then enabled restores tabIndex when active', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab active disabled>Z</nile-nav-tab>`);
    el.disabled = false;
    await el.updateComplete;
    expect(el.tabIndex).to.equal(0);
  });

  it('active inactive active toggles tabIndex correctly', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>Q</nile-nav-tab>`);
    el.active = true;
    await el.updateComplete;
    expect(el.tabIndex).to.equal(0);
    el.active = false;
    await el.updateComplete;
    expect(el.tabIndex).to.equal(-1);
  });

  it('aria-label removes when label becomes whitespace only', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>x</nile-nav-tab>`);
    await el.updateComplete;
    el.textContent = '   ';
    await el.updateComplete;
    expect(el.hasAttribute('aria-label')).to.be.false;
    expect(el.hasAttribute('data-auto-aria-label')).to.be.false;
  });

  it('href on inner anchor updates when link changes second time', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab link="https://a/"></nile-nav-tab>`);
    el.link = 'https://b/';
    await el.updateComplete;
    expect(getInnerAnchor(el)!.getAttribute('href')).to.equal('https://b/');
  });

  it('nile-link has href binding present when link empty string', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab link=""></nile-nav-tab>`);
    await el.updateComplete;
    const anchor = getInnerAnchor(el);
    expect(anchor).to.exist;
  });

  it('closable close button is inside nile-link sibling structure', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab closable>C</nile-nav-tab>`);
    expect(el.shadowRoot!.querySelector('nile-link')).to.exist;
    expect(el.shadowRoot!.querySelector('nile-icon-button')).to.exist;
  });

  it('list item wrapper uses role presentation', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    const li = el.shadowRoot!.querySelector('.nav-tab-container');
    expect(li!.getAttribute('role')).to.equal('presentation');
  });

  it('matches selector :host not needed but host matches tag', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab class="nt"></nile-nav-tab>`);
    expect(el.matches('nile-nav-tab.nt')).to.be.true;
  });

  it('blur is callable on host', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab active>B</nile-nav-tab>`);
    el.focus();
    expect(() => el.blur()).to.not.throw();
  });

  it('does not prevent default when metaKey and shiftKey together', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>T</nile-nav-tab>`);
    const ev = new MouseEvent('click', {
      bubbles: true,
      composed: true,
      cancelable: true,
      button: 0,
      metaKey: true,
      shiftKey: true,
    });
    getLink(el)!.dispatchEvent(ev);
    expect(ev.defaultPrevented).to.be.false;
  });

  it('allows panel id with hyphen and digits', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab panel="tab-99"></nile-nav-tab>`);
    expect(el.panel).to.equal('tab-99');
  });

  it('prefix and suffix slots compose label text', async () => {
    const el = await fixture<NileNavTab>(html`
      <nile-nav-tab><span slot="prefix">Pre</span>Mid<span slot="suffix">Suf</span></nile-nav-tab>
    `);
    await el.updateComplete;
    expect(el.textContent).to.contain('Pre');
    expect(el.textContent).to.contain('Mid');
    expect(el.textContent).to.contain('Suf');
  });

  it('aria-label includes digits from slotted text', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>Tab 42</nile-nav-tab>`);
    await el.updateComplete;
    expect(el.getAttribute('aria-label')).to.equal('Tab 42');
  });

  it('link with relative path appears on anchor', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab link="/rel/path"></nile-nav-tab>`);
    await el.updateComplete;
    expect(getInnerAnchor(el)!.getAttribute('href')).to.equal('/rel/path');
  });

  it('closable true and link set coexist', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab closable link="https://x/">X</nile-nav-tab>`);
    await el.updateComplete;
    expect(el.shadowRoot!.querySelector('nile-icon-button')).to.exist;
    expect(getInnerAnchor(el)!.getAttribute('href')).to.equal('https://x/');
  });

  it('nile-close fires at most once per close button activation', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab closable>Z</nile-nav-tab>`);
    let n = 0;
    el.addEventListener('nile-close', () => n++);
    const btn = el.shadowRoot?.querySelector('nile-icon-button');
    expect(btn).to.exist;
    btn?.dispatchEvent(new MouseEvent('click', { bubbles: true, composed: true }));
    expect(n).to.equal(1);
  });

  it('host has no aria-selected until upgraded then reflects active', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab></nile-nav-tab>`);
    expect(el.getAttribute('aria-selected')).to.equal('false');
    el.active = true;
    await el.updateComplete;
    expect(el.getAttribute('aria-selected')).to.equal('true');
  });

  it('removeAttribute disabled restores aria-disabled false', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab disabled>D</nile-nav-tab>`);
    el.removeAttribute('disabled');
    await el.updateComplete;
    expect(el.getAttribute('aria-disabled')).to.equal('false');
  });

  it('setAttribute disabled without property uses reflected state', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>A</nile-nav-tab>`);
    el.setAttribute('disabled', '');
    await el.updateComplete;
    expect(el.disabled).to.be.true;
  });

  it('childNodes in light DOM include slotted text node', async () => {
    const el = await fixture<NileNavTab>(html`<nile-nav-tab>K</nile-nav-tab>`);
    expect(el.childNodes.length).to.be.at.least(1);
  });

  /**
   * 100 generated cases — compiled to dist/src/nile-nav-tab/nile-nav-tab.test.js via `yarn tsc`.
   * Each iteration asserts a small invariant (panel, active, link, closable, centered, disabled, a11y, DOM).
   */
  describe('bulk generated coverage (100 cases)', () => {
    for (let i = 0; i < 100; i++) {
      const suffix = `${i}`;
      it(`bulk case ${i + 1}/100 (mode ${i % 10})`, async () => {
        const el = await fixture<NileNavTab>(html`<nile-nav-tab>T${suffix}</nile-nav-tab>`);
        await el.updateComplete;
        const mode = i % 10;

        if (mode === 0) {
          el.panel = `panel-${suffix}`;
          await el.updateComplete;
          expect(el.getAttribute('panel')).to.equal(`panel-${suffix}`);
        } else if (mode === 1) {
          el.active = true;
          await el.updateComplete;
          expect(el.getAttribute('aria-selected')).to.equal('true');
          expect(el.tabIndex).to.equal(0);
        } else if (mode === 2) {
          el.link = `https://example.test/${suffix}`;
          await el.updateComplete;
          expect(getInnerAnchor(el)!.getAttribute('href')).to.equal(`https://example.test/${suffix}`);
        } else if (mode === 3) {
          el.closable = true;
          await el.updateComplete;
          expect(el.shadowRoot!.querySelector('nile-icon-button')).to.exist;
          expect(getLink(el)!.classList.contains('nav-tab--closable')).to.be.true;
        } else if (mode === 4) {
          el.centered = true;
          await el.updateComplete;
          expect(getLink(el)!.classList.contains('tab--centered')).to.be.true;
        } else if (mode === 5) {
          el.disabled = true;
          await el.updateComplete;
          expect(el.getAttribute('aria-disabled')).to.equal('true');
          expect(el.tabIndex).to.equal(-1);
        } else if (mode === 6) {
          el.active = true;
          el.disabled = true;
          await el.updateComplete;
          expect(el.tabIndex).to.equal(-1);
        } else if (mode === 7) {
          el.setAttribute('panel', `attr-${suffix}`);
          await el.updateComplete;
          expect(el.panel).to.equal(`attr-${suffix}`);
        } else if (mode === 8) {
          expect(el.getAttribute('role')).to.equal('tab');
          expect(el.shadowRoot!.querySelector('.nav-tab-container')).to.exist;
        } else {
          const link = getLink(el)!;
          const ev = new MouseEvent('click', {
            bubbles: true,
            composed: true,
            cancelable: true,
            button: 0,
            ctrlKey: Boolean(i % 2),
          });
          link.dispatchEvent(ev);
          expect(ev.defaultPrevented).to.equal(!Boolean(i % 2));
        }
      });
    }
  });
});

