// @vitest-environment happy-dom
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { fireEvent, render, screen, waitFor } from '@testing-library/react'

// Drive the Script Tags list through a mocked cmsApi so the test owns the
// data and can assert the design-parity surface: stat pills, search,
// location/consent filter pills, badges, and row actions.
function makeTag(over: Record<string, unknown> = {}) {
  return {
    id: 't1',
    name: 'Google Analytics 4',
    code: '<script>/* ga */</script>',
    placement: 'head',
    scope: 'site',
    targetPaths: [],
    priority: 1,
    enabled: true,
    loadAsync: true,
    disableInPreview: true,
    consentCategory: 'analytics',
    createdAt: '2026-05-20T10:00:00.000Z',
    updatedAt: '2026-05-20T10:00:00.000Z',
    ...over,
  }
}

const TAGS = [
  makeTag(),
  makeTag({
    id: 't2',
    name: 'Live Chat Widget',
    placement: 'body_close',
    priority: 5,
    consentCategory: 'functional',
  }),
  makeTag({
    id: 't3',
    name: 'Org Schema',
    placement: 'head',
    priority: 3,
    enabled: false,
    consentCategory: 'none',
  }),
]

const cmsApi = vi.fn(async (endpoint: string, init?: RequestInit) => {
  if (!init?.method) return { data: TAGS }
  if (init.method === 'POST') return { data: makeTag({ id: 'new-copy' }) }
  return { data: {} }
})

vi.mock('../../lib/api.js', () => ({
  cmsApi: (endpoint: string, init?: RequestInit) => cmsApi(endpoint, init),
}))

vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn(), loading: vi.fn() } }))

const { ScriptTags } = await import('../../views/ScriptTags.js')

beforeEach(() => {
  cmsApi.mockClear()
})

describe('ScriptTags list', () => {
  it('renders rows, stat pills (incl. consent-gated), and colored badges', async () => {
    render(<ScriptTags />)

    expect(await screen.findByText('Google Analytics 4')).toBeTruthy()
    expect(screen.getByText('Live Chat Widget')).toBeTruthy()

    // Stat pills: 3 total, 2 active, 1 inactive, 2 consent-gated.
    expect(screen.getByText('Consent-gated')).toBeTruthy()
    expect(screen.getByText('Consent-gated').closest('span')?.textContent).toContain('2')

    // Location badges use the design palette (head=accent, body_close=info).
    // Note: "Body end" also matches the filter pill, so pick the badge chip.
    const headBadge = screen.getAllByText('Head').find((el) => el.className.includes('--acc-l'))
    expect(headBadge).toBeTruthy()
    const bodyEndBadge = screen
      .getAllByText('Body end')
      .find((el) => el.className.includes('--info-l'))
    expect(bodyEndBadge).toBeTruthy()

    // Consent badges are per-category colored chips.
    const analyticsBadge = screen
      .getAllByText('Analytics')
      .find((el) => el.className.includes('--acc-l'))
    expect(analyticsBadge).toBeTruthy()
    const functionalBadge = screen
      .getAllByText('Functional')
      .find((el) => el.className.includes('--suc-l'))
    expect(functionalBadge).toBeTruthy()
  })

  it('filters by location pill and shows the filtered count', async () => {
    render(<ScriptTags />)
    await screen.findByText('Google Analytics 4')

    expect(screen.getByText('3 scripts')).toBeTruthy()
    fireEvent.click(screen.getByRole('button', { name: 'Body end', pressed: false }))

    await waitFor(() => {
      expect(screen.getByText('Live Chat Widget')).toBeTruthy()
      expect(screen.queryByText('Google Analytics 4')).toBeNull()
      expect(screen.getByText('1 script')).toBeTruthy()
    })
  })

  it('filters by consent pill', async () => {
    render(<ScriptTags />)
    await screen.findByText('Google Analytics 4')

    fireEvent.click(screen.getByRole('button', { name: 'No consent', pressed: false }))

    await waitFor(() => {
      expect(screen.getByText('Org Schema')).toBeTruthy()
      expect(screen.queryByText('Google Analytics 4')).toBeNull()
    })
  })

  it('searches by name and offers clear-filters from the empty state', async () => {
    render(<ScriptTags />)
    await screen.findByText('Google Analytics 4')

    fireEvent.change(screen.getByPlaceholderText('Search scripts…'), {
      target: { value: 'zzz-no-match' },
    })

    expect(await screen.findByText('No scripts match your filters')).toBeTruthy()
    fireEvent.click(screen.getByRole('button', { name: 'Clear filters' }))
    expect(await screen.findByText('Google Analytics 4')).toBeTruthy()
  })

  it('select-all checks visible rows and shows selected count with clear', async () => {
    render(<ScriptTags />)
    await screen.findByText('Google Analytics 4')

    fireEvent.click(screen.getByLabelText('Select all scripts'))
    expect(await screen.findByText(/3 selected/)).toBeTruthy()

    fireEvent.click(screen.getByRole('button', { name: 'Clear' }))
    await waitFor(() => expect(screen.queryByText(/selected/)).toBeNull())
  })

  it('row duplicate posts a disabled copy and refetches', async () => {
    render(<ScriptTags />)
    await screen.findByText('Google Analytics 4')

    fireEvent.click(screen.getByRole('button', { name: 'Duplicate Google Analytics 4' }))

    await waitFor(() => {
      const post = cmsApi.mock.calls.find(
        (c) => (c[1] as RequestInit | undefined)?.method === 'POST',
      )
      expect(post).toBeTruthy()
      const body = JSON.parse(String((post![1] as RequestInit).body))
      expect(body.name).toBe('Google Analytics 4 (Copy)')
      expect(body.enabled).toBe(false)
    })
  })

  it('row delete asks for confirmation, then issues DELETE', async () => {
    render(<ScriptTags />)
    await screen.findByText('Org Schema')

    fireEvent.click(screen.getByRole('button', { name: 'Delete Org Schema' }))
    expect(await screen.findByText('Delete script?')).toBeTruthy()
    fireEvent.click(screen.getByRole('button', { name: 'Delete' }))

    await waitFor(() => {
      const del = cmsApi.mock.calls.find(
        (c) => (c[1] as RequestInit | undefined)?.method === 'DELETE',
      )
      expect(del).toBeTruthy()
      expect(del![0]).toBe('/script-tags/t3')
    })
  })
})
