import { useAuthStore } from '@/stores/auth';
import type { AuthUser } from '@alqove/types';
import { render, screen, waitFor, fireEvent } from '@testing-library/react';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
import { SellerReturnsPanel } from '../seller-returns-panel';
import type { OrderReturn } from '@alqove/api-client';

const getMock = vi.fn();
const approveMock = vi.fn();
const rejectMock = vi.fn();
const markReceivedMock = vi.fn();
const retryLabelMock = vi.fn();
const sellerCloseWithoutRefundMock = vi.fn();
vi.mock('@/lib/api', () => ({
  api: {
    returns: { forStore: (storeId: string) => {
      expect(storeId).toBe('store-1');
      return {
      get: (...a: unknown[]) => getMock(...a),
      approve: (...a: unknown[]) => approveMock(...a),
      reject: (...a: unknown[]) => rejectMock(...a),
      markReceived: (...a: unknown[]) => markReceivedMock(...a),
      retryLabel: (...a: unknown[]) => retryLabelMock(...a),
      sellerCloseWithoutRefund: (...a: unknown[]) =>
        sellerCloseWithoutRefundMock(...a),
      };
    } },
  },
}));

function wrap(node: React.ReactNode) {
  const qc = new QueryClient({
    defaultOptions: { queries: { retry: false }, mutations: { retry: false } },
  });
  return <QueryClientProvider client={qc}>{node}</QueryClientProvider>;
}

const baseReturn: OrderReturn = {
  id: 'r1',
  order_id: 'o1',
  initiated_by: 'buyer',
  state: 'requested',
  reason: 'damaged',
  reason_text: null,
  return_shipping_payer: 'seller',
  restocking_fee_cents: 0,
  refund_amount_cents: null,
  refund_original_shipping: false,
  refund_amount_override_cents: null,
  stripe_refund_id: null,
  tracking_number: null,
  carrier: null,
  tracker_id: null,
  tracking_url: null,
  shipping_label_url: null,
  easypost_label_error: null,
  items: [],
  approved_at: null,
  rejected_at: null,
  label_issued_at: null,
  in_transit_at: null,
  received_at: null,
  refunded_at: null,
  closed_at: null,
  cancelled_at: null,
  escalation: null,
  created_at: '2026-05-07T10:00:00Z',
  updated_at: '2026-05-07T10:00:00Z',
};

describe('SellerReturnsPanel', () => {
  beforeEach(() => {
    getMock.mockReset();
    approveMock.mockReset();
    rejectMock.mockReset();
    markReceivedMock.mockReset();
    retryLabelMock.mockReset();
    sellerCloseWithoutRefundMock.mockReset();
  });

  it('renders nothing when openReturnId is null', () => {
    const { container } = render(wrap(<SellerReturnsPanel openReturnId={null} />));
    expect(container.firstChild).toBeNull();
  });

  it('renders timeline + action bar when state=requested', async () => {
    getMock.mockResolvedValueOnce({ data: baseReturn });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    expect(await screen.findByRole('button', { name: /Approve/i })).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /Decline/i })).toBeInTheDocument();
    expect(screen.getByText('Awaiting seller')).toBeInTheDocument();
  });

  it('approve flow calls mutation with restocking_fee=0 by default', async () => {
    getMock.mockResolvedValueOnce({ data: baseReturn });
    approveMock.mockResolvedValueOnce({
      data: { ...baseReturn, state: 'closed' },
    });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    fireEvent.click(await screen.findByRole('button', { name: /Approve/i }));
    fireEvent.click(screen.getByRole('button', { name: /Confirm approve/i }));
    await waitFor(() => expect(approveMock).toHaveBeenCalledWith('r1', 0));
  });

  it('reject flow requires ≥10 chars of reason_text', async () => {
    getMock.mockResolvedValueOnce({ data: baseReturn });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    fireEvent.click(await screen.findByRole('button', { name: /Decline/i }));
    fireEvent.change(screen.getByRole('textbox'), { target: { value: 'short' } });
    fireEvent.click(screen.getByRole('button', { name: /Confirm decline/i }));
    await waitFor(() =>
      expect(screen.getByText(/at least 10 characters/i)).toBeInTheDocument(),
    );
    expect(rejectMock).not.toHaveBeenCalled();
  });

  it('hides restocking fee input when reason is seller-fault', async () => {
    getMock.mockResolvedValueOnce({ data: { ...baseReturn, reason: 'damaged' } });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    fireEvent.click(await screen.findByRole('button', { name: /Approve/i }));
    expect(screen.queryByTestId('approve-restocking-fee')).toBeNull();
  });

  it('shows restocking fee input when reason is buyer-side', async () => {
    getMock.mockResolvedValueOnce({ data: { ...baseReturn, reason: 'doesnt_fit' } });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    fireEvent.click(await screen.findByRole('button', { name: /Approve/i }));
    expect(screen.getByTestId('approve-restocking-fee')).toBeInTheDocument();
  });

  it('shows error banner + Retry label button in approved state with easypost_label_error', async () => {
    getMock.mockResolvedValueOnce({
      data: {
        ...baseReturn,
        state: 'approved',
        approved_at: '2026-05-07T10:00:00Z',
        easypost_label_error: 'EasyPost: rate not available',
      },
    });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    expect(await screen.findByText(/rate not available/i)).toBeInTheDocument();
    expect(screen.getByRole('button', { name: /Retry label/i })).toBeInTheDocument();
  });

  it('Retry label button calls retryLabel mutation with the return id', async () => {
    getMock.mockResolvedValueOnce({
      data: {
        ...baseReturn,
        state: 'approved',
        approved_at: '2026-05-07T10:00:00Z',
        easypost_label_error: 'EasyPost: rate not available',
      },
    });
    retryLabelMock.mockResolvedValueOnce({
      data: {
        ...baseReturn,
        state: 'awaiting_shipment',
        order_id: 'o1',
        id: 'r1',
      },
    });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    fireEvent.click(await screen.findByRole('button', { name: /Retry label/i }));
    await waitFor(() => expect(retryLabelMock).toHaveBeenCalledWith('r1'));
  });

  it('shows Mark received button in awaiting_shipment', async () => {
    getMock.mockResolvedValueOnce({
      data: {
        ...baseReturn,
        state: 'awaiting_shipment',
        approved_at: '2026-05-07T10:00:00Z',
        label_issued_at: '2026-05-07T10:30:00Z',
        shipping_label_url: 'https://easypost-files.s3.amazonaws.com/label.pdf',
      },
    });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    expect(
      await screen.findByRole('button', { name: /Mark received/i }),
    ).toBeInTheDocument();
  });

  it('Mark received button calls markReceived mutation with the return id', async () => {
    getMock.mockResolvedValueOnce({
      data: {
        ...baseReturn,
        state: 'awaiting_shipment',
        approved_at: '2026-05-07T10:00:00Z',
        label_issued_at: '2026-05-07T10:30:00Z',
      },
    });
    markReceivedMock.mockResolvedValueOnce({
      data: { ...baseReturn, state: 'closed', order_id: 'o1', id: 'r1' },
    });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    fireEvent.click(await screen.findByRole('button', { name: /Mark received/i }));
    await waitFor(() => expect(markReceivedMock).toHaveBeenCalledWith('r1'));
  });

  it('renders read-only when state is received (no action buttons)', async () => {
    getMock.mockResolvedValueOnce({
      data: {
        ...baseReturn,
        state: 'received',
        approved_at: '2026-05-07T10:00:00Z',
        received_at: '2026-05-07T13:00:00Z',
      },
    });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    await screen.findByTestId('return-state-badge');
    expect(screen.queryByRole('button', { name: /Mark received/i })).toBeNull();
    expect(screen.queryByRole('button', { name: /Retry label/i })).toBeNull();
    expect(screen.queryByRole('button', { name: /Approve/i })).toBeNull();
  });

  it('renders read-only when state is refunded', async () => {
    getMock.mockResolvedValueOnce({
      data: {
        ...baseReturn,
        state: 'refunded',
        approved_at: '2026-05-07T10:00:00Z',
        refund_amount_cents: 5000,
        stripe_refund_id: 're_abcdefgh12345678',
      },
    });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    await screen.findByText('Refunded');
    expect(screen.queryByRole('button', { name: /Mark received/i })).toBeNull();
    expect(screen.getByText(/\$50.00/)).toBeInTheDocument();
  });

  it('shows Close-without-refund button in awaiting_shipment / in_transit', async () => {
    getMock.mockResolvedValueOnce({
      data: {
        ...baseReturn,
        state: 'awaiting_shipment',
        approved_at: '2026-05-07T10:00:00Z',
        label_issued_at: '2026-05-07T10:30:00Z',
      },
    });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    expect(
      await screen.findByRole('button', { name: /Close without refund/i }),
    ).toBeInTheDocument();
  });

  it('hides Close-without-refund button in approved / received / refunded / closed / cancelled', async () => {
    for (const state of ['approved', 'received', 'refunded', 'closed', 'cancelled'] as const) {
      getMock.mockResolvedValueOnce({
        data: {
          ...baseReturn,
          state,
          approved_at: '2026-05-07T10:00:00Z',
          easypost_label_error: state === 'approved' ? 'temporary error' : null,
        },
      });
      const { unmount } = render(wrap(<SellerReturnsPanel openReturnId="r1" />));
      await screen.findByTestId('return-state-badge');
      expect(
        screen.queryByRole('button', { name: /Close without refund/i }),
      ).toBeNull();
      unmount();
    }
  });

  it('Close-without-refund submits with returnId + reason', async () => {
    getMock.mockResolvedValueOnce({
      data: {
        ...baseReturn,
        state: 'in_transit',
        approved_at: '2026-05-07T10:00:00Z',
        label_issued_at: '2026-05-07T10:30:00Z',
        in_transit_at: '2026-05-07T11:00:00Z',
      },
    });
    sellerCloseWithoutRefundMock.mockResolvedValueOnce({
      data: { ...baseReturn, state: 'closed', order_id: 'o1', id: 'r1' },
    });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    fireEvent.click(
      await screen.findByRole('button', { name: /Close without refund/i }),
    );
    fireEvent.change(screen.getByRole('textbox'), {
      target: { value: 'Item never returned after 10 days.' },
    });
    fireEvent.click(screen.getByRole('button', { name: /Confirm close/i }));
    await waitFor(() =>
      expect(sellerCloseWithoutRefundMock).toHaveBeenCalledWith(
        'r1',
        'Item never returned after 10 days.',
      ),
    );
  });

  it('renders read-only when state is closed', async () => {
    getMock.mockResolvedValueOnce({
      data: {
        ...baseReturn,
        state: 'closed',
        approved_at: '2026-05-07T10:00:00Z',
        closed_at: '2026-05-07T14:00:00Z',
      },
    });
    render(wrap(<SellerReturnsPanel openReturnId="r1" />));
    await screen.findByTestId('return-state-badge');
    expect(screen.queryByRole('button', { name: /Mark received/i })).toBeNull();
    expect(screen.queryByRole('button', { name: /Approve/i })).toBeNull();
  });
});

beforeEach(() => {
  useAuthStore.setState({ user: { id: 'seller', store_id: 'legacy' } as AuthUser, token: 'token', isLoading: false,
    selectedStoreId: 'store-1', memberships: [{ id: 'm', store_id: 'store-1', role: 'owner', capabilities: ['store.admin'], is_exempt: false }] });
});
