import type { ProductsRepository } from './products.repository.js';
import type { CreateProductInput, UpdateProductInput, ProductQueryInput } from '@meshitrack/shared';
import { ProductSource } from '@meshitrack/shared';
import { NotFoundError, ConflictError } from '../../common/errors.js';

interface Deps {
  productsRepository: ProductsRepository;
}

export class ProductsService {
  private readonly productsRepository: ProductsRepository;

  public constructor({ productsRepository }: Deps) {
    this.productsRepository = productsRepository;
  }

  public async list(householdId: string, query: ProductQueryInput) {
    return this.productsRepository.findByHousehold(householdId, query);
  }

  public async getById(id: string, householdId: string) {
    const product = await this.productsRepository.findById(id, householdId);
    if (!product) {
      throw new NotFoundError('Product not found');
    }
    return product;
  }

  public async create(data: CreateProductInput, householdId: string, createdBy: string) {
    if (data.barcode) {
      const existing = await this.productsRepository.findByBarcode(householdId, data.barcode);
      if (existing) {
        throw new ConflictError('A product with this barcode already exists in your household');
      }
    }

    const duplicate = await this.productsRepository.findDuplicate(
      householdId,
      data.name,
      data.brand,
    );
    if (duplicate) {
      throw new ConflictError('A product with the same name and brand already exists');
    }

    return this.productsRepository.create({
      ...data,
      householdId,
      createdBy,
      source: data.source ?? ProductSource.MANUAL,
    });
  }

  public async update(id: string, householdId: string, data: UpdateProductInput) {
    await this.getById(id, householdId);

    if (data.barcode) {
      const existing = await this.productsRepository.findByBarcode(householdId, data.barcode);
      if (existing && existing._id.toString() !== id) {
        throw new ConflictError('A product with this barcode already exists in your household');
      }
    }

    if (data.name || data.brand !== undefined) {
      const current = await this.productsRepository.findById(id, householdId);
      const name = data.name ?? current!.name;
      const brand = data.brand !== undefined ? data.brand : (current!.brand ?? undefined);

      const duplicate = await this.productsRepository.findDuplicate(householdId, name, brand, id);
      if (duplicate) {
        throw new ConflictError('A product with the same name and brand already exists');
      }
    }

    const updated = await this.productsRepository.update(id, householdId, data);
    if (!updated) throw new NotFoundError('Product not found');
    return updated;
  }

  public async delete(id: string, householdId: string) {
    await this.getById(id, householdId);
    const deleted = await this.productsRepository.softDelete(id, householdId);
    if (!deleted) throw new NotFoundError('Product not found');
    return deleted;
  }

  public async importProducts(householdId: string, createdBy: string, items: CreateProductInput[]) {
    const imported: number[] = [];
    const skipped: number[] = [];
    const errors: { row: number; message: string }[] = [];

    for (let i = 0; i < items.length; i++) {
      const item = items[i]!;
      try {
        if (item.barcode) {
          const existing = await this.productsRepository.findByBarcode(householdId, item.barcode);
          if (existing) {
            skipped.push(i);
            continue;
          }
        }
        const duplicate = await this.productsRepository.findDuplicate(
          householdId,
          item.name,
          item.brand,
        );
        if (duplicate) {
          skipped.push(i);
          continue;
        }
        imported.push(i);
      } catch {
        errors.push({ row: i + 1, message: 'Validation error' });
      }
    }

    const toCreate = imported.map((i) => ({
      ...items[i]!,
      householdId,
      createdBy,
      source: items[i]!.source ?? ProductSource.IMPORT,
    }));

    if (toCreate.length > 0) {
      await this.productsRepository.bulkCreate(householdId, toCreate);
    }

    return {
      imported: imported.length,
      skipped: skipped.length,
      errors,
    };
  }
}
