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

const cmsApi = vi.fn()
vi.mock('../../lib/api.js', () => ({ cmsApi: (...args: unknown[]) => cmsApi(...args) }))
vi.mock('sonner', () => ({ toast: { success: vi.fn(), error: vi.fn(), loading: vi.fn() } }))

const { PageTemplates } = await import('../../views/page-builder/PageTemplates.js')

function section(id: string) {
  return { id, type: 'section', settings: {}, children: [] }
}

const TEMPLATES = [
  {
    id: 'builtin-landing',
    name: 'Landing Page',
    description: 'Hero, features cards, CTA, testimonials, and final CTA',
    category: 'landing',
    builtIn: true,
    updatedAt: '2026-05-29T00:00:00.000Z',
    thumbnail: null,
    tree: { id: 'p1', type: 'page', children: [section('a'), section('b'), section('c')] },
  },
  {
    id: 'custom-1',
    name: 'Team Member',
    description: 'Avatar hero, full bio, and contact links',
    category: 'content',
    builtIn: false,
    updatedAt: '2026-06-10T00:00:00.000Z',
    thumbnail: null,
    tree: { id: 'p2', type: 'page', children: [section('a')] },
  },
]

beforeEach(() => {
  cmsApi.mockReset()
  cmsApi.mockImplementation(async (endpoint: string, options?: RequestInit) => {
    if (endpoint === '/page-templates' && !options?.method) {
      return { data: TEMPLATES, status: 200 }
    }
    return { data: {}, status: 200 }
  })
})

describe('PageTemplates card grid', () => {
  it('renders a card per template with name, category, and section-count meta', async () => {
    render(<PageTemplates onNavigate={() => {}} />)

    expect(await screen.findByText('Landing Page')).toBeTruthy()
    expect(screen.getByText('Team Member')).toBeTruthy()
    // Section counts derived from each template's tree.
    expect(screen.getByText(/3 sections/)).toBeTruthy()
    expect(screen.getByText(/1 section ·/)).toBeTruthy()
    // Category labels from the design's palette.
    expect(screen.getByText('Landing Page')).toBeTruthy()
    expect(screen.getByText('Built-in')).toBeTruthy()
  })

  it('hides edit/delete actions on built-in templates (server rejects them)', async () => {
    render(<PageTemplates onNavigate={() => {}} />)
    await screen.findByText('Landing Page')

    expect(screen.queryByRole('button', { name: 'Edit Landing Page' })).toBeNull()
    expect(screen.queryByRole('button', { name: 'Delete Landing Page' })).toBeNull()
    expect(screen.getByRole('button', { name: 'Edit Team Member' })).toBeTruthy()
    expect(screen.getByRole('button', { name: 'Delete Team Member' })).toBeTruthy()
  })

  it('deletes a custom template only after the confirm dialog is accepted', async () => {
    render(<PageTemplates onNavigate={() => {}} />)
    await screen.findByText('Team Member')

    fireEvent.click(screen.getByRole('button', { name: 'Delete Team Member' }))

    // Confirm dialog is up; nothing deleted yet.
    expect(await screen.findByText('Delete template?')).toBeTruthy()
    expect(cmsApi).not.toHaveBeenCalledWith(
      '/page-templates/custom-1',
      expect.objectContaining({ method: 'DELETE' }),
    )

    fireEvent.click(screen.getByRole('button', { name: 'Delete' }))

    await waitFor(() =>
      expect(cmsApi).toHaveBeenCalledWith(
        '/page-templates/custom-1',
        expect.objectContaining({ method: 'DELETE' }),
      ),
    )
  })

  it('opens the edit modal seeded with the template and saves via PUT', async () => {
    render(<PageTemplates onNavigate={() => {}} />)
    await screen.findByText('Team Member')

    fireEvent.click(screen.getByRole('button', { name: 'Edit Team Member' }))

    const nameInput = (await screen.findByPlaceholderText('Template name')) as HTMLInputElement
    expect(nameInput.value).toBe('Team Member')

    fireEvent.change(nameInput, { target: { value: 'Team Bio' } })
    fireEvent.click(screen.getByRole('button', { name: 'Save changes' }))

    await waitFor(() =>
      expect(cmsApi).toHaveBeenCalledWith(
        '/page-templates/custom-1',
        expect.objectContaining({ method: 'PUT' }),
      ),
    )
    const putCall = cmsApi.mock.calls.find(
      (c) => (c[1] as RequestInit | undefined)?.method === 'PUT',
    )!
    expect(JSON.parse((putCall[1] as RequestInit).body as string)).toMatchObject({
      name: 'Team Bio',
      category: 'content',
    })
  })

  it('routes "Use template" into the builder with the template id', async () => {
    const onNavigate = vi.fn()
    render(<PageTemplates onNavigate={onNavigate} />)
    await screen.findByText('Landing Page')

    fireEvent.click(screen.getByRole('button', { name: 'Create page from Landing Page' }))

    expect(onNavigate).toHaveBeenCalledWith('/page-builder/new?template=builtin-landing')
  })

  it('duplicates a template (built-ins included) via POST with a "(copy)" name', async () => {
    render(<PageTemplates onNavigate={() => {}} />)
    await screen.findByText('Landing Page')

    fireEvent.click(screen.getByRole('button', { name: 'Duplicate Landing Page' }))

    await waitFor(() =>
      expect(cmsApi).toHaveBeenCalledWith(
        '/page-templates',
        expect.objectContaining({ method: 'POST' }),
      ),
    )
    const postCall = cmsApi.mock.calls.find(
      (c) => (c[1] as RequestInit | undefined)?.method === 'POST',
    )!
    expect(JSON.parse((postCall[1] as RequestInit).body as string)).toMatchObject({
      name: 'Landing Page (copy)',
      category: 'landing',
      tree: TEMPLATES[0]!.tree,
    })
    // The grid refetches so the copy appears.
    await waitFor(() =>
      expect(
        cmsApi.mock.calls.filter(
          (c) => c[0] === '/page-templates' && !(c[1] as RequestInit | undefined)?.method,
        ).length,
      ).toBeGreaterThanOrEqual(2),
    )
  })

  it('filters cards by search text', async () => {
    render(<PageTemplates onNavigate={() => {}} />)
    await screen.findByText('Team Member')

    fireEvent.change(screen.getByPlaceholderText('Search templates…'), {
      target: { value: 'landing' },
    })

    await waitFor(() => {
      expect(screen.queryByText('Team Member')).toBeNull()
      expect(screen.getByText('Landing Page')).toBeTruthy()
    })
  })

  it('shows the empty state when no templates exist', async () => {
    cmsApi.mockResolvedValue({ data: [], status: 200 })
    render(<PageTemplates onNavigate={() => {}} />)

    expect(await screen.findByText('No page templates yet')).toBeTruthy()
  })
})
