import { ConflictException, Injectable, NotFoundException } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { CreatePantryItemDto } from './dto/create-pantry-item.dto';
import { CreateInventoryDto } from '../inventory/dto/create-inventory.dto';
import { Prisma } from '@prisma/client';
import { UpdatePantryItemDto } from './dto/update-pantry-item.dto';

type PantryQuery = {
  userId?: number;
};

@Injectable()
export class PantryService {
  constructor(private readonly prisma: PrismaService) {}

  private readonly productWithCategoryInclude = {
    include: {
      categoryRef: {
        include: {
          parent: {
            include: {
              parent: true,
            },
          },
        },
      },
    },
  };

  private async ensureProductExistsAny(productId: number) {
    const product = await this.prisma.product.findUnique({ where: { id: productId } });
    if (!product) {
      throw new NotFoundException('Product not found');
    }
    return product;
  }

  private async ensureUserExists(userId: number) {
    const user = await this.prisma.user.findUnique({
      where: { id: userId },
      select: { id: true },
    });
    if (!user) {
      throw new NotFoundException(`User with id ${userId} not found`);
    }
  }

  findAll(userId: number) {
    return this.prisma.pantryItem.findMany({
      where: { userId },
      include: {
        product: this.productWithCategoryInclude,
      },
      orderBy: {
        product: { name: 'asc' },
      },
    });
  }

  findAllAdmin(query?: PantryQuery) {
    return this.prisma.pantryItem.findMany({
      where: typeof query?.userId === 'number' ? { userId: query.userId } : {},
      include: {
        user: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        product: {
          ...this.productWithCategoryInclude,
        },
      },
      orderBy: [
        { user: { username: 'asc' } },
        { product: { name: 'asc' } },
      ],
    });
  }

  async create(userId: number, data: CreatePantryItemDto) {
    const existing = await this.prisma.pantryItem.findUnique({
      where: {
        userId_productId: {
          userId,
          productId: data.productId,
        },
      },
    });

    if (existing) {
      throw new ConflictException('Produkten finns redan i baslagret');
    }

    return this.prisma.pantryItem.create({
      data: {
        userId,
        productId: data.productId,
        location: data.location?.trim() || null,
      },
      include: { product: this.productWithCategoryInclude },
    });
  }

  async createAdmin(adminUserId: number, data: CreatePantryItemDto, targetUserId?: number) {
    const effectiveUserId = typeof targetUserId === 'number' ? targetUserId : adminUserId;
    await this.ensureUserExists(effectiveUserId);
    await this.ensureProductExistsAny(data.productId);

    const existing = await this.prisma.pantryItem.findUnique({
      where: {
        userId_productId: {
          userId: effectiveUserId,
          productId: data.productId,
        },
      },
    });
    if (existing) {
      throw new ConflictException('Produkten finns redan i baslagret');
    }

    return this.prisma.pantryItem.create({
      data: {
        userId: effectiveUserId,
        productId: data.productId,
        location: data.location?.trim() || null,
      },
      include: {
        user: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        product: this.productWithCategoryInclude,
      },
    });
  }

  async updateAdmin(id: number, data: UpdatePantryItemDto) {
    const existing = await this.prisma.pantryItem.findUnique({ where: { id } });
    if (!existing) {
      throw new NotFoundException(`PantryItem med id ${id} hittades inte`);
    }

    if (typeof data.productId === 'number') {
      await this.ensureProductExistsAny(data.productId);
      const duplicate = await this.prisma.pantryItem.findUnique({
        where: {
          userId_productId: {
            userId: existing.userId,
            productId: data.productId,
          },
        },
      });
      if (duplicate && duplicate.id !== id) {
        throw new ConflictException('Produkten finns redan i baslagret');
      }
    }

    return this.prisma.pantryItem.update({
      where: { id },
      data: {
        ...(typeof data.productId === 'number'
            ? { product: { connect: { id: data.productId } } }
            : {}),
        ...(typeof data.location === 'string'
            ? { location: data.location.trim() || null }
            : {}),
      },
      include: {
        user: {
          select: {
            id: true,
            username: true,
            email: true,
          },
        },
        product: this.productWithCategoryInclude,
      },
    });
  }

  async remove(userId: number, id: number) {
    const item = await this.prisma.pantryItem.findFirst({
      where: { id, userId },
    });

    if (!item) {
      throw new NotFoundException(`PantryItem med id ${id} hittades inte`);
    }

    return this.prisma.pantryItem.delete({ where: { id } });
  }

  async removeAdmin(id: number) {
    const item = await this.prisma.pantryItem.findUnique({ where: { id } });

    if (!item) {
      throw new NotFoundException(`PantryItem med id ${id} hittades inte`);
    }

    return this.prisma.pantryItem.delete({ where: { id } });
  }

  private async movePantryItemToInventoryCore(
    item: { id: number; userId: number; productId: number; location: string | null },
    data: CreateInventoryDto,
  ) {
    return this.prisma.$transaction(async (tx) => {
      const inventoryItem = await tx.inventoryItem.create({
        data: {
          userId: item.userId,
          productId: item.productId,
          quantity: new Prisma.Decimal(data.quantity),
          unit: data.unit.trim(),
          location: data.location?.trim() || item.location || undefined,
          purchaseDate: data.purchaseDate ? new Date(data.purchaseDate) : undefined,
          bestBeforeDate: data.bestBeforeDate ? new Date(data.bestBeforeDate) : undefined,
          brand: data.brand?.trim() || undefined,
          origin: data.origin?.trim() || undefined,
          receiptName: data.receiptName?.trim() || undefined,
          opened: data.opened ?? false,
          suitableFor: data.suitableFor?.trim() || undefined,
          comment: data.comment?.trim() || undefined,
        },
        include: { product: this.productWithCategoryInclude },
      });

      await tx.pantryItem.delete({ where: { id: item.id } });

      return inventoryItem;
    });
  }

  async moveToInventory(userId: number, pantryItemId: number, data: CreateInventoryDto) {
    const item = await this.prisma.pantryItem.findFirst({
      where: { id: pantryItemId, userId },
    });

    if (!item) {
      throw new NotFoundException(`PantryItem med id ${pantryItemId} hittades inte`);
    }

    return this.movePantryItemToInventoryCore(item, data);
  }

  async moveToInventoryAdmin(pantryItemId: number, data: CreateInventoryDto) {
    const item = await this.prisma.pantryItem.findUnique({
      where: { id: pantryItemId },
    });

    if (!item) {
      throw new NotFoundException(`PantryItem med id ${pantryItemId} hittades inte`);
    }

    return this.movePantryItemToInventoryCore(item, data);
  }
}
