import { describe, it, expect, beforeEach } from 'vitest';
import {
  installIconLoaders,
  registerIcon,
  hasIcon,
  isIconName,
  getIcon,
  listIcons,
} from './icons.js';

// Tests for `installIconLoaders` + `resolveLoader` + the §140 suffix-
// fallback fix for FEEDBACK-06 (color-app's `installIconLoaders` →
// `warnMissingIcon` failures on `caret-right` / `x` / `caret-up-down`
// when the consumer's `modules` keys didn't match the canonical
// `/node_modules/@phosphor-icons/core/assets/<weight>/<name>.svg` path.
//
// `isIconName` is the SYNC probe — it calls `resolveLoader` without
// triggering an async load. That's what we need to assert the fallback
// logic finds the loader regardless of path shape.

const CARET_RIGHT_SVG = '<svg data-icon="caret-right"/>';
const X_SVG = '<svg data-icon="x"/>';
const CARET_UP_DOWN_SVG = '<svg data-icon="caret-up-down"/>';

describe('installIconLoaders + resolveLoader (§140 suffix fallback)', () => {
  beforeEach(() => {
    // Reset to a known state. Note: the registry Map itself is module-
    // singleton + doesn't reset between tests, so we keep test SVG
    // contents distinct from any real phosphor names just in case.
    installIconLoaders({ regular: {}, thin: {}, light: {}, bold: {}, fill: {}, duotone: {} });
  });

  it('resolves by canonical phosphor path (fast path)', () => {
    installIconLoaders({
      regular: {
        '/node_modules/@phosphor-icons/core/assets/regular/caret-right.svg': CARET_RIGHT_SVG,
        '/node_modules/@phosphor-icons/core/assets/regular/x.svg': X_SVG,
      },
    });

    expect(isIconName('caret-right')).toBe(true);
    expect(isIconName('x')).toBe(true);
  });

  it('resolves by filename suffix when key has a pnpm-style prefix', () => {
    // pnpm hoists phosphor inside `.pnpm/<pkg>@<ver>/node_modules/...`
    installIconLoaders({
      regular: {
        '/node_modules/.pnpm/@phosphor-icons+core@2.1.1/node_modules/@phosphor-icons/core/assets/regular/caret-right.svg':
          CARET_RIGHT_SVG,
      },
    });

    expect(isIconName('caret-right')).toBe(true);
  });

  it('resolves by filename suffix when key is a custom Vite-root absolute path', () => {
    // Custom Vite root puts node_modules at a deeper location.
    installIconLoaders({
      regular: {
        '/packages/my-app/node_modules/@phosphor-icons/core/assets/regular/caret-up-down.svg':
          CARET_UP_DOWN_SVG,
      },
    });

    expect(isIconName('caret-up-down')).toBe(true);
  });

  it('resolves by filename suffix for an arbitrary manually-built loader map', () => {
    // Manual loader maps may have any keys at all — only the filename matters.
    installIconLoaders({
      regular: {
        '/anywhere/at/all/caret-right.svg': CARET_RIGHT_SVG,
        '/another/path/entirely/x.svg': X_SVG,
      },
    });

    expect(isIconName('caret-right')).toBe(true);
    expect(isIconName('x')).toBe(true);
  });

  it('returns false on miss across both paths', () => {
    installIconLoaders({
      regular: {
        '/node_modules/@phosphor-icons/core/assets/regular/caret-right.svg': CARET_RIGHT_SVG,
      },
    });

    expect(isIconName('not-a-real-icon-name')).toBe(false);
  });

  it('resolves `-fill` back-compat suffix in name via fast path', () => {
    const STAR_FILL_SVG = '<svg data-icon="star-fill"/>';
    installIconLoaders({
      regular: {},
      fill: {
        '/node_modules/@phosphor-icons/core/assets/fill/star-fill.svg': STAR_FILL_SVG,
      },
    });

    // `name="star-fill"` with default weight=regular falls back to fill weight.
    expect(isIconName('star-fill')).toBe(true);
  });

  it('resolves `-fill` back-compat suffix in name via suffix fallback', () => {
    const STAR_FILL_SVG = '<svg data-icon="star-fill"/>';
    installIconLoaders({
      regular: {},
      fill: {
        '/anywhere/star-fill.svg': STAR_FILL_SVG,
      },
    });

    expect(isIconName('star-fill')).toBe(true);
  });

  it('resolves non-regular weights (bold, fill, etc.) via suffix fallback', () => {
    const HEART_BOLD_SVG = '<svg data-icon="heart-bold"/>';
    installIconLoaders({
      regular: {},
      bold: {
        '/anywhere/heart-bold.svg': HEART_BOLD_SVG,
      },
    });

    expect(isIconName('heart', 'bold')).toBe(true);
  });

  it('isIconName returns true via alias resolution + suffix fallback', () => {
    // `send` → `paper-plane-right` in ICON_ALIASES. Suffix fallback should
    // still match the aliased filename.
    const PAPER_PLANE_SVG = '<svg data-icon="paper-plane-right"/>';
    installIconLoaders({
      regular: {
        '/anywhere/paper-plane-right.svg': PAPER_PLANE_SVG,
      },
    });

    expect(isIconName('send')).toBe(true);
  });

  it('end-to-end via DOM re-stamp: installIconLoaders auto-loads existing <icon-ui[name]>', async () => {
    // The §140 fix's primary symptom: `installIconLoaders` walks existing
    // `<icon-ui[name]>` elements + calls `loadIcon` for each. Without the
    // suffix fallback, `loadIcon` → `resolveLoader` returns null →
    // `warnMissingIcon` fires. With the fix, the load completes.
    const PAW_SVG = '<svg data-icon="paw"/>';

    // Plant an `<icon-ui[name="paw"]>` element. We don't need it to
    // actually upgrade; we just need `querySelectorAll('icon-ui[name]')`
    // to find it.
    const el = document.createElement('icon-ui');
    el.setAttribute('name', 'paw');
    document.body.appendChild(el);

    // Install a loader map with a pnpm-style path (suffix fallback exercise).
    installIconLoaders({
      regular: {
        '/some/non-canonical/prefix/paw.svg': PAW_SVG,
      },
    });

    // `loadIcon` runs sync (loader is a string, not a function), so the
    // registry is written by the time `installIconLoaders` returns.
    expect(hasIcon('paw')).toBe(true);

    document.body.removeChild(el);
  });
});

describe('registerIcon / hasIcon — unchanged by §140', () => {
  it('registerIcon adds an icon by name without needing a loader map', () => {
    const CUSTOM_SVG = '<svg data-icon="custom-§140-marker"/>';
    registerIcon('my-§140-custom-icon', CUSTOM_SVG);

    expect(hasIcon('my-§140-custom-icon')).toBe(true);
    expect(getIcon('my-§140-custom-icon')).toBe(CUSTOM_SVG);
  });

  it('listIcons returns registered names', () => {
    registerIcon('§140-listIcons-marker-one', '<svg/>');
    registerIcon('§140-listIcons-marker-two', '<svg/>');

    expect(listIcons().some((k) => k.includes('§140-listIcons-marker-one'))).toBe(true);
    expect(listIcons().some((k) => k.includes('§140-listIcons-marker-two'))).toBe(true);
  });
});
