/**
 * pagination-ui — regression guards for the v0.6.36 native-primitive
 * leak fix and the universal [size] system threading.
 *
 * Before the fix, every page / prev / next item was a raw <button>,
 * and pagination hardcoded `--a-size-sm` for its button cells (no
 * [size] prop, no threading to the substrate's 24/30/36 px size
 * system). After the fix, every item is a <button-ui> whose `size`
 * attribute mirrors the host's, and active items render as
 * `variant="primary"` so the filled-accent state comes from
 * button-ui's primary surface matrix.
 */

import { describe, it, expect, beforeEach } from 'vitest';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';
import '../../core/element.js';
import './pagination.js';
import '../button/button.js';

const HERE = dirname(fileURLToPath(import.meta.url));
const CLASS_JS = readFileSync(resolve(HERE, 'pagination.class.js'), 'utf8');

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;
}

beforeEach(() => { document.body.innerHTML = ''; });

// ── Source-grep contract guards ─────────────────────────────────────

describe('pagination-ui — no native <button> leak', () => {
  it('class.js creates <button-ui> elements (not raw <button>)', () => {
    expect(CLASS_JS).toMatch(/createElement\(['"]button-ui['"]\)/);
    // Negative — guard against accidental revert to raw <button>.
    expect(CLASS_JS).not.toMatch(/createElement\(['"]button['"]\)/);
  });

  it('declares caret-left + caret-right in static requiredIcons', () => {
    expect(CLASS_JS).toMatch(/static\s+requiredIcons\s*=\s*\[[^\]]*['"]caret-left['"]/);
    expect(CLASS_JS).toMatch(/static\s+requiredIcons\s*=\s*\[[^\]]*['"]caret-right['"]/);
  });

  it('declares size prop in static properties (universal [size] thread-through)', () => {
    expect(CLASS_JS).toMatch(/static\s+properties\s*=[\s\S]*?size:\s*\{\s*type:\s*String/);
  });
});

// ── DOM contract guards ─────────────────────────────────────────────

describe('pagination-ui — DOM composition', () => {
  it('stamps <button-ui> for every page / prev / next cell', async () => {
    const el = mount('<pagination-ui page="3" total="10"></pagination-ui>');
    await tick();
    const buttons = el.querySelectorAll('button-ui');
    expect(buttons.length).toBeGreaterThan(0);
    // No raw <button> children — confirms the native-leak is closed at runtime.
    expect(el.querySelectorAll(':scope button:not(button-ui)').length).toBe(0);
  });

  it('threads size attribute through to every nested button-ui', async () => {
    const el = mount('<pagination-ui page="2" total="5" size="lg"></pagination-ui>');
    await tick();
    const buttons = el.querySelectorAll('button-ui');
    expect(buttons.length).toBeGreaterThan(0);
    for (const btn of buttons) {
      expect(btn.getAttribute('size')).toBe('lg');
    }
  });

  it('defaults to size="md" (aligned with button-ui default)', async () => {
    const el = mount('<pagination-ui page="2" total="5"></pagination-ui>');
    await tick();
    const buttons = el.querySelectorAll('button-ui');
    expect(buttons.length).toBeGreaterThan(0);
    for (const btn of buttons) {
      expect(btn.getAttribute('size')).toBe('md');
    }
  });

  it('renders the active page as variant="primary" (button-ui token chain)', async () => {
    const el = mount('<pagination-ui page="3" total="10"></pagination-ui>');
    await tick();
    const active = el.querySelector('button-ui[data-active]');
    expect(active).toBeTruthy();
    expect(active.getAttribute('variant')).toBe('primary');
    expect(active.getAttribute('aria-current')).toBe('page');
  });

  it('renders prev / next as ghost (default variant) or outline (button variant)', async () => {
    const elDefault = mount('<pagination-ui page="3" total="10"></pagination-ui>');
    await tick();
    expect(elDefault.querySelector('button-ui[data-prev]').getAttribute('variant')).toBe('ghost');
    expect(elDefault.querySelector('button-ui[data-next]').getAttribute('variant')).toBe('ghost');

    const elButton = mount('<pagination-ui page="3" total="10" variant="button"></pagination-ui>');
    await tick();
    expect(elButton.querySelector('button-ui[data-prev]').getAttribute('variant')).toBe('outline');
    expect(elButton.querySelector('button-ui[data-next]').getAttribute('variant')).toBe('outline');
  });

  it('uses icon="caret-left" / "caret-right" on prev / next (not glyph text)', async () => {
    const el = mount('<pagination-ui page="3" total="10"></pagination-ui>');
    await tick();
    expect(el.querySelector('button-ui[data-prev]').getAttribute('icon')).toBe('caret-left');
    expect(el.querySelector('button-ui[data-next]').getAttribute('icon')).toBe('caret-right');
  });
});

// ── Width invariance: compact-mode cell count is constant across pages ──
//
// Compact width W = 2*siblings + 5 (= 7 at siblings=1). When the current
// page advances by one, the row's visible cell count must NOT change —
// otherwise the layout wobbles. For total ≤ W, we show every page and
// skip compact mode entirely (no point compressing if it doesn't save
// slots — also wobble-free since every page just highlights a different
// constant-width row).

describe('pagination-ui — compact-mode width invariance', () => {
  function pageValues(el) {
    return [...el.querySelectorAll('button-ui[data-page]')].map((b) => Number(b.dataset.value));
  }
  function cellCount(el) {
    return el.querySelectorAll('button-ui[data-page], [data-ellipsis]').length;
  }
  function hasEllipsis(el) {
    return el.querySelectorAll('[data-ellipsis]').length > 0;
  }

  it('total=5 siblings=1 → never compacts (shows 1-5 every page)', async () => {
    for (const page of [1, 2, 3, 4, 5]) {
      const el = mount(`<pagination-ui page="${page}" total="5" siblings="1"></pagination-ui>`);
      await tick();
      expect(pageValues(el)).toEqual([1, 2, 3, 4, 5]);
      expect(hasEllipsis(el)).toBe(false);
    }
  });

  it('total=10 siblings=1 → exactly 7 cells on every page (no wobble)', async () => {
    for (const page of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]) {
      const el = mount(`<pagination-ui page="${page}" total="10" siblings="1"></pagination-ui>`);
      await tick();
      expect(cellCount(el)).toBe(7);
    }
  });

  it('total=10 page=3 siblings=1 → near-start: [1,2,3,4,5,…,10]', async () => {
    const el = mount('<pagination-ui page="3" total="10" siblings="1"></pagination-ui>');
    await tick();
    expect(pageValues(el)).toEqual([1, 2, 3, 4, 5, 10]);
    expect(el.querySelectorAll('[data-ellipsis]').length).toBe(1);
  });

  it('total=10 page=4 siblings=1 → still near-start: [1,2,3,4,5,…,10] (same as page=3)', async () => {
    const el = mount('<pagination-ui page="4" total="10" siblings="1"></pagination-ui>');
    await tick();
    expect(pageValues(el)).toEqual([1, 2, 3, 4, 5, 10]);
    expect(el.querySelectorAll('[data-ellipsis]').length).toBe(1);
  });

  it('total=10 page=5 siblings=1 → middle: [1,…,4,5,6,…,10]', async () => {
    const el = mount('<pagination-ui page="5" total="10" siblings="1"></pagination-ui>');
    await tick();
    expect(pageValues(el)).toEqual([1, 4, 5, 6, 10]);
    expect(el.querySelectorAll('[data-ellipsis]').length).toBe(2);
  });

  it('total=10 page=7 siblings=1 → near-end: [1,…,6,7,8,9,10]', async () => {
    const el = mount('<pagination-ui page="7" total="10" siblings="1"></pagination-ui>');
    await tick();
    expect(pageValues(el)).toEqual([1, 6, 7, 8, 9, 10]);
    expect(el.querySelectorAll('[data-ellipsis]').length).toBe(1);
  });

  it('total=20 page=10 siblings=1 → middle: [1,…,9,10,11,…,20]', async () => {
    const el = mount('<pagination-ui page="10" total="20" siblings="1"></pagination-ui>');
    await tick();
    expect(pageValues(el)).toEqual([1, 9, 10, 11, 20]);
    expect(el.querySelectorAll('[data-ellipsis]').length).toBe(2);
  });

  it('total=20 siblings=2 → exactly 9 cells on every page', async () => {
    for (const page of [1, 5, 10, 15, 20]) {
      const el = mount(`<pagination-ui page="${page}" total="20" siblings="2"></pagination-ui>`);
      await tick();
      expect(cellCount(el)).toBe(9);
    }
  });
});
