/**
 * (infinite list branches, empty CTA, load-more, inline toast callbacks).
 */
import React from 'react'
import { fireEvent, render, screen } from '@testing-library/react'
import ListingView from '../index'
import { MOCK_APPLICATION_DETAIL } from '@/_tests_/mocks'

const mockPush = jest.fn()
const mockReplace = jest.fn()
const mockShowSuccess = jest.fn()
const mockShowError = jest.fn()
let mockSearchParams = new URLSearchParams()
jest.mock('@/i18n/navigation', () => ({
  useRouter: () => ({ push: mockPush, replace: mockReplace }),
  usePathname: () => '/en/api-products/api-1',
}))
jest.mock('next-intl', () => ({ useTranslations: () => (k: string) => k }))
jest.mock('next/navigation', () => ({
  useSearchParams: () => mockSearchParams,
}))
const mockUseAuthSession = jest.fn()
jest.mock('@/providers/auth-session-provider', () => ({
  useAuthSession: () => mockUseAuthSession(),
}))
const mockUseFilter = jest.fn()
jest.mock('@/providers/filter-provider', () => ({
  useFilter: () => mockUseFilter(),
}))
const mockFetchNextPage = jest.fn()
const mockUseGetAllApplicationsInfinite = jest.fn()
jest.mock('@/services/application/application.query-options', () => ({
  useGetAllApplicationsInfinite: () => mockUseGetAllApplicationsInfinite(),
}))
jest.mock('@/app/not-found', () => ({
  __esModule: true,
  default: () => <div data-testid="listing-404">404</div>,
}))
jest.mock('@/share/components/empty-state', () => ({
  __esModule: true,
  default: ({ buttonTitle, onClick }: { buttonTitle: string; onClick: () => void }) => (
    <button type="button" onClick={onClick}>{buttonTitle}</button>
  ),
}))
jest.mock('@/share/ui/button', () => ({
  Button: ({
    children,
    onClick,
    disabled,
  }: {
    children: React.ReactNode
    onClick?: () => void
    disabled?: boolean
  }) => (
    <button type="button" onClick={onClick} disabled={disabled}>{children}</button>
  ),
}))
jest.mock('@/share/ui/spinner', () => ({
  SpinnerCustom: () => <div data-testid="listing-spinner">Spinner</div>,
}))
jest.mock('lucide-react', () => ({
  ChevronDown: () => <span>ChevronDown</span>,
  OctagonXIcon: () => <span>OctagonXIcon</span>,
  PlusIcon: () => <span>PlusIcon</span>,
}))
jest.mock('@/share/icons', () => ({
  ChevronDown: () => <span>ChevronDown</span>,
  ChevronUp: () => <span>ChevronUp</span>,
}))
jest.mock('../../inline-toast', () => ({
  __esModule: true,
  default: () => <div data-testid="inline-toast-stub">InlineToast</div>,
  useApplicationInlineToast: () => ({
    inlineToast: null,
    isToastExiting: false,
    showSuccess: mockShowSuccess,
    showError: mockShowError,
  }),
}))
jest.mock('@/components/application/listing/card', () => ({
  __esModule: true,
  default: ({
    application,
    onSubscribeSuccess,
    onSubscribeError,
  }: {
    application: { name?: string }
    onSubscribeSuccess?: (m: string, o: { durationMs: number }) => void
    onSubscribeError?: (m: string, o: { durationMs: number }) => void
  }) => (
    <div data-testid="card-stub">
      <div>Card: {application?.name}</div>
      <button
        type="button"
        onClick={() => onSubscribeSuccess?.('subscribe success', { durationMs: 10 })}
      >
        trigger-success
      </button>
      <button
        type="button"
        onClick={() => onSubscribeError?.('subscribe error', { durationMs: 10 })}
      >
        trigger-error
      </button>
    </div>
  ),
}))

const defaultProps = {
  api_id: 'api-1',
  api_product_name: 'api-name',
  inlinetoasttype: '',
  inlinetoastmsgkey: '',
} as const

const successQuery = (list: typeof MOCK_APPLICATION_DETAIL[]) => ({
  data: { pages: [{ data: { list } }] },
  isFetching: false,
  isError: false,
  isSuccess: true,
  hasNextPage: false,
  isFetchingNextPage: false,
  fetchNextPage: mockFetchNextPage,
})

describe('[components/application/listing] ListingView', () => {
  beforeEach(() => {
    mockSearchParams = new URLSearchParams()
    mockPush.mockClear()
    mockReplace.mockClear()
    mockShowSuccess.mockClear()
    mockShowError.mockClear()
    mockFetchNextPage.mockClear()
    mockUseAuthSession.mockReturnValue({ authSession: {} })
    mockUseFilter.mockReturnValue({ filter: { limit: 6, offset: 0 } })
    mockUseGetAllApplicationsInfinite.mockReturnValue(
      successQuery([MOCK_APPLICATION_DETAIL])
    )
  })

  describe('happy path — data present', () => {
    it('renders card grid stub', () => {
      render(<ListingView {...defaultProps} />)
      expect(screen.getByTestId('card-stub')).toBeInTheDocument()
      expect(screen.getByText(/Card:/)).toBeInTheDocument()
    })

    it('renders inline toast host', () => {
      render(<ListingView {...defaultProps} />)
      expect(screen.getByTestId('inline-toast-stub')).toBeInTheDocument()
    })
  })

  describe('loading', () => {
    it('shows spinner when fetching first page with no cached data', () => {
      mockUseGetAllApplicationsInfinite.mockReturnValue({
        data: null,
        isFetching: true,
        isError: false,
        isSuccess: false,
        hasNextPage: false,
        isFetchingNextPage: false,
        fetchNextPage: mockFetchNextPage,
      })
      render(<ListingView {...defaultProps} />)
      expect(screen.getByTestId('listing-spinner')).toBeInTheDocument()
    })
  })

  describe('errors', () => {
    it('shows NotFound when infinite query errors', () => {
      mockUseGetAllApplicationsInfinite.mockReturnValue({
        data: null,
        isFetching: false,
        isError: true,
        isSuccess: false,
        hasNextPage: false,
        isFetchingNextPage: false,
        fetchNextPage: mockFetchNextPage,
      })
      render(<ListingView {...defaultProps} />)
      expect(screen.getByTestId('listing-404')).toBeInTheDocument()
    })
  })

  describe('empty list', () => {
    it('empty state CTA routes to application create with callback', () => {
      mockUseGetAllApplicationsInfinite.mockReturnValue(successQuery([]))

      render(<ListingView {...defaultProps} />)
      fireEvent.click(screen.getByText('listing.btn.create'))

      expect(mockPush).toHaveBeenCalledWith(
        '/api-products/api-1/application/create?callback=http://localhost/'
      )
    })
  })

  describe('pagination & auth shell', () => {
    it('create + load more when hasNextPage and not fetching next', () => {
      mockUseGetAllApplicationsInfinite.mockReturnValue({
        ...successQuery([MOCK_APPLICATION_DETAIL]),
        hasNextPage: true,
        isFetchingNextPage: false,
      })

      render(<ListingView {...defaultProps} />)
      fireEvent.click(screen.getByText('btn.create'))
      fireEvent.click(screen.getByText('btn.load_more'))

      expect(mockPush).toHaveBeenCalledWith(
        '/api-products/api-1/application/create?callback=http://localhost/'
      )
      expect(mockFetchNextPage).toHaveBeenCalledTimes(1)
    })

    it('hides create and shows spinner while fetching next page when guest shell rules apply', () => {
      mockUseAuthSession.mockReturnValue({ authSession: null })
      mockUseGetAllApplicationsInfinite.mockReturnValue({
        ...successQuery([MOCK_APPLICATION_DETAIL]),
        hasNextPage: true,
        isFetchingNextPage: true,
      })

      render(<ListingView {...defaultProps} />)

      expect(screen.queryByText('btn.create')).not.toBeInTheDocument()
      expect(screen.getByTestId('listing-spinner')).toBeInTheDocument()
    })
  })

  describe('subscribe callbacks → inline toast bridge', () => {
    beforeEach(() => {
      render(<ListingView {...defaultProps} />)
    })

    it('card success invokes showSuccess with subscription message key', () => {
      fireEvent.click(screen.getByText('trigger-success'))
      expect(mockShowSuccess).toHaveBeenCalledWith('mess.subscribe.success')
    })

    it('card error invokes showError with subscription error key', () => {
      fireEvent.click(screen.getByText('trigger-error'))
      expect(mockShowError).toHaveBeenCalledWith('mess.subscribe.error')
    })
  })
})
