<?php

declare(strict_types=1);

namespace App\Business\DataImport\Support;

use App\Business\DataImport\Enums\DataImportRowStatus;
use App\Models\DberpCustomerCategory;
use App\Models\DberpDataImportBatchRow;
use App\Models\DberpEmployee;
use App\Models\DberpProduct;
use App\Models\DberpProductBrand;
use App\Models\DberpProductCategory;
use App\Models\DberpProductSku;
use App\Models\DberpRegion;
use App\Models\DberpSupplierCategory;
use App\Models\DberpUnit;
use App\Models\DberpWarehouse;
use Illuminate\Database\Eloquent\Model;

final class DataImportReferenceResolver
{
    /**
     * @param  list<string>  $employeeCodes
     * @return array<string, int>
     */
    public function employeeIdsByCodes(array $employeeCodes): array
    {
        if ($employeeCodes === []) {
            return [];
        }

        return DberpEmployee::query()
            ->whereIn('code', $employeeCodes)
            ->pluck('id', 'code')
            ->mapWithKeys(static fn ($id, $code): array => [(string) $code => (int) $id])
            ->all();
    }

    /**
     * @param  list<string>  $regionCodes
     * @return array<string, DberpRegion>
     */
    public function regionsByCodes(array $regionCodes): array
    {
        if ($regionCodes === []) {
            return [];
        }

        $regions = DberpRegion::query()
            ->whereIn('region_code', $regionCodes)
            ->get();

        $map = [];

        foreach ($regions as $region) {
            $map[(string) $region->region_code] = $region;
        }

        return $map;
    }

    /**
     * @param  list<string>  $categoryCodes
     * @return array<string, int>
     */
    public function customerCategoryIdsByCodes(array $categoryCodes): array
    {
        return $this->categoryIdsByCodes(DberpCustomerCategory::class, $categoryCodes);
    }

    /**
     * @param  list<string>  $categoryCodes
     * @return array<string, int>
     */
    public function supplierCategoryIdsByCodes(array $categoryCodes): array
    {
        return $this->categoryIdsByCodes(DberpSupplierCategory::class, $categoryCodes);
    }

    /**
     * @param  class-string<Model>  $modelClass
     * @param  list<string>  $categoryCodes
     * @return array<string, int>
     */
    private function categoryIdsByCodes(string $modelClass, array $categoryCodes): array
    {
        if ($categoryCodes === []) {
            return [];
        }

        return $modelClass::query()
            ->whereIn('code', $categoryCodes)
            ->whereNotNull('code')
            ->pluck('id', 'code')
            ->mapWithKeys(static fn ($id, $code): array => [(string) $code => (int) $id])
            ->all();
    }

    /**
     * @param  list<string>  $unitCodes
     * @return array<string, int>
     */
    public function enabledUnitIdsByCodes(array $unitCodes): array
    {
        if ($unitCodes === []) {
            return [];
        }

        return DberpUnit::query()
            ->whereIn('code', $unitCodes)
            ->where('is_enabled', true)
            ->pluck('id', 'code')
            ->mapWithKeys(static fn ($id, $code): array => [(string) $code => (int) $id])
            ->all();
    }

    /**
     * @param  list<string>  $categoryCodes
     * @return array<string, int>
     */
    public function enabledProductCategoryIdsByCodes(array $categoryCodes): array
    {
        if ($categoryCodes === []) {
            return [];
        }

        return DberpProductCategory::query()
            ->whereIn('code', $categoryCodes)
            ->where('is_enabled', true)
            ->pluck('id', 'code')
            ->mapWithKeys(static fn ($id, $code): array => [(string) $code => (int) $id])
            ->all();
    }

    /**
     * @param  list<string>  $brandCodes
     * @return array<string, int>
     */
    public function enabledProductBrandIdsByCodes(array $brandCodes): array
    {
        if ($brandCodes === []) {
            return [];
        }

        return DberpProductBrand::query()
            ->whereIn('brand_code', $brandCodes)
            ->where('is_enabled', true)
            ->pluck('id', 'brand_code')
            ->mapWithKeys(static fn ($id, $code): array => [(string) $code => (int) $id])
            ->all();
    }

    /**
     * @param  list<string>  $barcodes
     * @return list<string>
     */
    public function existingProductBarcodes(array $barcodes): array
    {
        if ($barcodes === []) {
            return [];
        }

        $fromProducts = DberpProduct::query()
            ->whereIn('barcode', $barcodes)
            ->pluck('barcode')
            ->map(static fn ($value): string => (string) $value)
            ->all();

        $fromSkus = DberpProductSku::query()
            ->whereIn('barcode', $barcodes)
            ->pluck('barcode')
            ->map(static fn ($value): string => (string) $value)
            ->all();

        return array_values(array_unique(array_merge($fromProducts, $fromSkus)));
    }

    /**
     * @param  list<string>  $unitCodes
     * @return list<string>
     */
    public function existingUnitCodes(array $unitCodes): array
    {
        if ($unitCodes === []) {
            return [];
        }

        return DberpUnit::query()
            ->whereIn('code', $unitCodes)
            ->pluck('code')
            ->map(static fn ($value): string => (string) $value)
            ->all();
    }

    /**
     * @param  list<string>  $categoryCodes
     * @return list<string>
     */
    public function existingProductCategoryCodes(array $categoryCodes): array
    {
        if ($categoryCodes === []) {
            return [];
        }

        return DberpProductCategory::query()
            ->whereIn('code', $categoryCodes)
            ->whereNotNull('code')
            ->pluck('code')
            ->map(static fn ($value): string => (string) $value)
            ->all();
    }

    /**
     * @param  list<string>  $brandCodes
     * @return list<string>
     */
    public function existingProductBrandCodes(array $brandCodes): array
    {
        if ($brandCodes === []) {
            return [];
        }

        return DberpProductBrand::query()
            ->whereIn('brand_code', $brandCodes)
            ->pluck('brand_code')
            ->map(static fn ($value): string => (string) $value)
            ->all();
    }

    /**
     * @param  list<string>  $productCodes
     * @return array<string, DberpProduct>
     */
    public function enabledInventoryProductsByCodes(array $productCodes): array
    {
        if ($productCodes === []) {
            return [];
        }

        $products = DberpProduct::query()
            ->whereIn('code', $productCodes)
            ->where('is_enabled', true)
            ->where('manages_inventory', true)
            ->get();

        $map = [];

        foreach ($products as $product) {
            $map[(string) $product->code] = $product;
        }

        return $map;
    }

    /**
     * @param  list<string>  $warehouseCodes
     * @return array<string, DberpWarehouse>
     */
    public function enabledWarehousesByCodes(array $warehouseCodes): array
    {
        if ($warehouseCodes === []) {
            return [];
        }

        $warehouses = DberpWarehouse::query()
            ->whereIn('code', $warehouseCodes)
            ->where('is_enabled', true)
            ->get();

        $map = [];

        foreach ($warehouses as $warehouse) {
            $code = DataImportValueParser::trimmed($warehouse->code);

            if ($code !== null) {
                $map[$code] = $warehouse;
            }
        }

        return $map;
    }

    /**
     * @param  list<string>  $productCodes
     * @return list<string>
     */
    public function existingProductCodes(array $productCodes): array
    {
        if ($productCodes === []) {
            return [];
        }

        return DberpProduct::query()
            ->whereIn('code', $productCodes)
            ->pluck('code')
            ->map(static fn ($value): string => (string) $value)
            ->all();
    }

    /**
     * @param  list<string>  $warehouseCodes
     * @return list<string>
     */
    public function existingWarehouseCodes(array $warehouseCodes): array
    {
        if ($warehouseCodes === []) {
            return [];
        }

        return DberpWarehouse::query()
            ->whereIn('code', $warehouseCodes)
            ->whereNotNull('code')
            ->pluck('code')
            ->map(static fn ($value): string => (string) $value)
            ->all();
    }

    public function enabledSkuForProductAndCode(DberpProduct $product, string $skuCode): ?DberpProductSku
    {
        $sku = DberpProductSku::query()
            ->where('product_id', $product->getKey())
            ->where('sku_code', $skuCode)
            ->where('is_enabled', true)
            ->first();

        return $sku instanceof DberpProductSku ? $sku : null;
    }

    /**
     * @param  list<string>  $externalKeys
     * @return list<string>
     */
    public function importedOpeningStockExternalKeys(array $externalKeys): array
    {
        if ($externalKeys === []) {
            return [];
        }

        return DberpDataImportBatchRow::query()
            ->whereIn('external_key', $externalKeys)
            ->where('status', DataImportRowStatus::Imported)
            ->whereNotNull('created_record_id')
            ->pluck('external_key')
            ->map(static fn ($value): string => (string) $value)
            ->all();
    }
}
