import { beforeEach, describe, expect, it, vi } from 'vitest';

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

vi.mock('./github-ci.js', () => ({
  checkGitHubActionsRun: checkGitHubActionsRunMock,
  computeGitHubWatcherDelayMs: vi.fn(() => 15_000),
  MAX_GITHUB_CONSECUTIVE_ERRORS: 5,
  parseGitHubCiMetadata: vi.fn((raw: string | null | undefined) => {
    if (!raw) return null;
    return JSON.parse(raw);
  }),
  serializeGitHubCiMetadata: vi.fn((metadata: unknown) =>
    JSON.stringify(metadata),
  ),
}));

vi.mock('./logger.js', () => {
  const mockLogger = {
    debug: vi.fn(),
    info: vi.fn(),
    warn: vi.fn(),
    error: vi.fn(),
    child: (_bindings?: Record<string, unknown>) => mockLogger,
  };
  return {
    logger: mockLogger,
    createScopedLogger: (_bindings?: Record<string, unknown>) => mockLogger,
  };
});

import {
  _initTestDatabase,
  createPairedTask,
  createTask,
  getRecentChatMessages,
  getTaskById,
} from './db.js';
import { runGithubCiTask } from './task-scheduler-github.js';
import type { SchedulerDependencies } from './task-scheduler-types.js';
import type { ScheduledTask } from './types.js';

function buildSchedulerDeps(overrides: {
  sendMessage?: SchedulerDependencies['sendMessage'];
  sendTrackedMessage?: SchedulerDependencies['sendTrackedMessage'];
  editTrackedMessage?: SchedulerDependencies['editTrackedMessage'];
  enqueueMessageCheck?: (chatJid: string, ipcDir?: string) => void;
}): SchedulerDependencies {
  return {
    serviceAgentType: 'codex',
    roomBindings: () => ({}),
    getSessions: () => ({}),
    queue: {
      enqueueMessageCheck: overrides.enqueueMessageCheck ?? vi.fn(),
    } as any,
    onProcess: () => {},
    sendMessage: overrides.sendMessage ?? vi.fn(async () => {}),
    sendTrackedMessage: overrides.sendTrackedMessage,
    editTrackedMessage: overrides.editTrackedMessage,
  };
}

function createReviewReadyPairedTask(now: string): void {
  createPairedTask({
    id: 'paired-review-ready',
    chat_jid: 'shared@g.us',
    group_folder: 'shared-group',
    owner_service_id: 'codex-main',
    reviewer_service_id: 'claude-review',
    owner_agent_type: 'codex',
    reviewer_agent_type: 'claude-code',
    arbiter_agent_type: 'codex',
    title: null,
    source_ref: null,
    plan_notes: null,
    review_requested_at: null,
    round_trip_count: 1,
    owner_failure_count: 0,
    owner_step_done_streak: 0,
    finalize_step_done_count: 0,
    task_done_then_user_reopen_count: 0,
    empty_step_done_streak: 0,
    status: 'review_ready',
    arbiter_verdict: null,
    arbiter_requested_at: null,
    completion_reason: null,
    created_at: now,
    updated_at: now,
  });
}

function createGithubWatcherTask(overrides: {
  id?: string;
  roomRole?: ScheduledTask['room_role'];
  statusMessageId?: string | null;
  statusStartedAt?: string | null;
  now: string;
}): ScheduledTask {
  const taskId = overrides.id ?? 'task-github-owner-complete';
  createTask({
    id: taskId,
    group_folder: 'shared-group',
    chat_jid: 'shared@g.us',
    agent_type: 'codex',
    room_role: overrides.roomRole,
    ci_provider: 'github',
    ci_metadata: JSON.stringify({
      repo: 'owner/repo',
      run_id: 777777,
    }),
    prompt: `
[BACKGROUND CI WATCH]

Watch target:
GitHub Actions run 777777

Check instructions:
Managed by host-driven watcher.
    `.trim(),
    schedule_type: 'interval',
    schedule_value: '15000',
    context_mode: 'isolated',
    next_run: overrides.now,
    status: 'active',
    status_message_id: overrides.statusMessageId,
    status_started_at: overrides.statusStartedAt,
    created_at: overrides.now,
  });
  return getTaskById(taskId)!;
}

describe('GitHub CI watcher scheduler', () => {
  beforeEach(() => {
    _initTestDatabase();
    checkGitHubActionsRunMock.mockReset();
  });

  it('queues an owner turn after an owner CI watcher completes in a paired room', async () => {
    checkGitHubActionsRunMock.mockResolvedValueOnce({
      terminal: true,
      resultSummary: '성공: owner/repo run 777777',
      completionMessage: 'CI 완료: GitHub Actions run 777777\n판정: 성공',
    });
    const now = '2026-02-22T00:00:00.000Z';
    createReviewReadyPairedTask(now);
    const task = createGithubWatcherTask({ now, roomRole: 'owner' });
    const sendMessage = vi.fn(async () => {});
    const enqueueMessageCheck = vi.fn();

    await runGithubCiTask(
      task,
      buildSchedulerDeps({ sendMessage, enqueueMessageCheck }),
    );

    expect(sendMessage).toHaveBeenCalledWith(
      'shared@g.us',
      'CI 완료: GitHub Actions run 777777\n판정: 성공',
    );
    expect(enqueueMessageCheck).toHaveBeenCalledWith(
      'shared@g.us',
      expect.stringContaining('shared-group'),
    );
    expect(getRecentChatMessages('shared@g.us', 5).at(-1)).toMatchObject({
      sender: 'ci-watcher',
      sender_name: 'CI watcher',
      is_bot_message: false,
      message_source_kind: 'trusted_external_bot',
      content:
        '[CI watcher completed]\nCI 완료: GitHub Actions run 777777\n판정: 성공',
    });
    expect(getTaskById(task.id)).toBeUndefined();
  });

  it('queues an owner turn even when the paired task already closed before CI completion', async () => {
    checkGitHubActionsRunMock.mockResolvedValueOnce({
      terminal: true,
      resultSummary: '성공: owner/repo run 777777',
      completionMessage: 'CI 완료: GitHub Actions run 777777\n판정: 성공',
    });
    const now = '2026-02-22T00:00:00.000Z';
    const task = createGithubWatcherTask({ now, roomRole: 'owner' });
    const enqueueMessageCheck = vi.fn();

    await runGithubCiTask(task, buildSchedulerDeps({ enqueueMessageCheck }));

    expect(enqueueMessageCheck).toHaveBeenCalledWith(
      'shared@g.us',
      expect.stringContaining('shared-group'),
    );
    expect(getRecentChatMessages('shared@g.us', 5).at(-1)).toMatchObject({
      sender: 'ci-watcher',
      sender_name: 'CI watcher',
      is_bot_message: false,
      message_source_kind: 'trusted_external_bot',
      content:
        '[CI watcher completed]\nCI 완료: GitHub Actions run 777777\n판정: 성공',
    });
    expect(getTaskById(task.id)).toBeUndefined();
  });

  it('does not queue owner wake-up for non-owner CI watchers', async () => {
    checkGitHubActionsRunMock.mockResolvedValueOnce({
      terminal: true,
      resultSummary: '성공: owner/repo run 777777',
      completionMessage: 'CI 완료: GitHub Actions run 777777\n판정: 성공',
    });
    const now = '2026-02-22T00:00:00.000Z';
    createReviewReadyPairedTask(now);
    const task = createGithubWatcherTask({ now, roomRole: 'reviewer' });
    const enqueueMessageCheck = vi.fn();

    await runGithubCiTask(task, buildSchedulerDeps({ enqueueMessageCheck }));

    expect(enqueueMessageCheck).not.toHaveBeenCalled();
    expect(getRecentChatMessages('shared@g.us', 5)).toEqual([]);
    expect(getTaskById(task.id)).toBeUndefined();
  });

  it('edits the tracked watcher status and still wakes owner without a duplicate bot completion message', async () => {
    checkGitHubActionsRunMock.mockResolvedValueOnce({
      terminal: true,
      resultSummary: '성공: owner/repo run 777777',
      completionMessage: 'CI 완료: GitHub Actions run 777777\n판정: 성공',
    });
    const now = '2026-02-22T00:00:00.000Z';
    createReviewReadyPairedTask(now);
    const task = createGithubWatcherTask({
      now,
      roomRole: 'owner',
      statusMessageId: 'status-msg-1',
      statusStartedAt: now,
    });
    const sendMessage = vi.fn(async () => {});
    const sendTrackedMessage = vi.fn(async () => 'status-msg-new');
    const editTrackedMessage = vi.fn(async () => {});
    const enqueueMessageCheck = vi.fn();

    await runGithubCiTask(
      task,
      buildSchedulerDeps({
        sendMessage,
        sendTrackedMessage,
        editTrackedMessage,
        enqueueMessageCheck,
      }),
    );

    expect(editTrackedMessage).toHaveBeenCalledWith(
      'shared@g.us',
      'status-msg-1',
      expect.stringContaining('CI 감시 종료: GitHub Actions run 777777'),
    );
    expect(sendTrackedMessage).not.toHaveBeenCalled();
    expect(sendMessage).not.toHaveBeenCalled();
    expect(enqueueMessageCheck).toHaveBeenCalledWith(
      'shared@g.us',
      expect.stringContaining('shared-group'),
    );
    expect(getRecentChatMessages('shared@g.us', 5).at(-1)).toMatchObject({
      sender: 'ci-watcher',
      sender_name: 'CI watcher',
      is_bot_message: false,
      message_source_kind: 'trusted_external_bot',
      content:
        '[CI watcher completed]\nCI 완료: GitHub Actions run 777777\n판정: 성공',
    });
    expect(getTaskById(task.id)).toBeUndefined();
  });
});
