import { describe, it, expect } from 'vitest';
import { screen, waitFor, within } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { http, HttpResponse } from 'msw';
import { renderRoute } from './render';
import { server } from './mocks/server';

// ---------------------------------------------------------------------------
// 1. Override tab renders in tab bar
// ---------------------------------------------------------------------------

describe('Override tab in Trading Engine', () => {
  it('renders Override tab in the tab bar', async () => {
    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      const tab = screen.getByRole('tab', { name: 'Override' });
      expect(tab).toBeInTheDocument();
      expect(tab).toHaveAttribute('aria-selected', 'true');
    });
  });

  // ---------------------------------------------------------------------------
  // 2. Override tab shows form and positions sections
  // ---------------------------------------------------------------------------

  it('shows order form and positions sections', async () => {
    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      expect(screen.getByText('Submit Override Order')).toBeInTheDocument();
    });
    expect(screen.getByText('Current Positions')).toBeInTheDocument();
  });

  // ---------------------------------------------------------------------------
  // 3. Override tab accessible via URL param ?tab=override
  // ---------------------------------------------------------------------------

  it('is accessible via URL param ?tab=override', async () => {
    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      expect(screen.getByText('Submit Override Order')).toBeInTheDocument();
    });
    // The Override tab should be selected
    const tab = screen.getByRole('tab', { name: 'Override' });
    expect(tab).toHaveAttribute('aria-selected', 'true');
  });

  // ---------------------------------------------------------------------------
  // 4. Order form fields are present
  // ---------------------------------------------------------------------------

  it('renders all order form fields (ticker, side, quantity, order type)', async () => {
    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      expect(screen.getByTestId('ticker-input')).toBeInTheDocument();
    });
    expect(screen.getByTestId('side-buy')).toBeInTheDocument();
    expect(screen.getByTestId('side-sell')).toBeInTheDocument();
    expect(screen.getByTestId('quantity-input')).toBeInTheDocument();
    expect(screen.getByTestId('order-type-select')).toBeInTheDocument();
    expect(screen.getByTestId('submit-order')).toBeInTheDocument();
  });

  // ---------------------------------------------------------------------------
  // 5. Conditional price fields show/hide based on order type
  // ---------------------------------------------------------------------------

  it('shows limit price field when order type is limit', async () => {
    const user = userEvent.setup();
    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      expect(screen.getByTestId('order-type-select')).toBeInTheDocument();
    });

    // Market order — no price fields
    expect(screen.queryByTestId('limit-price-input')).not.toBeInTheDocument();
    expect(screen.queryByTestId('stop-price-input')).not.toBeInTheDocument();

    // Switch to limit
    await user.selectOptions(screen.getByTestId('order-type-select'), 'limit');
    expect(screen.getByTestId('limit-price-input')).toBeInTheDocument();
    expect(screen.queryByTestId('stop-price-input')).not.toBeInTheDocument();

    // Switch to stop
    await user.selectOptions(screen.getByTestId('order-type-select'), 'stop');
    expect(screen.queryByTestId('limit-price-input')).not.toBeInTheDocument();
    expect(screen.getByTestId('stop-price-input')).toBeInTheDocument();

    // Switch to stop_limit
    await user.selectOptions(screen.getByTestId('order-type-select'), 'stop_limit');
    expect(screen.getByTestId('limit-price-input')).toBeInTheDocument();
    expect(screen.getByTestId('stop-price-input')).toBeInTheDocument();

    // Switch back to market
    await user.selectOptions(screen.getByTestId('order-type-select'), 'market');
    expect(screen.queryByTestId('limit-price-input')).not.toBeInTheDocument();
    expect(screen.queryByTestId('stop-price-input')).not.toBeInTheDocument();
  });

  // ---------------------------------------------------------------------------
  // 6. Form validation errors for invalid inputs
  // ---------------------------------------------------------------------------

  it('shows validation errors for invalid inputs', async () => {
    const user = userEvent.setup();
    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      expect(screen.getByTestId('submit-order')).toBeInTheDocument();
    });

    // Submit empty form
    await user.click(screen.getByTestId('submit-order'));

    await waitFor(() => {
      // Ticker and quantity should show errors
      const alerts = screen.getAllByRole('alert');
      expect(alerts.length).toBeGreaterThanOrEqual(2);
    });

    expect(screen.getByText(/Ticker must be 1–10 alphabetic characters/)).toBeInTheDocument();
    expect(screen.getByText(/Quantity must be a positive number/)).toBeInTheDocument();
  });

  it('shows validation error for missing limit price on limit order', async () => {
    const user = userEvent.setup();
    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      expect(screen.getByTestId('order-type-select')).toBeInTheDocument();
    });

    await user.type(screen.getByTestId('ticker-input'), 'AAPL');
    await user.type(screen.getByTestId('quantity-input'), '10');
    await user.selectOptions(screen.getByTestId('order-type-select'), 'limit');

    // Submit without limit price
    await user.click(screen.getByTestId('submit-order'));

    await waitFor(() => {
      expect(screen.getByText(/Limit price is required/)).toBeInTheDocument();
    });
  });

  // ---------------------------------------------------------------------------
  // 7. Successful order submission shows success message and resets form
  // ---------------------------------------------------------------------------

  it('shows success message and resets form on successful submission', async () => {
    const user = userEvent.setup();
    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      expect(screen.getByTestId('ticker-input')).toBeInTheDocument();
    });

    await user.type(screen.getByTestId('ticker-input'), 'AAPL');
    await user.type(screen.getByTestId('quantity-input'), '10');
    await user.click(screen.getByTestId('submit-order'));

    await waitFor(() => {
      expect(screen.getByTestId('success-banner')).toBeInTheDocument();
    });

    expect(screen.getByText(/Order queued — Job ID: job-test-123/)).toBeInTheDocument();
    expect(screen.getByText(/Status: queued/)).toBeInTheDocument();

    // Form should be reset
    expect(screen.getByTestId('ticker-input')).toHaveValue('');
    expect(screen.getByTestId('quantity-input')).toHaveValue(null);
  });

  // ---------------------------------------------------------------------------
  // 8. 422 error display
  // ---------------------------------------------------------------------------

  it('displays 422 validation errors from the server', async () => {
    // Override the handler to return 422
    server.use(
      http.post('/trading/api/trading/override/order', () => {
        return HttpResponse.json(
          { detail: [{ msg: 'Ticker not recognized by broker' }] },
          { status: 422 },
        );
      }),
    );

    const user = userEvent.setup();
    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      expect(screen.getByTestId('ticker-input')).toBeInTheDocument();
    });

    await user.type(screen.getByTestId('ticker-input'), 'AAPL');
    await user.type(screen.getByTestId('quantity-input'), '10');
    await user.click(screen.getByTestId('submit-order'));

    await waitFor(() => {
      expect(screen.getByTestId('validation-errors')).toBeInTheDocument();
    });

    expect(screen.getByText('Ticker not recognized by broker')).toBeInTheDocument();
  });

  // ---------------------------------------------------------------------------
  // 9. Submit button loading state during submission
  // ---------------------------------------------------------------------------

  it('disables submit button and shows loading state during submission', async () => {
    // Use a delayed handler to observe loading state
    server.use(
      http.post('/trading/api/trading/override/order', async ({ request }) => {
        await new Promise((resolve) => setTimeout(resolve, 200));
        const body = await request.json() as Record<string, unknown>;
        return HttpResponse.json(
          { job_id: 'job-test-123', status: 'queued', ticker: body.ticker, side: body.side, quantity: body.quantity, auto_registered: false },
          { status: 202 },
        );
      }),
    );

    const user = userEvent.setup();
    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      expect(screen.getByTestId('ticker-input')).toBeInTheDocument();
    });

    await user.type(screen.getByTestId('ticker-input'), 'AAPL');
    await user.type(screen.getByTestId('quantity-input'), '10');
    await user.click(screen.getByTestId('submit-order'));

    // Button should be disabled and show loading text
    await waitFor(() => {
      const btn = screen.getByTestId('submit-order');
      expect(btn).toBeDisabled();
      expect(btn).toHaveTextContent('Submitting…');
    });

    // Eventually resolves to success
    await waitFor(() => {
      expect(screen.getByTestId('success-banner')).toBeInTheDocument();
    });
  });

  // ---------------------------------------------------------------------------
  // 10. Positions table renders with mock data
  // ---------------------------------------------------------------------------

  it('renders positions table with mock data', async () => {
    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      expect(screen.getByTestId('positions-table')).toBeInTheDocument();
    });

    const table = screen.getByTestId('positions-table');
    expect(within(table).getByText('AAPL')).toBeInTheDocument();
    expect(within(table).getByText('10')).toBeInTheDocument();
  });

  // ---------------------------------------------------------------------------
  // 11. Positions loading state
  // ---------------------------------------------------------------------------

  it('shows loading state while positions are loading', async () => {
    // Delay the positions response to observe loading state
    server.use(
      http.get('/api/positions', async () => {
        await new Promise((resolve) => setTimeout(resolve, 500));
        return HttpResponse.json([]);
      }),
    );

    renderRoute('/trading/engine?tab=override');

    // The form should render while positions are loading
    await waitFor(() => {
      expect(screen.getByText('Submit Override Order')).toBeInTheDocument();
    });

    // The positions section should show a loading indicator (LoadingSpinner renders a role="status" element)
    expect(screen.getByText('Current Positions')).toBeInTheDocument();
  });

  // ---------------------------------------------------------------------------
  // 12. Positions empty state
  // ---------------------------------------------------------------------------

  it('shows empty state when no positions exist', async () => {
    server.use(
      http.get('/api/positions', () => HttpResponse.json([])),
    );

    renderRoute('/trading/engine?tab=override');
    await waitFor(() => {
      expect(screen.getByTestId('no-positions')).toBeInTheDocument();
    });

    expect(screen.getByText('No current positions')).toBeInTheDocument();
  });
});

// ---------------------------------------------------------------------------
// 13. "Override Trade" button on Trading page exists and links correctly
// ---------------------------------------------------------------------------

describe('Override Trade button on Trading page', () => {
  it('renders Override Trade button that links to override tab', async () => {
    renderRoute('/trading');
    await waitFor(() => {
      expect(screen.getByTestId('override-trade-button')).toBeInTheDocument();
    });

    const link = screen.getByTestId('override-trade-button');
    expect(link).toHaveTextContent('Override Trade');
    expect(link).toHaveAttribute('href', '/trading/engine?tab=override');
  });
});
