import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach } from 'vitest';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { DeliverableStatus } from '../../../types/deliverable.types';
import { StatusTransitionModal } from '../StatusTransitionModal';

const mockShowToast = vi.fn();
vi.mock('../../ui/Toast', () => ({
  useToast: () => ({ showToast: mockShowToast }),
}));

const mockMutate = vi.fn();
const mockAllowedTransitions: { data: DeliverableStatus[]; isLoading: boolean } = {
  data: [DeliverableStatus.EMITIDA, DeliverableStatus.CANCELADA],
  isLoading: false,
};

vi.mock('../../../hooks/useDeliverables', () => ({
  useAllowedTransitions: () => mockAllowedTransitions,
  useChangeStatus: () => ({
    mutate: mockMutate,
    isPending: false,
  }),
}));

function renderModal(props: Partial<React.ComponentProps<typeof StatusTransitionModal>> = {}) {
  const queryClient = new QueryClient();
  return render(
    <QueryClientProvider client={queryClient}>
      <StatusTransitionModal
        isOpen={true}
        onClose={vi.fn()}
        serviceOrderId="os-id-1"
        currentStatus={DeliverableStatus.RASCUNHO}
        {...props}
      />
    </QueryClientProvider>,
  );
}

describe('StatusTransitionModal', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    mockAllowedTransitions.data = [DeliverableStatus.EMITIDA, DeliverableStatus.CANCELADA];
  });

  it('exibe apenas status permitidos', () => {
    renderModal();

    expect(screen.getByText('Emitida')).toBeInTheDocument();
    expect(screen.getByText('Cancelada')).toBeInTheDocument();
    expect(screen.queryByText('Aprovada')).not.toBeInTheDocument();
  });

  it('campo de observação aparece como obrigatório para CANCELADA', () => {
    renderModal();

    fireEvent.click(screen.getByText('Cancelada'));

    expect(screen.getByText('*')).toBeInTheDocument();
    expect(screen.getByText('Observação obrigatória para esta transição')).toBeInTheDocument();
  });

  it('campo de observação não é obrigatório para EMITIDA', () => {
    renderModal();

    fireEvent.click(screen.getByText('Emitida'));

    expect(
      screen.queryByText('Observação obrigatória para esta transição'),
    ).not.toBeInTheDocument();
  });

  it('botão confirmar desabilitado sem seleção de status', () => {
    renderModal();

    const confirmButton = screen.getByText('Confirmar');
    expect(confirmButton).toBeDisabled();
  });

  it('botão confirmar desabilitado quando observação obrigatória está vazia', () => {
    renderModal();

    fireEvent.click(screen.getByText('Cancelada'));

    const confirmButton = screen.getByText('Confirmar');
    expect(confirmButton).toBeDisabled();
  });

  it('botão confirmar habilitado quando observação obrigatória está preenchida', () => {
    renderModal();

    fireEvent.click(screen.getByText('Cancelada'));
    fireEvent.change(screen.getByPlaceholderText('Informe o motivo da mudança de status...'), {
      target: { value: 'Motivo do cancelamento' },
    });

    const confirmButton = screen.getByText('Confirmar');
    expect(confirmButton).not.toBeDisabled();
  });

  it('chama mutate ao confirmar com dados corretos', async () => {
    const onClose = vi.fn();
    renderModal({ onClose });

    fireEvent.click(screen.getByText('Emitida'));
    fireEvent.click(screen.getByText('Confirmar'));

    expect(mockMutate).toHaveBeenCalledWith(
      {
        id: 'os-id-1',
        data: {
          status: DeliverableStatus.EMITIDA,
          observation: undefined,
        },
      },
      expect.objectContaining({
        onSuccess: expect.any(Function),
        onError: expect.any(Function),
      }),
    );
  });

  it('não renderiza quando isOpen é false', () => {
    renderModal({ isOpen: false });

    expect(screen.queryByText('Alterar Status')).not.toBeInTheDocument();
  });

  it('exibe aviso de sprint quando OS está em RASCUNHO sem sprint vinculada', () => {
    renderModal({ sprintLinked: false });

    expect(
      screen.getByText('Para emitir este entregável, vincule-o a uma sprint na tela de edição.'),
    ).toBeInTheDocument();
  });

  it('não exibe aviso de sprint quando OS está em RASCUNHO com sprint vinculada', () => {
    renderModal({ sprintLinked: true });

    expect(
      screen.queryByText('Para emitir este entregável, vincule-o a uma sprint na tela de edição.'),
    ).not.toBeInTheDocument();
  });

  it('não exibe aviso de sprint para status diferente de RASCUNHO', () => {
    mockAllowedTransitions.data = [DeliverableStatus.EM_EXECUCAO, DeliverableStatus.CANCELADA];
    renderModal({ currentStatus: DeliverableStatus.EMITIDA, sprintLinked: false });

    expect(
      screen.queryByText('Para emitir este entregável, vincule-o a uma sprint na tela de edição.'),
    ).not.toBeInTheDocument();
  });
});
