import { fireEvent, render, screen, waitFor, within } from '@testing-library/react';
import { MemoryRouter, Route, Routes } from 'react-router-dom';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import Settings from '@/pages/Settings';
import { createProjectConfig } from '../../shared/project-config';
import { useOpencodeStore } from '@/stores/opencode';
import { useSettingsStore } from '@/stores/settings';

const hostApiFetchMock = vi.fn();
const invokeIpcMock = vi.fn();

vi.mock('@/lib/host-api', () => ({
  hostApiFetch: (...args: unknown[]) => hostApiFetchMock(...args),
}));

vi.mock('@/lib/api-client', () => ({
  invokeIpc: (...args: unknown[]) => invokeIpcMock(...args),
  toUserMessage: (error: unknown) => (error instanceof Error ? error.message : String(error)),
}));

vi.mock('@/components/settings/UpdateSettings', () => ({
  UpdateSettings: () => null,
}));

vi.mock('react-i18next', async (importOriginal) => {
  const actual = await importOriginal<typeof import('react-i18next')>();
  return {
    ...actual,
    useTranslation: () => ({
      t: (key: string) => key,
    }),
  };
});

vi.mock('sonner', () => ({
  toast: {
    success: vi.fn(),
    error: vi.fn(),
  },
}));

function renderSettings() {
  return render(
    <MemoryRouter initialEntries={['/settings']}>
      <Routes>
        <Route path="/settings" element={<Settings />} />
        <Route path="/models" element={<div data-testid="models-config-page" />} />
      </Routes>
    </MemoryRouter>
  );
}

function mockValidatedWorkspaceHostApi(activeProject: {
  id: string;
  path: string;
  name: string;
  createdAt: string;
  updatedAt: string;
  lastOpenedAt: string;
}) {
  hostApiFetchMock.mockImplementation(async (path: string, init?: RequestInit) => {
    if (path === '/api/opencode/status') {
      return { state: 'stopped', port: 4096 };
    }
    if (path === '/api/opencode/config-summary') {
      return {
        model: 'openai/gpt-5.4',
        smallModel: 'openai/gpt-5.4-mini',
        providerIds: ['openai', 'openrouter'],
        providerCount: 2,
      };
    }
    if (path === '/api/opencode/projects') {
      return { projects: [activeProject], activeProject };
    }
    if (path === '/api/opencode/start' && init?.method === 'POST') {
      return {
        success: true,
        status: { state: 'running', port: 4097, url: 'http://127.0.0.1:4097' },
      };
    }
    if (path === '/api/opencode/sessions') {
      return { sessions: [] };
    }
    if (path === '/api/opencode/sessions/status') {
      return { statuses: {} };
    }
    if (path === '/api/opencode/health') {
      return {
        ok: true,
        status: { state: 'running', port: 4097, url: 'http://127.0.0.1:4097' },
      };
    }
    throw new Error(`Unexpected path ${path}`);
  });
}

describe('Settings runtime workspace', () => {
  beforeEach(() => {
    vi.clearAllMocks();
    invokeIpcMock.mockResolvedValue(undefined);
    useSettingsStore.setState({
      theme: 'system',
      language: 'en',
      launchAtStartup: false,
      telemetryEnabled: false,
      proxyEnabled: false,
      proxyServer: '',
      proxyHttpServer: '',
      proxyHttpsServer: '',
      proxyAllServer: '',
      proxyBypassRules: '',
      sidebarCollapsed: false,
      devModeUnlocked: false,
    });
    useOpencodeStore.setState({
      status: { state: 'stopped', port: 4096 },
      health: null,
      healthCheckedAt: null,
      runtimeConfigSummary: null,
      projects: [],
      activeProject: null,
      sessions: [],
      sessionsByProjectId: {},
      selectedSessionId: null,
      sessionStatuses: {},
      sessionMessages: [],
      sessionMessagesBySessionId: {},
      streamingMessage: null,
      streamingMessagesBySessionId: {},
      streamingTools: [],
      streamingToolsBySessionId: {},
      sendingSessionId: null,
      sendingSessionIds: {},
      queuedSessionPrompts: {},
      sessionDiffs: [],
      sessionDiffLoading: false,
      sessionDiffError: null,
      fileStatuses: [],
      fileSearchResults: [],
      contentSearchResults: [],
      selectedContextFile: null,
      fileContextLoading: false,
      fileContextError: null,
      runtimeAutoStartAttempted: false,
      loading: false,
      error: null,
    });
  });

  it('hides non-general settings until the general header reveal button is clicked', async () => {
    const activeProject = {
      id: 'prj_1',
      path: 'D:/repo/packages/ui',
      name: 'ui',
      createdAt: '2026-05-12T00:00:00.000Z',
      updatedAt: '2026-05-12T00:00:00.000Z',
      lastOpenedAt: '2026-05-12T00:00:00.000Z',
    };
    mockValidatedWorkspaceHostApi(activeProject);

    renderSettings();

    expect(await screen.findByRole('heading', { name: 'appearance.title' })).toBeInTheDocument();
    expect(screen.getByTestId('settings-opencode-workspace').closest('[data-state]')).toHaveAttribute('data-state', 'closed');
    expect(screen.getByText('Network').closest('[data-state]')).toHaveAttribute('data-state', 'closed');
    expect(screen.getByText('updates.title').closest('[data-state]')).toHaveAttribute('data-state', 'closed');
    expect(screen.getByText('advanced.title').closest('[data-state]')).toHaveAttribute('data-state', 'closed');

    fireEvent.click(screen.getByRole('button', { name: 'appearance.showMoreSettings' }));

    expect(await screen.findByTestId('settings-opencode-workspace')).toBeInTheDocument();
    expect(screen.getByText('Network')).toBeInTheDocument();
    expect(screen.getByText('updates.title')).toBeInTheDocument();
    expect(screen.getByText('advanced.title')).toBeInTheDocument();
  });

  it('renders runtime, model, and workspace controls in settings without exposing the runtime brand', async () => {
    const activeProject = {
      id: 'prj_1',
      path: 'D:/repo/packages/ui',
      name: 'ui',
      createdAt: '2026-05-12T00:00:00.000Z',
      updatedAt: '2026-05-12T00:00:00.000Z',
      lastOpenedAt: '2026-05-12T00:00:00.000Z',
    };

    mockValidatedWorkspaceHostApi(activeProject);

    renderSettings();
    fireEvent.click(await screen.findByRole('button', { name: 'appearance.showMoreSettings' }));

    const workspace = await screen.findByTestId('settings-opencode-workspace');
    expect(within(workspace).getByText('Workspace')).toBeInTheDocument();
    expect(screen.getByTestId('settings-opencode-runtime')).toBeInTheDocument();
    expect(screen.getByTestId('settings-opencode-models')).toBeInTheDocument();
    expect(screen.getByText('openai/gpt-5.4')).toBeInTheDocument();
    expect(screen.getByText('openai/gpt-5.4-mini')).toBeInTheDocument();
    expect(screen.getByText('2 providers')).toBeInTheDocument();
    expect(screen.getAllByText('D:/repo/packages/ui').length).toBeGreaterThan(0);

    await waitFor(() =>
      expect(hostApiFetchMock).toHaveBeenCalledWith('/api/opencode/start', {
        method: 'POST',
      })
    );
    expect(screen.getByRole('button', { name: 'Restart runtime' })).toBeInTheDocument();
    expect(screen.getByRole('button', { name: 'Stop runtime' })).toBeInTheDocument();
    expect(document.body.textContent).not.toMatch(/opencode/i);
    expect(screen.queryByLabelText(/opencode/i)).not.toBeInTheDocument();

    fireEvent.click(screen.getByRole('button', { name: 'Check health' }));

    await waitFor(() => expect(hostApiFetchMock).toHaveBeenCalledWith('/api/opencode/health'));
  });

  it('opens the model configuration page from the workspace models card', async () => {
    const activeProject = {
      id: 'prj_1',
      path: 'D:/repo/packages/ui',
      name: 'ui',
      createdAt: '2026-05-12T00:00:00.000Z',
      updatedAt: '2026-05-12T00:00:00.000Z',
      lastOpenedAt: '2026-05-12T00:00:00.000Z',
    };
    mockValidatedWorkspaceHostApi(activeProject);

    renderSettings();
    fireEvent.click(await screen.findByRole('button', { name: 'appearance.showMoreSettings' }));

    const modelsCard = await screen.findByTestId('settings-opencode-models');
    fireEvent.click(within(modelsCard).getByRole('button', { name: 'Configure models' }));

    expect(screen.getByTestId('models-config-page')).toBeInTheDocument();
  });

  it('does not activate a workspace project when its configuration is missing', async () => {
    const activeProject = {
      id: 'prj_1',
      path: 'D:/repo/packages/ui',
      name: 'ui',
      createdAt: '2026-05-12T00:00:00.000Z',
      updatedAt: '2026-05-12T00:00:00.000Z',
      lastOpenedAt: '2026-05-12T00:00:00.000Z',
    };
    hostApiFetchMock.mockImplementation(async (path: string, _init?: RequestInit) => {
      if (path === '/api/opencode/status') return { state: 'stopped', port: 4096 };
      if (path === '/api/opencode/config-summary') {
        return {
          model: 'openai/gpt-5.4',
          smallModel: 'openai/gpt-5.4-mini',
          providerIds: ['openai'],
          providerCount: 1,
        };
      }
      if (path === '/api/opencode/projects') return { projects: [activeProject], activeProject: null };
      if (path === `/api/opencode/projects/config?projectId=${encodeURIComponent(activeProject.id)}`) {
        return { status: 'missing' };
      }
      throw new Error(`Unexpected path ${path}`);
    });

    renderSettings();
    fireEvent.click(await screen.findByRole('button', { name: 'appearance.showMoreSettings' }));
    fireEvent.click(await screen.findByRole('button', { name: `Use project ${activeProject.name}` }));

    expect(await screen.findByText(/configuration is missing/i)).toBeInTheDocument();
    expect(useOpencodeStore.getState().activeProject).toBeNull();
  });

  it('activates a workspace project only after its configuration validates', async () => {
    const activeProject = {
      id: 'prj_1',
      path: 'D:/repo/packages/ui',
      name: 'ui',
      createdAt: '2026-05-12T00:00:00.000Z',
      updatedAt: '2026-05-12T00:00:00.000Z',
      lastOpenedAt: '2026-05-12T00:00:00.000Z',
    };
    const config = { ...createProjectConfig(), initialized: true };
    hostApiFetchMock.mockImplementation(async (path: string, init?: RequestInit) => {
      if (path === '/api/opencode/status') return { state: 'stopped', port: 4096 };
      if (path === '/api/opencode/config-summary') {
        return {
          model: 'openai/gpt-5.4',
          smallModel: 'openai/gpt-5.4-mini',
          providerIds: ['openai'],
          providerCount: 1,
        };
      }
      if (path === '/api/opencode/projects') return { projects: [activeProject], activeProject: null };
      if (path === `/api/opencode/projects/config?projectId=${encodeURIComponent(activeProject.id)}`) {
        return { status: 'valid', config, knowledgeFiles: [] };
      }
      if (path === '/api/opencode/projects/active' && init?.method === 'POST') {
        return {
          success: true,
          project: activeProject,
          projects: [activeProject],
          activeProject,
        };
      }
      throw new Error(`Unexpected path ${path}`);
    });

    renderSettings();
    fireEvent.click(await screen.findByRole('button', { name: 'appearance.showMoreSettings' }));
    fireEvent.click(await screen.findByRole('button', { name: `Use project ${activeProject.name}` }));

    await waitFor(() => {
      expect(hostApiFetchMock).toHaveBeenCalledWith('/api/opencode/projects/active', {
        method: 'POST',
        body: JSON.stringify({ projectId: activeProject.id }),
      });
    });
    expect(useOpencodeStore.getState().activeProject).toEqual(activeProject);
  });
});
