// @vitest-environment jsdom
import { describe, expect, it, vi, beforeEach, afterEach } from 'vitest';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import userEvent from '@testing-library/user-event';
import { CodeStrategyEditor } from './CodeStrategyEditor';

const { createTradeProfileMock } = vi.hoisted(() => ({
  createTradeProfileMock: vi.fn(),
}));

vi.mock('@monaco-editor/react', () => ({
  loader: { config: vi.fn() },
  default: ({ value, onChange }: any) => (
    <textarea
      aria-label="strategy code"
      value={value}
      onChange={(event) => onChange(event.currentTarget.value)}
    />
  ),
}));

vi.mock('monaco-editor', () => ({
  editor: {},
}));

vi.mock('monaco-editor/esm/vs/editor/editor.worker?worker', () => ({
  default: class MockEditorWorker {},
}));

vi.mock('monaco-editor/esm/vs/language/json/json.worker?worker', () => ({
  default: class MockJsonWorker {},
}));

vi.mock('monaco-editor/esm/vs/language/css/css.worker?worker', () => ({
  default: class MockCssWorker {},
}));

vi.mock('monaco-editor/esm/vs/language/html/html.worker?worker', () => ({
  default: class MockHtmlWorker {},
}));

vi.mock('monaco-editor/esm/vs/language/typescript/ts.worker?worker', () => ({
  default: class MockTsWorker {},
}));

vi.mock('../../lib/profileApi', () => ({
  createTradeProfile: (...args: any[]) => createTradeProfileMock(...args),
}));

vi.mock('../../lib/authSession', () => ({
  getPlatformAccessToken: vi.fn(async () => 'test-token'),
}));

vi.mock('../../lib/runtime', () => ({
  tradingRuntime: { tradingApiUrl: 'https://trading.test' },
}));

describe('CodeStrategyEditor save behavior', () => {
  let setItemSpy: ReturnType<typeof vi.spyOn>;

  beforeEach(() => {
    createTradeProfileMock.mockReset();
    createTradeProfileMock.mockResolvedValue({ id: 'profile-code-1' });
    setItemSpy = vi.spyOn(Storage.prototype, 'setItem');
  });

  afterEach(() => {
    vi.useRealTimers();
    vi.unstubAllGlobals();
    setItemSpy.mockRestore();
  });

  it('persists code strategies through createTradeProfile instead of localStorage', async () => {
    const user = userEvent.setup();
    render(<CodeStrategyEditor symbol="AAPL" allocatedCapital={2500} riskPerTradePercent={2} />);

    fireEvent.change(await screen.findByLabelText('strategy code'), {
      target: { value: 'function strategy() { return { signal: "HOLD" }; }' },
    });
    await user.click(screen.getByRole('button', { name: /^save$/i }));

    await waitFor(() => expect(createTradeProfileMock).toHaveBeenCalledTimes(1));
    expect(createTradeProfileMock).toHaveBeenCalledWith({
      name: 'Code Strategy (AAPL)',
      symbols: 'AAPL',
      allocated_capital: 2500,
      risk_per_trade_percent: 2,
      is_active: false,
      strategy_config: {
        type: 'code',
        version: 1,
        language: 'javascript',
        code: 'function strategy() { return { signal: "HOLD" }; }',
      },
    });
    expect(setItemSpy).not.toHaveBeenCalled();
    expect(await screen.findByRole('button', { name: /saved!/i })).toBeInTheDocument();
  });

  it('surfaces save failures from the profile API', async () => {
    const user = userEvent.setup();
    createTradeProfileMock.mockRejectedValueOnce(new Error('Profile save failed'));

    render(<CodeStrategyEditor symbol="MSFT" />);
    await user.click(screen.getByRole('button', { name: /^save$/i }));

    expect(await screen.findByText('Profile save failed')).toBeInTheDocument();
  });

  it('clears the saved status timeout when unmounted', async () => {
    const clearTimeoutSpy = vi.spyOn(globalThis, 'clearTimeout');
    const user = userEvent.setup();

    const { unmount } = render(<CodeStrategyEditor symbol="NVDA" />);
    await user.click(screen.getByRole('button', { name: /^save$/i }));

    await waitFor(() => expect(screen.getByRole('button', { name: /saved!/i })).toBeInTheDocument());
    unmount();

    expect(clearTimeoutSpy).toHaveBeenCalled();
    clearTimeoutSpy.mockRestore();
  });

  it('saves with Cmd/Ctrl-S while focused in the editor', async () => {
    render(<CodeStrategyEditor symbol="TSLA" />);

    fireEvent.keyDown(await screen.findByLabelText('strategy code'), { key: 's', metaKey: true });

    await waitFor(() => expect(createTradeProfileMock).toHaveBeenCalledTimes(1));
  });

  it('runs a backtest with Cmd/Ctrl-Enter while focused in the editor', async () => {
    const fetchMock = vi.fn().mockResolvedValue({
      ok: true,
      json: async () => ({ results: { trades: 0 } }),
    });
    vi.stubGlobal('fetch', fetchMock);

    render(<CodeStrategyEditor symbol="TSLA" />);
    fireEvent.keyDown(await screen.findByLabelText('strategy code'), { key: 'Enter', metaKey: true });

    await waitFor(() => expect(fetchMock).toHaveBeenCalledTimes(1));
    expect(String(fetchMock.mock.calls[0][0])).toBe('https://trading.test/api/backtest/run');
  });
});
