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

const hostApiFetchMock = vi.hoisted(() => vi.fn());

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

function resetAuthStore() {
  useAuthStore.setState({
    initialized: true,
    loading: false,
    error: null,
    authBase: '',
    clientId: 'app',
    accessToken: null,
    refreshToken: null,
    tokenType: null,
    expiresAt: null,
    user: null,
  });
}

describe('Login page', () => {
  beforeEach(() => {
    window.electron.imageWorkspaceLocalDevelopment = false;
    window.localStorage.clear();
    hostApiFetchMock.mockReset();
    resetAuthStore();
    useSettingsStore.getState().resetSettings();
    useOpencodeStore.setState({ projects: [], activeProject: null });
    useProjectConfigStore.setState({ configsByProjectId: {}, knowledgeByProjectId: {}, loadingProjectId: null, errorsByProjectId: {} });
    vi.unstubAllGlobals();
  });

  it('keeps the login route reachable from the app router', () => {
    vi.stubGlobal('__NIANCODE_RENDERER_ONLY__', true);

    render(
      <MemoryRouter initialEntries={['/login']}>
        <App />
      </MemoryRouter>,
    );

    expect(screen.getByRole('button', { name: 'Continue in browser' })).toBeInTheDocument();
  });

  it('redirects protected app routes to login when setup is complete but the user is signed out', async () => {
    useSettingsStore.setState({ setupComplete: true });

    render(
      <MemoryRouter initialEntries={['/models']}>
        <App />
      </MemoryRouter>,
    );

    expect(await screen.findByRole('button', { name: 'Continue in browser' })).toBeInTheDocument();
  });

  it('opens only the local image workspace anonymously when the explicit development mode is active', async () => {
    useSettingsStore.setState({ setupComplete: true });
    window.electron.imageWorkspaceLocalDevelopment = true;
    hostApiFetchMock.mockImplementation(async (path: string) => {
      if (path === '/api/works/image-workspace') {
        return {
          success: true,
          workspace: {
            activeProjectId: null,
            capabilities: {
              modes: [],
              models: [],
              aspectRatios: [],
              resolutions: [],
              outputCounts: [],
              maxReferenceImages: 0,
              referenceUpload: { enabled: false, acceptedMimeTypes: [] },
            },
            projects: [],
          },
        };
      }
      return { success: true };
    });

    const { unmount } = render(
      <MemoryRouter initialEntries={['/']}>
        <App />
      </MemoryRouter>,
    );

    expect(await screen.findByText('创建第一个项目')).toBeInTheDocument();
    expect(screen.queryByText('本地开发')).not.toBeInTheDocument();
    expect(screen.queryByRole('button', { name: 'Continue in browser' })).not.toBeInTheDocument();
    unmount();

    render(
      <MemoryRouter initialEntries={['/models']}>
        <App />
      </MemoryRouter>,
    );
    expect(await screen.findByRole('button', { name: 'Continue in browser' })).toBeInTheDocument();
  });

  it('opens the project conversation from the default route for an initialized active project', async () => {
    vi.stubGlobal('__NIANCODE_RENDERER_ONLY__', true);
    const project = {
      id: 'project-default-chat',
      path: '/tmp/project-default-chat',
      name: 'default-chat',
      createdAt: '2026-07-12T00:00:00.000Z',
      updatedAt: '2026-07-12T00:00:00.000Z',
      lastOpenedAt: '2026-07-12T00:00:00.000Z',
    };
    const config = { ...createProjectConfig('standard-development'), initialized: true };
    useOpencodeStore.setState({ projects: [project], activeProject: project });
    hostApiFetchMock.mockImplementation(async (path: string) => {
      if (path === '/api/opencode/projects') return { projects: [project], activeProject: project };
      if (path.startsWith('/api/opencode/projects/config?')) return { status: 'valid', config, knowledgeFiles: [] };
      if (path === '/api/opencode/status') return { state: 'stopped', port: 4096 };
      if (path === '/api/opencode/config-summary') return { providerIds: [], providerCount: 0, envKeys: [] };
      if (path === '/api/provider-accounts') return [];
      if (path === '/api/provider-accounts/key-info') return [];
      if (path === '/api/provider-vendors') return [];
      if (path === '/api/provider-accounts/default') return { accountId: null };
      return { success: true };
    });

    render(
      <MemoryRouter initialEntries={['/']}>
        <App />
      </MemoryRouter>,
    );

    expect(await screen.findByTestId('chat-operation-page')).toBeInTheDocument();
  });

  it('keeps the default route on project configuration without an active project', async () => {
    vi.stubGlobal('__NIANCODE_RENDERER_ONLY__', true);
    hostApiFetchMock.mockImplementation(async (path: string) => {
      if (path === '/api/opencode/projects') return { projects: [], activeProject: null };
      if (path === '/api/opencode/status') return { state: 'stopped', port: 4096 };
      if (path === '/api/opencode/config-summary') return { providerIds: [], providerCount: 0, envKeys: [] };
      if (path === '/api/provider-accounts') return [];
      if (path === '/api/provider-accounts/key-info') return [];
      if (path === '/api/provider-vendors') return [];
      if (path === '/api/provider-accounts/default') return { accountId: null };
      return { success: true };
    });

    render(
      <MemoryRouter initialEntries={['/']}>
        <App />
      </MemoryRouter>,
    );

    expect(await screen.findByText('请先新建项目')).toBeInTheDocument();
    expect(screen.queryByTestId('chat-operation-page')).not.toBeInTheDocument();
  });

  it('keeps an uninitialized active project on project configuration', async () => {
    vi.stubGlobal('__NIANCODE_RENDERER_ONLY__', true);
    const project = {
      id: 'project-uninitialized',
      path: '/tmp/project-uninitialized',
      name: 'uninitialized',
      createdAt: '2026-07-12T00:00:00.000Z',
      updatedAt: '2026-07-12T00:00:00.000Z',
      lastOpenedAt: '2026-07-12T00:00:00.000Z',
    };
    const config = { ...createProjectConfig('standard-development'), initialized: false };
    useOpencodeStore.setState({ projects: [project], activeProject: project });
    hostApiFetchMock.mockImplementation(async (path: string) => {
      if (path === '/api/opencode/projects') return { projects: [project], activeProject: project };
      if (path.startsWith('/api/opencode/projects/config?')) return { status: 'valid', config, knowledgeFiles: [] };
      if (path === '/api/opencode/status') return { state: 'stopped', port: 4096 };
      if (path === '/api/opencode/config-summary') return { providerIds: [], providerCount: 0, envKeys: [] };
      if (path === '/api/provider-accounts') return [];
      if (path === '/api/provider-accounts/key-info') return [];
      if (path === '/api/provider-vendors') return [];
      if (path === '/api/provider-accounts/default') return { accountId: null };
      return { success: true };
    });

    render(
      <MemoryRouter initialEntries={['/']}>
        <App />
      </MemoryRouter>,
    );

    expect(await screen.findByText('在这里配置你的项目基础！')).toBeInTheDocument();
    expect(screen.queryByTestId('chat-operation-page')).not.toBeInTheDocument();
  });

  it('starts browser authorization and enters the app after success', async () => {
    hostApiFetchMock.mockImplementation(async (path: string) => {
      if (path === '/api/auth/browser/start') {
        return {
          success: true,
          token: {
            access_token: 'access-token',
            refresh_token: 'refresh-token',
            token_type: 'Bearer',
            expires_in: 43200,
            username: 'zhangsan',
            user_id: '1',
          },
        };
      }
      if (path === '/api/auth/session/sync') return { success: true };
      if (path === '/api/provider-accounts/import-user-model-config') {
        return {
          success: true,
          account: {
            id: 'niancode-user-models',
            vendorId: 'custom',
            label: 'Makelore Models',
            authMode: 'api_key',
            model: 'gpt-4.1-mini',
            enabled: true,
            isDefault: true,
            createdAt: '2026-07-07T00:00:00.000Z',
            updatedAt: '2026-07-07T00:00:00.000Z',
          },
          importedModels: ['gpt-4.1-mini'],
        };
      }
      if (path === '/api/provider-accounts') return [];
      if (path === '/api/provider-accounts/key-info') return [];
      if (path === '/api/provider-vendors') return [];
      if (path === '/api/provider-accounts/default') return { accountId: null };
      return { success: true };
    });

    render(
      <MemoryRouter initialEntries={['/login']}>
        <Routes>
          <Route path="/login" element={<Login />} />
          <Route path="/module-select" element={<div>Module Selection</div>} />
        </Routes>
      </MemoryRouter>,
    );

    expect(screen.queryByLabelText('Username')).not.toBeInTheDocument();
    expect(screen.queryByLabelText('Password')).not.toBeInTheDocument();
    expect(screen.queryByLabelText('Captcha')).not.toBeInTheDocument();
    fireEvent.click(screen.getByRole('button', { name: 'Continue in browser' }));

    await screen.findByText('Module Selection');
    await waitFor(() => {
      expect(hostApiFetchMock).toHaveBeenCalledWith('/api/auth/browser/start', {
        method: 'POST',
      });
    });
  });

  it('syncs the current Works Square user model config after browser login', async () => {
    hostApiFetchMock.mockImplementation(async (path: string) => {
      if (path === '/api/auth/browser/start') {
        return {
          success: true,
          token: {
            access_token: 'fresh-access-token',
            refresh_token: 'refresh-token',
            token_type: 'Bearer',
            expires_in: 43200,
            username: 'zhangsan',
            user_id: '1',
          },
        };
      }
      if (path === '/api/auth/session/sync') {
        return { success: true };
      }
      if (path === '/api/provider-accounts/import-user-model-config') {
        return {
          success: true,
          account: {
            id: 'niancode-user-models',
            vendorId: 'custom',
            label: 'Makelore Models',
            authMode: 'api_key',
            model: 'gpt-4.1-mini',
            fallbackModels: ['claude-3-5-haiku'],
            enabled: true,
            isDefault: true,
            createdAt: '2026-07-07T00:00:00.000Z',
            updatedAt: '2026-07-07T00:00:00.000Z',
          },
          importedModels: ['gpt-4.1-mini', 'claude-3-5-haiku'],
        };
      }
      if (path === '/api/provider-accounts/default') {
        return { accountId: 'niancode-user-models' };
      }
      return [];
    });

    render(
      <MemoryRouter initialEntries={['/login']}>
        <Routes>
          <Route path="/login" element={<Login />} />
          <Route path="/module-select" element={<div>Module Selection</div>} />
        </Routes>
      </MemoryRouter>,
    );

    fireEvent.click(screen.getByRole('button', { name: 'Continue in browser' }));

    await waitFor(() => {
      expect(hostApiFetchMock).toHaveBeenCalledWith('/api/provider-accounts/import-user-model-config', {
        method: 'POST',
        body: JSON.stringify({ accessToken: 'fresh-access-token' }),
      });
    });
  });

  it('keeps the user on login when model config sync fails after browser login', async () => {
    hostApiFetchMock
      .mockResolvedValueOnce({
        success: true,
        token: {
          access_token: 'fresh-access-token',
          refresh_token: 'refresh-token',
          token_type: 'Bearer',
          expires_in: 43200,
          username: 'zhangsan',
          user_id: '1',
        },
      })
      .mockResolvedValueOnce({ success: true })
      .mockRejectedValueOnce(new Error('sync failed'))
      .mockResolvedValueOnce({ success: true });

    render(
      <MemoryRouter initialEntries={['/login']}>
        <Routes>
          <Route path="/login" element={<Login />} />
          <Route path="/module-select" element={<div>Module Selection</div>} />
        </Routes>
      </MemoryRouter>,
    );

    fireEvent.click(screen.getByRole('button', { name: 'Continue in browser' }));

    expect(await screen.findByText('sync failed')).toBeInTheDocument();
    expect(screen.queryByText('Module Selection')).not.toBeInTheDocument();
    expect(hostApiFetchMock).toHaveBeenCalledWith('/api/auth/logout', {
      method: 'POST',
      body: JSON.stringify({ accessToken: 'fresh-access-token' }),
    });
  });
});
