import { Test } from '@nestjs/testing';
import {
  BadRequestException,
  NotFoundException,
} from '@nestjs/common';
import { CategoriesService } from './categories.service';
import { PrismaService } from '../prisma/prisma.service';

describe('CategoriesService', () => {
  let service: CategoriesService;
  let prisma: PrismaService;
  const createdNames: string[] = [];

  beforeAll(async () => {
    const moduleRef = await Test.createTestingModule({
      providers: [CategoriesService, PrismaService],
    }).compile();
    service = moduleRef.get(CategoriesService);
    prisma = moduleRef.get(PrismaService);
    await prisma.onModuleInit();
  });

  afterAll(async () => {
    if (createdNames.length) {
      await prisma.category.deleteMany({
        where: { categoryName: { in: createdNames } },
      });
    }
    await prisma.onModuleDestroy();
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  it('creates root + child + grandchild and assembles them into a tree', async () => {
    const stamp = Date.now();
    const rootName = `Root ${stamp}`;
    const childName = `Child ${stamp}`;
    const leafName = `Leaf ${stamp}`;
    createdNames.push(rootName, childName, leafName);

    const root = await service.create({ categoryName: rootName });
    const child = await service.create({
      categoryName: childName,
      parentCategoryId: Number(root.id),
    });
    const leaf = await service.create({
      categoryName: leafName,
      parentCategoryId: Number(child.id),
    });

    const tree = await service.findAll();
    const findNode = (
      list: Array<{ id: string; children: Array<{ id: string }> }>,
      id: string,
    ): { id: string; children: Array<{ id: string }> } | undefined => {
      for (const n of list) {
        if (n.id === id) return n;
        const inner = findNode(n.children as any, id);
        if (inner) return inner;
      }
      return undefined;
    };
    const rootNode = findNode(tree as any, root.id.toString());
    expect(rootNode).toBeDefined();
    const childNode = findNode(rootNode!.children as any, child.id.toString());
    expect(childNode).toBeDefined();
    const leafNode = findNode(childNode!.children as any, leaf.id.toString());
    expect(leafNode).toBeDefined();
  });

  it('rejects deletion when children exist', async () => {
    const stamp = Date.now();
    const parent = await service.create({ categoryName: `Parent ${stamp}` });
    createdNames.push(parent.categoryName);
    const child = await service.create({
      categoryName: `Child of ${stamp}`,
      parentCategoryId: Number(parent.id),
    });
    createdNames.push(child.categoryName);

    await expect(service.remove(parent.id)).rejects.toBeInstanceOf(
      BadRequestException,
    );
  });

  it('throws NotFoundException for unknown id', async () => {
    await expect(service.findOne(BigInt(99999999))).rejects.toBeInstanceOf(
      NotFoundException,
    );
  });

  it('returns a flat list and a single node', async () => {
    const stamp = Date.now();
    const c = await service.create({ categoryName: `Flat ${stamp}` });
    createdNames.push(c.categoryName);
    const flat = await service.findFlat();
    expect(flat.some((row) => row.id === c.id)).toBe(true);
    const single = await service.findOne(c.id);
    expect(single.categoryName).toBe(c.categoryName);
  });
});
