import { Test, TestingModule } from '@nestjs/testing';
import { NotFoundException, ConflictException, BadRequestException } from '@nestjs/common';
import { EnterpriseService } from '../enterprise.service';
import { PrismaService } from '../../../prisma/prisma.service';

describe('EnterpriseService', () => {
  let service: EnterpriseService;
  let prisma: PrismaService;

  const mockPrisma = {
    organization: {
      findFirst: jest.fn(),
      findUnique: jest.fn(),
      findMany: jest.fn(),
      create: jest.fn(),
      update: jest.fn(),
      count: jest.fn(),
    },
    organizationMember: {
      findUnique: jest.fn(),
      findMany: jest.fn(),
      create: jest.fn(),
      delete: jest.fn(),
    },
    courseAssignment: {
      findFirst: jest.fn(),
      findMany: jest.fn(),
      create: jest.fn(),
      delete: jest.fn(),
    },
    course: {
      findUnique: jest.fn(),
    },
    learnRecord: {
      findMany: jest.fn(),
      count: jest.fn(),
    },
    lesson: {
      count: jest.fn(),
    },
    user: {
      findUnique: jest.fn(),
    },
  };

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        EnterpriseService,
        { provide: PrismaService, useValue: mockPrisma },
      ],
    }).compile();

    service = module.get<EnterpriseService>(EnterpriseService);
    prisma = module.get<PrismaService>(PrismaService);
    jest.clearAllMocks();
  });

  describe('createOrganization', () => {
    it('should create organization and add creator as admin', async () => {
      mockPrisma.organization.findFirst.mockResolvedValue(null);
      mockPrisma.organization.create.mockResolvedValue({ id: 1, name: '测试企业', memberCount: 0 });
      mockPrisma.organizationMember.create.mockResolvedValue({});
      mockPrisma.organization.update.mockResolvedValue({});

      const result = await service.createOrganization(1, { name: '测试企业' });

      expect(result).toEqual({ id: 1, name: '测试企业', memberCount: 0 });
      expect(mockPrisma.organization.create).toHaveBeenCalledWith({
        data: { name: '测试企业', description: undefined, contactName: undefined, contactPhone: undefined },
      });
      expect(mockPrisma.organizationMember.create).toHaveBeenCalledWith({
        data: { organizationId: 1, userId: 1, role: 'ADMIN' },
      });
    });

    it('should throw ConflictException if name exists', async () => {
      mockPrisma.organization.findFirst.mockResolvedValue({ id: 1 });

      await expect(service.createOrganization(1, { name: '测试企业' }))
        .rejects.toThrow(ConflictException);
    });
  });

  describe('getOrganization', () => {
    it('should return organization with members and assignments', async () => {
      const mockOrg = {
        id: 1, name: '测试企业',
        _count: { members: 2, assignments: 1 },
        members: [{ id: 1, userId: 1, user: { id: 1, nickname: '用户1' } }],
        assignments: [{ id: 1, courseId: 1, course: { id: 1, title: '课程1' } }],
      };
      mockPrisma.organization.findUnique.mockResolvedValue(mockOrg);

      const result = await service.getOrganization(1);
      expect(result).toEqual(mockOrg);
    });

    it('should throw NotFoundException if not found', async () => {
      mockPrisma.organization.findUnique.mockResolvedValue(null);
      await expect(service.getOrganization(999)).rejects.toThrow(NotFoundException);
    });
  });

  describe('addMember', () => {
    it('should add member to organization', async () => {
      mockPrisma.organization.findUnique.mockResolvedValue({ id: 1 });
      mockPrisma.organizationMember.findUnique
        .mockResolvedValueOnce({ role: 'ADMIN' })  // caller is admin
        .mockResolvedValueOnce(null);               // user not yet member
      mockPrisma.organizationMember.create.mockResolvedValue({});
      mockPrisma.organization.update.mockResolvedValue({});

      const result = await service.addMember(1, 1, 2);
      expect(result).toEqual({ added: true });
    });

    it('should throw ConflictException if already member', async () => {
      mockPrisma.organization.findUnique.mockResolvedValue({ id: 1 });
      mockPrisma.organizationMember.findUnique
        .mockResolvedValueOnce({ role: 'ADMIN' })  // caller is admin
        .mockResolvedValueOnce({ id: 1 });          // user already member

      await expect(service.addMember(1, 1, 2)).rejects.toThrow(ConflictException);
    });
  });

  describe('assignCourse', () => {
    it('should assign course to organization', async () => {
      mockPrisma.organization.findUnique.mockResolvedValue({ id: 1 });
      mockPrisma.course.findUnique.mockResolvedValue({ id: 1 });
      mockPrisma.courseAssignment.findFirst.mockResolvedValue(null);
      mockPrisma.courseAssignment.create.mockResolvedValue({
        id: 1, courseId: 1, course: { id: 1, title: '课程1' },
      });

      const result = await service.assignCourse(1, 1, 1);
      expect(result).toHaveProperty('id');
    });

    it('should throw NotFoundException if org not found', async () => {
      mockPrisma.organization.findUnique.mockResolvedValue(null);
      await expect(service.assignCourse(1, 1, 1)).rejects.toThrow(NotFoundException);
    });
  });

  describe('listOrganizations', () => {
    it('should return paginated organizations', async () => {
      mockPrisma.organization.findMany.mockResolvedValue([{ id: 1, name: '测试企业' }]);
      mockPrisma.organization.count.mockResolvedValue(1);

      const result = await service.listOrganizations(1, 20);
      expect(result.items).toHaveLength(1);
      expect(result.total).toBe(1);
      expect(result.page).toBe(1);
    });
  });
});
