import { QueryClient, QueryClientProvider } from '@tanstack/react-query'
import { render, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { useUnit } from 'effector-react'
import { MemoryRouter } from 'react-router-dom'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { setAdminOrderStatus } from '@/entities/order/api/admin-order-api'
import type { AdminOrderDetailResponse } from '@/entities/order/api/admin-order-api'
import { ORDER_STATUS_MAP } from '@/shared/lib/order-status-data'
import { OrderDetailContent } from '../OrderDetailContent'

vi.mock('@/entities/order/api/admin-order-api', () => ({
  setAdminOrderStatus: vi.fn(),
  postAdminOrderMessage: vi.fn(),
}))

vi.mock('effector-react', () => ({
  useUnit: vi.fn(),
}))

vi.mock('@/shared/ui/RichTextMessageEditor.lazy', () => ({
  RichTextMessageEditor: ({
    value,
    onChange,
  }: {
    value: string
    onChange: (next: string) => void
    placeholder?: string
    disabled?: boolean
  }) => <textarea aria-label="Ответ админа" value={value} onChange={(e) => onChange(e.target.value)} />,
}))

const setAdminOrderStatusMock = vi.mocked(setAdminOrderStatus)
const useUnitMock = vi.mocked(useUnit)

function createDetail(overrides?: Partial<AdminOrderDetailResponse['item']>): AdminOrderDetailResponse['item'] {
  return {
    id: 'order-12345678',
    status: 'PENDING_PAYMENT',
    deliveryType: 'delivery',
    deliveryCarrier: null,
    paymentMethod: 'online',
    itemsSubtotalCents: 3000,
    deliveryFeeCents: 300,
    deliveryFeeLocked: true,
    totalCents: 3300,
    currency: 'RUB',
    addressSnapshotJson: null,
    comment: null,
    createdAt: '2026-05-28T10:00:00.000Z',
    updatedAt: '2026-05-28T10:00:00.000Z',
    user: {
      id: 'user-1',
      email: 'buyer@example.com',
      displayName: 'Покупатель',
      avatar: null,
      avatarStyle: null,
    },
    items: [
      {
        id: 'item-1',
        productId: 'product-1',
        qty: 1,
        titleSnapshot: 'Тестовый товар',
        priceCentsSnapshot: 3000,
      },
    ],
    messages: [],
    ...overrides,
  }
}

function createDeferred<T>() {
  let resolve!: (value: T | PromiseLike<T>) => void
  let reject!: (reason?: unknown) => void
  const promise = new Promise<T>((res, rej) => {
    resolve = res
    reject = rej
  })
  return { promise, resolve, reject }
}

function renderComponent(detail: AdminOrderDetailResponse['item'], orderId = 'order-12345678') {
  const queryClient = new QueryClient({
    defaultOptions: {
      queries: { retry: false },
      mutations: { retry: false },
    },
  })

  return render(
    <MemoryRouter>
      <QueryClientProvider client={queryClient}>
        <OrderDetailContent detail={detail} orderId={orderId} />
      </QueryClientProvider>
    </MemoryRouter>,
  )
}

describe('OrderDetailContent quick status transitions', () => {
  beforeEach(() => {
    vi.clearAllMocks()
    useUnitMock.mockReturnValue(null)
    setAdminOrderStatusMock.mockResolvedValue(undefined)
  })

  it('рендерит кнопки доступных переходов статуса', async () => {
    renderComponent(createDetail({ status: 'PENDING_PAYMENT', deliveryType: 'delivery' }))

    expect(screen.getByText('Быстрый переход статуса')).toBeInTheDocument()
    expect(await screen.findByRole('button', { name: ORDER_STATUS_MAP.PAID })).toBeInTheDocument()
    const cancelledButton = screen.getByRole('button', { name: ORDER_STATUS_MAP.CANCELLED })
    expect(cancelledButton).toBeInTheDocument()
    expect(cancelledButton).toHaveClass('MuiButton-outlined')
    expect(cancelledButton).toHaveClass('MuiButton-colorError')
  })

  it('по клику вызывает setAdminOrderStatus(orderId, статус)', async () => {
    const user = userEvent.setup()
    renderComponent(createDetail({ status: 'PENDING_PAYMENT', deliveryType: 'delivery' }), 'order-click-test')

    await user.click(await screen.findByRole('button', { name: ORDER_STATUS_MAP.PAID }))

    expect(setAdminOrderStatusMock).toHaveBeenCalledWith('order-click-test', 'PAID')
  })

  it('в pending состоянии дизейблит кнопки перехода', async () => {
    const user = userEvent.setup()
    const deferred = createDeferred<void>()
    setAdminOrderStatusMock.mockImplementationOnce(() => deferred.promise)

    renderComponent(createDetail({ status: 'PENDING_PAYMENT', deliveryType: 'delivery' }))

    await user.click(await screen.findByRole('button', { name: ORDER_STATUS_MAP.PAID }))

    await waitFor(() => {
      expect(screen.getByRole('button', { name: ORDER_STATUS_MAP.PAID })).toBeDisabled()
      expect(screen.getByRole('button', { name: ORDER_STATUS_MAP.CANCELLED })).toBeDisabled()
    })

    deferred.resolve(undefined)
    await waitFor(() => {
      expect(screen.getByRole('button', { name: ORDER_STATUS_MAP.PAID })).not.toBeDisabled()
    })
  })

  it('для финального статуса показывает сообщение без кнопок перехода', () => {
    renderComponent(createDetail({ status: 'CANCELLED' }))

    expect(screen.getByText('Статус финальный, смена недоступна')).toBeInTheDocument()
    expect(screen.queryByRole('button', { name: ORDER_STATUS_MAP.PAID })).not.toBeInTheDocument()
    expect(screen.queryByRole('button', { name: ORDER_STATUS_MAP.CANCELLED })).not.toBeInTheDocument()
  })

  it('показывает ошибку мутации и после завершения запроса снова даёт кликнуть', async () => {
    const user = userEvent.setup()
    const deferred = createDeferred<void>()
    setAdminOrderStatusMock.mockImplementationOnce(() => deferred.promise).mockResolvedValueOnce(undefined)

    renderComponent(createDetail({ status: 'PENDING_PAYMENT', deliveryType: 'delivery' }))

    const paidButton = await screen.findByRole('button', { name: ORDER_STATUS_MAP.PAID })
    await user.click(paidButton)

    await waitFor(() => {
      expect(paidButton).toBeDisabled()
    })

    deferred.reject(new Error('request failed'))

    const errorAlert = await screen.findByText('Не удалось сменить статус')
    expect(errorAlert).toBeInTheDocument()

    await waitFor(() => {
      expect(paidButton).not.toBeDisabled()
    })

    await user.click(paidButton)
    expect(setAdminOrderStatusMock).toHaveBeenCalledTimes(2)
  })
})
