/**
 * tag-ui regression tests.
 *
 * Source-grep contract for the `content: attr(text)` gating pattern.
 * Pre-fix (ungated `:scope::after { content: attr(text) }`), an empty
 * `attr(text)` still generated a zero-width pseudo-element flex item,
 * and the host's `inline-flex` + `gap` added a phantom `--tag-gap`
 * trailing space after slotted content like `<tag-ui>Beta</tag-ui>`.
 * The fix gates the pseudo-element on `[text]` attribute presence, so
 * slot-content consumers don't pay the gap cost.
 *
 * jsdom doesn't evaluate `@scope` rules via `getComputedStyle()`, so
 * we validate the CSS source shape directly (same recipe as
 * description-list.test.js and text.test.js).
 */

import { describe, it, expect } from 'vitest';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';

const HERE = dirname(fileURLToPath(import.meta.url));
const TAG_CSS = readFileSync(resolve(HERE, 'tag.css'), 'utf8');

describe('tag-ui — content: attr(text) gating', () => {
  it('gates the ::after pseudo-element on [text] attribute presence', () => {
    // The fixed rule: `:scope[text]::after { content: attr(text); }`
    expect(TAG_CSS).toMatch(/:scope\[text\]::after\s*\{[^}]*content:\s*attr\(text\)/);
  });

  it('does NOT carry an ungated :scope::after with content: attr(text)', () => {
    // The pre-fix shape would add phantom flex-gap space after slotted
    // content. Match the bare `:scope::after` form (no attribute
    // selector chained) followed by the attr(text) declaration.
    expect(TAG_CSS).not.toMatch(/:scope::after\s*\{[^}]*content:\s*attr\(text\)/);
  });

  it('host :scope still declares the flex + gap layout (gap is real, just no longer applied to an empty pseudo)', () => {
    // Sanity check that the fix didn't accidentally remove the host
    // flex/gap declarations — those are still load-bearing for the
    // [icon] + [dismiss] slot rendering and for proper :scope[text]
    // gap rendering when text IS set.
    expect(TAG_CSS).toMatch(/:scope\s*\{[^}]*display:\s*inline-flex/);
    // Tokens are read directly via `var(--prop)`; consumers override the
    // un-suffixed token on the element (or via @layer overrides).
    expect(TAG_CSS).toMatch(/:scope\s*\{[^}]*gap:\s*var\(--tag-gap\)/);
  });
});

// ── Tone defaults (solid by default for families; muted as opt-out) ──
//
// Default tone is `solid` for family variants — the base `:scope[variant=…]`
// rules use the saturated-fill pair `--a-{family}-bg` + `--a-{family}-fg`
// (the chip IS the state). `[tone="muted"]` opts back into the canonical
// muted pair `--a-{family}-muted` + `--a-{family}-text` (same look as
// <badge-ui>'s default). The `default` (no-family) variant stays quiet
// chrome unless `[tone="solid"]` is set explicitly (then it inverts
// to fg/bg for a high-contrast neutral stamp).

describe('tag-ui — variant defaults to solid fill', () => {
  it.each([
    ['info'],
    ['success'],
    ['danger'],
  ])('[variant="%s"] base rule uses --a-{family}-bg + --a-{family}-fg', (family) => {
    const block = new RegExp(
      `:scope\\[variant="${family}"\\]\\s*\\{[^}]*` +
      `--tag-bg:\\s*var\\(--a-${family}-bg\\)[^}]*` +
      `--tag-fg:\\s*var\\(--a-${family}-fg\\)`,
      's'
    );
    expect(TAG_CSS).toMatch(block);
  });

  it('[variant="warning"] uses the canonical --a-warning-bg + --a-warning-fg pair', () => {
    // v0.6.36 token-system fix: --a-warning-bg now redirects to
    // --a-warning-20-tint at the L3 layer (semantics.css), so consumers
    // can pair `-bg` + `-fg` cleanly without local overrides. The pair
    // resolves to bright-amber-bg + dark-brown-text in both schemes.
    expect(TAG_CSS).toMatch(
      /:scope\[variant="warning"\]\s*\{[^}]*--tag-bg:\s*var\(--a-warning-bg\)[^}]*--tag-fg:\s*var\(--a-warning-fg\)/s
    );
  });
});

describe('tag-ui — [tone="muted"] opts out to canonical muted pair', () => {
  it.each([
    ['info'],
    ['success'],
    ['warning'],
    ['danger'],
  ])('[tone="muted"][variant="%s"] uses --a-{family}-muted + --a-{family}-text', (family) => {
    const block = new RegExp(
      `:scope\\[tone="muted"\\]\\[variant="${family}"\\]\\s*\\{[^}]*` +
      `--tag-bg:\\s*var\\(--a-${family}-muted\\)[^}]*` +
      `--tag-fg:\\s*var\\(--a-${family}-text\\)`,
      's'
    );
    expect(TAG_CSS).toMatch(block);
  });
});

describe('tag-ui — neutral default + explicit solid stamp', () => {
  it('[variant="default"] stays quiet chrome (--a-bg-muted + --a-fg)', () => {
    expect(TAG_CSS).toMatch(
      /:scope\[variant="default"\]\s*\{[^}]*--tag-bg:\s*var\(--a-bg-muted\)[^}]*--tag-fg:\s*var\(--a-fg\)/s
    );
  });

  it('[tone="solid"] without a family inverts to fg/bg high-contrast stamp', () => {
    expect(TAG_CSS).toMatch(
      /:scope\[tone="solid"\]:not\(\[variant="info"\]\)[\s\S]*?--tag-bg:\s*var\(--a-fg\)[\s\S]*?--tag-fg:\s*var\(--a-bg\)/
    );
  });
});

describe('tag-ui — [tone="outline"] strips fill + colors border', () => {
  it('[tone="outline"] base rule sets bg to transparent', () => {
    expect(TAG_CSS).toMatch(
      /:scope\[tone="outline"\]\s*\{[^}]*--tag-bg:\s*transparent/s
    );
  });

  it.each([
    ['info'],
    ['success'],
    ['warning'],
    ['danger'],
  ])('[tone="outline"][variant="%s"] colors fg + border per family', (family) => {
    const block = new RegExp(
      `:scope\\[tone="outline"\\]\\[variant="${family}"\\]\\s*\\{[^}]*` +
      `--tag-fg:\\s*var\\(--a-${family}-text\\)[^}]*` +
      `--tag-border:\\s*var\\(--a-${family}-border\\)`,
      's'
    );
    expect(TAG_CSS).toMatch(block);
  });

  it('[tone="outline"] on neutral uses --a-fg-muted text + --a-border ring', () => {
    expect(TAG_CSS).toMatch(
      /:scope\[tone="outline"\]:not\(\[variant="info"\]\)[\s\S]*?--tag-fg:\s*var\(--a-fg-muted\)[\s\S]*?--tag-border:\s*var\(--a-border\)/
    );
  });
});
