import { BadRequestException, ForbiddenException, NotFoundException } from '@nestjs/common';
import { ReceiptAliasService } from './receipt-alias.service';

describe('ReceiptAliasService', () => {
  const prismaMock = {
    receiptAlias: {
      findMany: jest.fn(),
      findFirst: jest.fn(),
      findUnique: jest.fn(),
      create: jest.fn(),
      update: jest.fn(),
      delete: jest.fn(),
    },
  };

  const service = new ReceiptAliasService(prismaMock as any);

  beforeEach(() => {
    jest.clearAllMocks();
  });

  it('normaliserar alias före upsert', async () => {
    prismaMock.receiptAlias.findFirst.mockResolvedValue(null);
    prismaMock.receiptAlias.create.mockResolvedValue({ id: 1 });

    await service.upsert(
      { receiptName: '  ARLA   MJOLK 1L  ', productId: 7 },
      10,
      'user',
    );

    expect(prismaMock.receiptAlias.findFirst).toHaveBeenCalledWith({
      where: { receiptName: 'arla mjolk 1l', ownerId: 10, isGlobal: false },
    });
    expect(prismaMock.receiptAlias.create).toHaveBeenCalledWith({
      data: { receiptName: 'arla mjolk 1l', productId: 7, ownerId: 10, isGlobal: false },
    });
  });

  it('blockerar brusalias', async () => {
    await expect(
      service.upsert({ receiptName: '  Rabatt kupong ', productId: 7 }, 10, 'user'),
    ).rejects.toBeInstanceOf(BadRequestException);
  });

  it('tillåter inte vanlig användare att skapa globalt alias', async () => {
    await expect(
      service.upsert({ receiptName: 'mjolk 1l', productId: 7, isGlobal: true }, 10, 'user'),
    ).rejects.toBeInstanceOf(ForbiddenException);
  });

  it('tillåter inte vanlig användare att ta bort globalt alias', async () => {
    prismaMock.receiptAlias.findUnique.mockResolvedValue({
      id: 9,
      ownerId: null,
      isGlobal: true,
    });

    await expect(service.remove(9, 10, 'user')).rejects.toBeInstanceOf(ForbiddenException);
  });

  it('returnerar not found vid borttagning av okänt alias', async () => {
    prismaMock.receiptAlias.findUnique.mockResolvedValue(null);

    await expect(service.remove(99, 10, 'admin')).rejects.toBeInstanceOf(NotFoundException);
  });

  it('uppdaterar alias säkert via update()', async () => {
    prismaMock.receiptAlias.findUnique.mockResolvedValue({
      id: 12,
      receiptName: 'mjolk 1l',
      productId: 7,
      ownerId: 10,
      isGlobal: false,
    });
    prismaMock.receiptAlias.findFirst.mockResolvedValue(null);
    prismaMock.receiptAlias.update.mockResolvedValue({ id: 12 });

    await service.update(
      12,
      { receiptName: '  ARLA   MJOLK 1L  ', productId: 8 },
      10,
      'user',
    );

    expect(prismaMock.receiptAlias.update).toHaveBeenCalledWith({
      where: { id: 12 },
      data: {
        receiptName: 'arla mjolk 1l',
        productId: 8,
        isGlobal: false,
        ownerId: 10,
      },
    });
  });

  it('tillåter admin att ändra alias från privat till global', async () => {
    prismaMock.receiptAlias.findUnique.mockResolvedValue({
      id: 12,
      receiptName: 'mjolk 1l',
      productId: 7,
      ownerId: 10,
      isGlobal: false,
    });
    prismaMock.receiptAlias.findFirst.mockResolvedValue(null);
    prismaMock.receiptAlias.update.mockResolvedValue({ id: 12, isGlobal: true, ownerId: null });

    await service.update(12, { isGlobal: true }, 1, 'admin');

    expect(prismaMock.receiptAlias.findFirst).toHaveBeenCalledWith({
      where: { receiptName: 'mjolk 1l', isGlobal: true },
    });
    expect(prismaMock.receiptAlias.update).toHaveBeenCalledWith({
      where: { id: 12 },
      data: {
        receiptName: 'mjolk 1l',
        productId: 7,
        isGlobal: true,
        ownerId: null,
      },
    });
  });

  it('blockerar vanlig användare från att ändra alias till globalt', async () => {
    prismaMock.receiptAlias.findUnique.mockResolvedValue({
      id: 12,
      receiptName: 'mjolk 1l',
      productId: 7,
      ownerId: 10,
      isGlobal: false,
    });

    await expect(service.update(12, { isGlobal: true }, 10, 'user')).rejects.toBeInstanceOf(
      ForbiddenException,
    );
  });

  it('blockerar global till privat när alias saknar owner', async () => {
    prismaMock.receiptAlias.findUnique.mockResolvedValue({
      id: 12,
      receiptName: 'mjolk 1l',
      productId: 7,
      ownerId: null,
      isGlobal: true,
    });

    await expect(service.update(12, { isGlobal: false }, 1, 'admin')).rejects.toBeInstanceOf(
      BadRequestException,
    );
  });

  it('blockerar update när aliasnamn krockar i samma scope', async () => {
    prismaMock.receiptAlias.findUnique.mockResolvedValue({
      id: 12,
      receiptName: 'mjolk 1l',
      productId: 7,
      ownerId: 10,
      isGlobal: false,
    });
    prismaMock.receiptAlias.findFirst.mockResolvedValue({
      id: 99,
      receiptName: 'arla mjolk 1l',
      ownerId: 10,
      isGlobal: false,
    });

    await expect(
      service.update(12, { receiptName: 'ARLA MJOLK 1L' }, 10, 'user'),
    ).rejects.toBeInstanceOf(BadRequestException);
  });

  it('blockerar update för obehörig användare', async () => {
    prismaMock.receiptAlias.findUnique.mockResolvedValue({
      id: 12,
      receiptName: 'mjolk 1l',
      productId: 7,
      ownerId: null,
      isGlobal: true,
    });

    await expect(
      service.update(12, { receiptName: 'mjolk 1 liter' }, 10, 'user'),
    ).rejects.toBeInstanceOf(ForbiddenException);
  });
});