// @vitest-environment happy-dom
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
import { nextTick } from 'vue'

import BrowserExtensionCard from '../../src/components/settings/BrowserExtensionCard.vue'
import { freshPinia, mountComponent } from '../support/mountComponent.js'

// useApi is a thin fetch wrapper, so the seam is fetch itself (same shape as
// showcase.spec.js) rather than a module mock.
function stubApi(manifest) {
  globalThis.fetch = vi.fn(async (url) => {
    const payload = String(url).includes('/api/extension/manifest')
      ? manifest
      : { key: 'test-key' }
    return {
      ok: true, status: 200, statusText: '200',
      text: async () => JSON.stringify(payload),
    }
  })
}

async function mountCard(manifest) {
  stubApi(manifest)
  const w = mountComponent(BrowserExtensionCard, { pinia: freshPinia() })
  // onMounted fires two fetches (manifest + key) and each resolves through a
  // chain of microtasks. Yielding to a macrotask drains the whole queue, which
  // a fixed number of nextTicks would only do by luck.
  await new Promise((resolve) => setTimeout(resolve, 0))
  await nextTick()
  return w
}

const INSTALLED = {
  installed: true,
  version: '1.0.3499884',
  xpi_url: '/extension/fabledcurator-1.0.3499884.xpi',
  latest_url: '/extension/fabledcurator-latest.xpi',
  sha256: 'abc',
}

describe('BrowserExtensionCard — channel', () => {
  beforeEach(() => { vi.restoreAllMocks() })
  afterEach(() => { delete globalThis.fetch })

  it('names the channel the instance reports', async () => {
    // The point of the whole channel scheme: an operator can tell a dev
    // instance from a main one without installing anything.
    const w = await mountCard({ ...INSTALLED, channel: 'dev' })
    expect(w.text()).toContain('dev')
  })

  it('shows the version and the channel as SEPARATE text, never merged', async () => {
    // Regression guard with teeth: the tempting shortcut is a `-dev` version
    // suffix, and that is precisely what breaks the extension's comparator —
    // it parses each dotted segment with parseInt, so a suffixed segment reads
    // as 0 and every dev build compares equal to every other. If someone ever
    // "simplifies" by folding the channel into the version, the version text
    // stops being the bare derived number and this fails.
    const w = await mountCard({ ...INSTALLED, channel: 'dev' })
    expect(w.text()).toContain('v1.0.3499884')
    expect(w.text()).not.toContain('1.0.3499884-dev')
  })

  it('renders no channel when the instance declares none', async () => {
    // A locally-built image, or one predating the field. The card must read
    // exactly as it did before the channel existed rather than inventing an
    // "unknown" badge — absence is a normal answer here, not a fault.
    const w = await mountCard(INSTALLED)
    expect(w.text()).toContain('v1.0.3499884')
    expect(w.findAll('v-chip')).toHaveLength(0)
  })
})
