import { describe, expect, it, jest } from '@jest/globals';
import type { DataSource } from 'typeorm';
import { AgentToolContextFactory, type AgentToolContext } from '../agent-tools/agent-tool.types';
import type { AuthenticatedUser } from '../authorization';
import type { StudentAccessScopeFactory } from '../students/student-access-scope.factory';
import { PendingTasksService } from './pending-tasks.service';

function context(permissions: string[], isSuperAdmin = false): AgentToolContext {
  const user: AuthenticatedUser = {
    id: 7,
    username: 'ops',
    permissions,
    isSuperAdmin,
    roles: [],
  };
  return AgentToolContextFactory.fromAuthenticatedUser(user);
}

function createService(
  scopeType: 'manageAll' | 'teacher',
  queryMock: jest.Mock<Promise<Array<Record<string, unknown>>>>,
) {
  const dataSource = { query: queryMock } as unknown as DataSource;
  const scopeFactory = {
    buildScope: jest.fn().mockReturnValue(
      scopeType === 'manageAll'
        ? { type: 'manageAll' }
        : { type: 'teacher', userId: 7 },
    ),
  } as unknown as StudentAccessScopeFactory;
  return new PendingTasksService(dataSource, scopeFactory);
}

const ROW = (cnt: unknown) => [{ cnt }] as Record<string, unknown>[];

describe('PendingTasksService', () => {
  it('manageAll 权限下返回全部待办计数', async () => {
    const query = jest.fn().mockImplementation(async (sql: string) => {
      if (sql.includes('NOT EXISTS') && sql.includes('class_student')) return ROW('3');
      if (sql.includes('LEFT JOIN occupancies o')) return ROW('5');
      if (sql.includes('FROM occupancies o')) return ROW('8');
      if (sql.includes('classroom_rentals r') && sql.includes('contract_path IS NULL')) return ROW('2');
      if (sql.includes('classroom_rentals r') && sql.includes('end_date BETWEEN')) return ROW('4');
      return ROW('0');
    });
    const service = createService('manageAll', query);
    const tasks = await service.getPendingTasks(
      context([
        'student:view',
        'class:view',
        'occupancy:view',
        'bill:view',
        'rental:view',
      ]),
    );
    const byKey = Object.fromEntries(tasks.map((task) => [task.key, task]));
    expect(byKey.students_without_class?.count).toBe(3);
    expect(byKey.students_without_checkin?.count).toBe(5);
    expect(byKey.occupancies_without_bill?.count).toBe(8);
    expect(byKey.rentals_without_contract?.count).toBe(2);
    expect(byKey.rentals_ending_soon?.count).toBe(4);
    expect(byKey.students_without_class?.restricted).toBeUndefined();
  });

  it('teacher 范围只统计本人班级学生，且未分班任务标记 restricted', async () => {
    const query = jest.fn().mockImplementation(async (sql: string, params: unknown[]) => {
      expect(sql).toContain('cs.class_id IN');
      expect(params).toEqual([7]);
      if (sql.includes('LEFT JOIN occupancies o')) return ROW('2');
      if (sql.includes('FROM occupancies o')) return ROW('1');
      return ROW('0');
    });
    const service = createService('teacher', query);
    const tasks = await service.getPendingTasks(
      context(['student:view', 'occupancy:view', 'bill:view']),
    );
    const byKey = Object.fromEntries(tasks.map((task) => [task.key, task]));
    expect(byKey.students_without_class).toMatchObject({ count: null, restricted: true });
    expect(byKey.students_without_checkin?.count).toBe(2);
    expect(byKey.occupancies_without_bill?.count).toBe(1);
    expect(byKey.rentals_without_contract).toBeUndefined();
  });

  it('workflowKey 只返回该闭环的待办', async () => {
    const query = jest.fn().mockResolvedValue(ROW('0'));
    const service = createService('manageAll', query);
    const tasks = await service.getPendingTasks(
      context([], true),
      'classroom_rental',
    );
    expect(tasks.map((task) => task.key)).toEqual([
      'rentals_without_contract',
      'rentals_ending_soon',
    ]);
  });

  it('缺少权限的任务不返回', async () => {
    const query = jest.fn().mockResolvedValue(ROW('0'));
    const service = createService('manageAll', query);
    const tasks = await service.getPendingTasks(context(['student:view']));
    expect(tasks.map((task) => task.key)).toEqual(['students_without_class']);
  });
});
