import { beforeEach, describe, expect, it, vi } from 'vitest';
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
import { MemoryRouter } from 'react-router-dom';
import { Tasks } from '@/pages/Tasks';
import { useCronStore } from '@/stores/cron';
import { useGatewayStore } from '@/stores/gateway';
import { useSkillsStore } from '@/stores/skills';
import { useTaskCenterStore } from '@/stores/task-center';
import { useYinianStore } from '@/stores/yinian';
import { useChatStore } from '@/stores/chat';
import i18n from '@/i18n';
import type { YinianConfigSnapshot } from '../../shared/yinian';

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),
}));

const hotelHangzhou = {
  id: 'workspace_hangzhou_ops',
  name: '智念企业组织空间',
  city: '杭州',
  role: 'manager' as const,
  permissions: ['skills.sync'],
  ota: [],
};

function createConfig(overrides: Partial<YinianConfigSnapshot> = {}): YinianConfigSnapshot {
  return {
    serverTime: 1,
    user: { id: 'user_1', name: '王管理员' },
    hotel: hotelHangzhou,
    hotels: [hotelHangzhou],
    entitlements: [
      {
        skillId: 'daily-report',
        name: '日报生成助手',
        version: '1.0.0',
        enabled: true,
        category: 'reporting',
        triggers: ['manual', 'scheduled'],
      },
      {
        skillId: 'weekly-report',
        name: '周报分析助手',
        version: '1.0.0',
        enabled: true,
        category: 'reporting',
        triggers: ['manual', 'scheduled'],
      },
    ],
    notificationChannels: [],
    featureFlags: {},
    uiPolicy: { defaultPage: 'today', showAdvancedSettings: false },
    ...overrides,
  };
}

function renderTasks() {
  return render(
    <MemoryRouter>
      <Tasks />
    </MemoryRouter>,
  );
}

describe('Tasks page', () => {
  beforeEach(async () => {
    vi.clearAllMocks();
    await i18n.changeLanguage('zh');
    invokeIpcMock.mockResolvedValue({ success: true, result: { messages: [] } });
    hostApiFetchMock.mockImplementation(async (path: string, options?: { method?: string; body?: string }) => {
      if (path === '/api/channels/accounts') {
        return {
          success: true,
          channels: [
            {
              channelType: 'feishu',
              defaultAccountId: 'feishu-main',
              accounts: [{ accountId: 'feishu-main', name: '飞书主账号', isDefault: true }],
            },
          ],
        };
      }
      if (path.startsWith('/api/channels/targets')) {
        return {
          success: true,
          targets: [{ value: 'user:ou_daily', label: '运营负责人', kind: 'user' }],
        };
      }
      if (path === '/api/cron/jobs' && options?.method === 'POST') {
        const body = JSON.parse(options.body || '{}');
        return {
          id: 'cron-daily',
          createdAt: '2026-05-13T00:00:00.000Z',
          updatedAt: '2026-05-13T00:00:00.000Z',
          nextRun: '2026-05-14T01:00:00.000Z',
          lastRun: undefined,
          agentId: body.agentId || 'main',
          ...body,
        };
      }
      if (path === '/api/cron/jobs') {
        return { jobs: [] };
      }
      return { success: true };
    });
    useYinianStore.setState({
      status: 'authenticated',
      session: {
        authenticated: true,
        user: { id: 'user_1', name: '王管理员' },
        hotels: [hotelHangzhou],
        currentHotelId: hotelHangzhou.id,
        accessTokenExpiresAt: 100,
      },
      config: createConfig(),
      error: null,
    });
    useGatewayStore.setState({
      status: {
        state: 'running',
        port: 18789,
        gatewayReady: true,
      },
    });
    useSkillsStore.setState({
      skills: [],
      loading: false,
      error: null,
      fetchSkills: vi.fn().mockResolvedValue(undefined),
    });
    useCronStore.setState({
      jobs: [],
      loading: false,
      error: null,
    });
    useTaskCenterStore.setState({
      scheduledBindings: {},
      runRecords: [],
      pinnedTaskIds: [],
    });
    useChatStore.setState({
      currentSessionKey: 'agent:main:main',
      currentAgentId: 'main',
      sessions: [{ key: 'agent:main:main', displayName: 'main' }],
      messages: [],
      sessionLabels: {},
      sessionLastActivity: {},
      sending: false,
      activeRunId: null,
      activeRunSessionKey: null,
      streamingText: '',
      streamingMessage: null,
      streamingTools: [],
      pendingFinal: false,
      lastUserMessageAt: null,
      pendingToolImages: [],
      error: null,
    });
  });

  it('offers quick task templates that prefill the scheduled task dialog', async () => {
    renderTasks();

    expect(screen.getByTestId('tasks-quick-templates')).toHaveTextContent('快捷任务');
    fireEvent.click(screen.getByTestId('tasks-template-daily-brief'));

    expect(screen.getByDisplayValue('每日经营日报')).toBeInTheDocument();
    expect(screen.getByDisplayValue(/昨日经营情况/)).toBeInTheDocument();

    fireEvent.click(screen.getByRole('button', { name: '保存' }));

    await waitFor(() => {
      expect(hostApiFetchMock).toHaveBeenCalledWith('/api/cron/jobs', expect.objectContaining({ method: 'POST' }));
    });
    const createCall = hostApiFetchMock.mock.calls.find(([path, options]) => (
      path === '/api/cron/jobs' && (options as { method?: string } | undefined)?.method === 'POST'
    ));
    const payload = JSON.parse((createCall?.[1] as { body: string }).body);
    expect(payload).toMatchObject({
      name: '每日经营日报',
      schedule: '0 9 * * *',
      enabled: true,
    });
    expect(payload.message).toContain('昨日经营情况');
  });

  it('pins task center cards to the sidebar quick trigger list', async () => {
    useCronStore.setState({
      jobs: [{
        id: 'cron-daily',
        name: '每日经营日报',
        message: '使用日报生成助手 skill 生成日报',
        schedule: '0 9 * * *',
        delivery: { mode: 'none' },
        enabled: true,
        createdAt: '2026-05-13T00:00:00.000Z',
        updatedAt: '2026-05-13T00:00:00.000Z',
        agentId: 'main',
      }],
      loading: false,
      error: null,
    });

    renderTasks();

    fireEvent.click(await screen.findByRole('button', { name: '钉到侧边栏' }));
    expect(useTaskCenterStore.getState().pinnedTaskIds).toEqual(['cron-daily']);

    fireEvent.click(screen.getByRole('button', { name: '取消固定' }));
    expect(useTaskCenterStore.getState().pinnedTaskIds).toEqual([]);
  });

  it('runs a task in its fixed conversation and switches there immediately', async () => {
    useCronStore.setState({
      jobs: [{
        id: 'cron-daily',
        name: '每日经营日报',
        message: '使用日报生成助手 skill 生成日报',
        schedule: '0 9 * * *',
        delivery: { mode: 'none' },
        enabled: true,
        createdAt: '2026-05-13T00:00:00.000Z',
        updatedAt: '2026-05-13T00:00:00.000Z',
        agentId: 'main',
      }],
      loading: false,
      error: null,
    });

    renderTasks();

    fireEvent.click(await screen.findByRole('button', { name: '立即执行' }));

    await waitFor(() => {
      expect(useChatStore.getState().currentSessionKey).toBe('agent:main:cron:cron-daily');
    });
    expect(useChatStore.getState().sessionLabels['agent:main:cron:cron-daily']).toBe('每日经营日报');
    expect(useChatStore.getState().sessions.find((session) => session.key === 'agent:main:cron:cron-daily')).toMatchObject({
      label: '每日经营日报',
      displayName: '每日经营日报',
    });
    expect(useTaskCenterStore.getState().runRecords[0]).toMatchObject({
      cronJobId: 'cron-daily',
      sessionKey: 'agent:main:cron:cron-daily',
    });
    expect(hostApiFetchMock).toHaveBeenCalledWith('/api/cron/trigger', expect.objectContaining({ method: 'POST' }));
  });

  it('creates task center tasks with keyboard @ skill insertion, structured custom time, and delivery channel payload', async () => {
    renderTasks();

    expect(screen.queryByTestId('tasks-tab-quick')).not.toBeInTheDocument();
    fireEvent.click(await screen.findByRole('button', { name: '新建任务' }));
    expect(screen.queryByText('快捷能力模板（可选）')).not.toBeInTheDocument();
    fireEvent.change(screen.getByPlaceholderText('例如：每日经营日报'), {
      target: { value: '每日经营日报' },
    });

    const contentBox = screen.getByPlaceholderText('写清楚每次执行时需要完成的具体内容');
    fireEvent.change(contentBox, { target: { value: '@' } });
    await screen.findByRole('option', { name: /日报生成助手/ });
    fireEvent.keyDown(contentBox, { key: 'ArrowDown' });
    fireEvent.keyDown(contentBox, { key: 'Enter' });
    expect(contentBox).toHaveValue('使用周报分析助手 skill');

    const middleText = '请先检查@日报后继续';
    fireEvent.change(contentBox, {
      target: {
        value: middleText,
        selectionStart: '请先检查@日报'.length,
      },
    });
    fireEvent.mouseDown(await screen.findByRole('option', { name: /日报生成助手/ }));
    expect(contentBox).toHaveValue('请先检查 使用日报生成助手 skill 后继续');

    fireEvent.click(screen.getByRole('button', { name: '自定义时间' }));
    fireEvent.change(screen.getByLabelText('重复方式'), { target: { value: 'weekly' } });
    fireEvent.change(screen.getByLabelText('执行时刻'), { target: { value: '10:30' } });
    fireEvent.change(screen.getByLabelText('星期'), { target: { value: '3' } });

    fireEvent.click(screen.getByRole('button', { name: /发送到外部通道/ }));
    fireEvent.change(screen.getByLabelText('通道'), { target: { value: 'feishu' } });
    await waitFor(() => expect(hostApiFetchMock).toHaveBeenCalledWith('/api/channels/targets?channelType=feishu&accountId=feishu-main'));
    fireEvent.change(screen.getByLabelText('接收目标'), { target: { value: 'user:ou_daily' } });
    fireEvent.click(screen.getByRole('button', { name: '保存' }));

    await waitFor(() => {
      expect(hostApiFetchMock).toHaveBeenCalledWith('/api/cron/jobs', expect.objectContaining({ method: 'POST' }));
    });
    const createCall = hostApiFetchMock.mock.calls.find(([path, options]) => (
      path === '/api/cron/jobs' && (options as { method?: string } | undefined)?.method === 'POST'
    ));
    const payload = JSON.parse((createCall?.[1] as { body: string }).body);
    expect(payload.message).toContain('请先检查 使用日报生成助手 skill 后继续');
    expect(payload.schedule).toBe('30 10 * * 3');
    expect(payload.delivery).toEqual({
      mode: 'announce',
      channel: 'feishu',
      accountId: 'feishu-main',
      to: 'user:ou_daily',
    });
  });
});
