import { ForbiddenException, Injectable, NotFoundException } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import { ShoppingListItemResponse } from './dto/shopping-list-item.response';

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

  async listOpen(userId: number): Promise<ShoppingListItemResponse[]> {
    const rows = await this.prisma.shoppingListItem.findMany({
      where: { userId, status: 'open' },
      orderBy: [{ createdAt: 'desc' }],
    });
    return rows.map((row) => this.toResponse(row));
  }

  async updateCheckedStatus(
    userId: number,
    itemId: number,
    checked: boolean,
  ): Promise<ShoppingListItemResponse> {
    const existing = await this.prisma.shoppingListItem.findUnique({ where: { id: itemId } });
    if (!existing) {
      throw new NotFoundException('Inköpsrad hittades inte.');
    }
    if (existing.userId !== userId) {
      throw new ForbiddenException('Du saknar åtkomst till denna inköpsrad.');
    }

    const updated = await this.prisma.shoppingListItem.update({
      where: { id: itemId },
      data: {
        status: checked ? 'checked' : 'open',
        checkedAt: checked ? new Date() : null,
      },
    });

    return this.toResponse(updated);
  }

  async upsertFromFlyerSelections(
    sessionId: number,
    userId: number,
    itemIds?: number[],
  ): Promise<{ created: number; updated: number; processedSelectionIds: number[] }> {
    const selections = await this.prisma.flyerSelection.findMany({
      where: {
        sessionId,
        userId,
        status: 'planned',
        ...(itemIds && itemIds.length > 0 ? { itemId: { in: itemIds } } : {}),
      },
      include: {
        item: {
          select: {
            id: true,
            rawName: true,
            categoryId: true,
            matchedProductId: true,
            priceUnit: true,
          },
        },
      },
    });

    if (selections.length === 0) {
      return { created: 0, updated: 0, processedSelectionIds: [] };
    }

    let created = 0;
    let updated = 0;

    await this.prisma.$transaction(async (tx) => {
      for (const selection of selections) {
        const quantity = selection.plannedQuantity ?? new Prisma.Decimal(1);
        const unit = selection.plannedUnit ?? selection.item.priceUnit ?? 'st';
        const normalizedUnit = unit.trim() || 'st';
        const productId = selection.item.matchedProductId ?? null;

        let existing: { id: number } | null = null;
        if (productId != null) {
          existing = await tx.shoppingListItem.findFirst({
            where: {
              userId,
              status: 'open',
              productId,
              unit: normalizedUnit,
            },
            select: { id: true },
          });
        }

        if (existing) {
          await tx.shoppingListItem.update({
            where: { id: existing.id },
            data: {
              quantity: {
                increment: quantity,
              },
              name: selection.item.rawName,
              categoryId: selection.item.categoryId ?? undefined,
              source: 'flyer',
            },
          });
          updated += 1;
          continue;
        }

        await tx.shoppingListItem.create({
          data: {
            userId,
            name: selection.item.rawName,
            productId,
            categoryId: selection.item.categoryId,
            quantity,
            unit: normalizedUnit,
            source: 'flyer',
            status: 'open',
          },
        });
        created += 1;
      }
    });

    return {
      created,
      updated,
      processedSelectionIds: selections.map((selection) => selection.id),
    };
  }

  private toResponse(row: {
    id: number;
    userId: number;
    name: string;
    productId: number | null;
    categoryId: number | null;
    quantity: Prisma.Decimal | null;
    unit: string | null;
    source: string;
    status: string;
    checkedAt: Date | null;
    createdAt: Date;
    updatedAt: Date;
  }): ShoppingListItemResponse {
    return {
      id: row.id,
      userId: row.userId,
      name: row.name,
      productId: row.productId,
      categoryId: row.categoryId,
      quantity: row.quantity == null ? null : Number(row.quantity),
      unit: row.unit,
      source: row.source,
      status: row.status,
      checkedAt: row.checkedAt?.toISOString() ?? null,
      createdAt: row.createdAt.toISOString(),
      updatedAt: row.updatedAt.toISOString(),
    };
  }
}
