<?php

namespace App\Services;

use App\Http\Resources\ProductsImportResource;
use App\Models\Category;
use App\Models\Company;
use App\Models\DetailPriceListProduct;
use App\Models\PriceList;
use App\Models\Product;
use App\Models\ProductsImport;
use App\Models\StockProduct;
use App\Models\TypeProduct;
use App\Traits\HasResponse;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Tymon\JWTAuth\Facades\JWTAuth;

/**
 * Class ProductsImportService
 * @package App\Services
 */
class ProductsImportService
{
    use HasResponse;

    public function list($withPagination)
    {
        $productsImport = ProductsImport::productFilters()
            ->orderBy('id', 'desc');

        $productsImport = !empty($withPagination)
            ? $productsImport->paginate($withPagination['perPage'], page: $withPagination['page'])
            : $productsImport->get();

        $productsImport = ProductsImportResource::collection($productsImport->load('company', 'cointType', 'category', 'subcategory', 'specificsubcategory', 'type', 'supplier', 'unitmeasure', 'unitMeasureOptional', 'businessarea', 'classification', 'user'));

        return $this->successResponse('Lectura exitosa.', $productsImport);
    }

    public function update($id, $params)
    {
        DB::beginTransaction();
        try {
            $validate = $this->verifyProduct($id);
            if (!$validate->original['status']) return $validate;

            $product = $validate->original['data']['detail'];

            $imageFields = ['main_image', 'optional_image1', 'optional_image2', 'optional_image3', 'optional_image4'];
            foreach ($imageFields as $field) {
                if (isset($params[$field])) $params[$field] = $this->saveImage($product->$field, $params[$field], $params['code'], $params['idcompany']);
            }

            $product = ProductsImport::find($id);
            $product->update($params);

            DB::commit();
            return $this->successResponse("Producto actualizado satisfactoriamente.", $product);
        } catch (\Throwable $th) {
            DB::rollBack();
            return $this->externalError('durante la actualización de un producto importado.', $th->getMessage());
        }
    }

    public function importProduct($id)
    {
        DB::beginTransaction();
        try {
            $validate = $this->verifyProduct($id);
            if (!$validate->original['status']) return $validate;

            $product[] = $validate->original['data']['detail'];
            if($product[0]->status_import == 1) return $this->errorResponse("Este producto ya fue importado", 400);

            $importProduct = $this->validateImportProduct($product);
            if (!$importProduct->original['status']) return $importProduct;

            $arrayProducts = $importProduct->original['data']['detail'];
            $products = $arrayProducts['products'];

            //agregar cantidad de productos a categoria
            $productsCategories = collect($products)->groupBy('idcategory')->map(function ($items, $key){
                return [
                    'idcategory' => $items->first()['idcategory'],
                    'count' => $items->count(),
                ];
            })->values()->all();

            if(sizeof($productsCategories) > 0){
                foreach ($productsCategories as $item) {
                    $category = Category::where('id', $item['idcategory'])->first();
                    $category->quantity_products = $category->quantity_products + ($item['count'] ?? 0);
                    $category->save();
                }
            }

            Product::insert($products);

            if(!empty($arrayProducts['warehouses'])) {
                $warehouses = [];
                foreach ($arrayProducts['warehouses'] as $warehouse) {
                    $product = Product::where('code_system', $warehouse['code_system'])->where('created_at', $warehouse['created_at'])->active()->first();
                    if($product) {
                        $warehouses[] = [
                            'stock_erp'             => $warehouse['stock'],
                            'stock_system'          => $warehouse['stock'],
                            'registration_date'     => $warehouse['created_at'],
                            'last_update'           => $warehouse['created_at'],
                            'idproduct'             => $product->id,
                            'idwarehouse'           => $warehouse['id'],
                            'idcompany'             => $product->idcompany,
                            'created_at'            => $warehouse['created_at'],
                            'updated_at'            => $warehouse['updated_at']
                        ];

                        $product = null;
                    }
                }

                if (!empty($warehouses)) StockProduct::insert($warehouses);
            }

            # Agregamos el producto al listado de precio general de la compañia
            $priceLists = PriceList::where('type', 1)->company()->active()->get();
            foreach ($priceLists as $priceList) {
                $detail = [];
                foreach ($products as $prod) {
                    $product = Product::where('code_system', $prod['code_system'])->where('created_at', $prod['created_at'])->active()->first();

                    $stock = StockProduct::where('idproduct', $product->id)->active()->company()->sum('stock_system');

                    $detail[] = [
                        'idpricelist' => $priceList->id,
                        'price' => $product->price,
                        'cost' => $product->cost,
                        'stock' => $stock,
                        'subtotal' => $product->price * $stock,
                        'discount' => 0,
                        'idproduct' => $product->id,
                        'idcompany' => $product->idcompany,
                        'created_at' => $prod['created_at'],
                        'updated_at' => $prod['created_at']
                    ];
                }

                DetailPriceListProduct::insert($detail);
            }

            DB::commit();
            return $this->successResponse("Producto importado satisfactoriamente.", $products);
        } catch (\Throwable $th) {
            DB::rollBack();
            return $this->externalError('durante la importación de un producto importado.', $th->getMessage());
        }
    }

    public function importMassive($params)
    {
        try {
            $productsImport = ProductsImport::whereIn('id', $params['ids_products'])->where('status_import', 0)->active()->get();

            $validateImportProduct = $this->validateImportProduct($productsImport, false);
            if (!$validateImportProduct->original['status']) return $validateImportProduct;

            $arrayProducts = $validateImportProduct->original['data']['detail'];
            $products = $arrayProducts['products'];

            //agregar cantidad de productos a categoria
            $productsCategories = collect($products)->groupBy('idcategory')->map(function ($items, $key){
                return [
                    'idcategory' => $items->first()['idcategory'],
                    'count' => $items->count(),
                ];
            })->values()->all();

            if(sizeof($productsCategories) > 0){
                foreach ($productsCategories as $item) {
                    $category = Category::where('id', $item['idcategory'])->first();
                    $category->quantity_products = $category->quantity_products + ($item['count'] ?? 0);
                    $category->save();
                }
            }

            Product::insert($products);

            if(!empty($arrayProducts['warehouses'])) {
                $warehouses = [];
                foreach ($arrayProducts['warehouses'] as $warehouse) {
                    $product = Product::where('code_system', $warehouse['code_system'])->where('created_at', $warehouse['created_at'])->active()->first();
                    if($product) {
                        $warehouses[] = [
                            'stock_erp'             => $warehouse['stock'],
                            'stock_system'          => $warehouse['stock'],
                            'registration_date'     => $warehouse['created_at'],
                            'last_update'           => $warehouse['created_at'],
                            'idproduct'             => $product->id,
                            'idwarehouse'           => $warehouse['id'],
                            'idcompany'             => $product->idcompany,
                            'created_at'            => $warehouse['created_at'],
                            'updated_at'            => $warehouse['updated_at']
                        ];

                        $product = null;
                    }
                }

                if (!empty($warehouses)) StockProduct::insert($warehouses);
            }

            # Agregamos el producto al listado de precio general de la compañia
            $priceLists = PriceList::where('type', 1)->company()->active()->get();
            foreach ($priceLists as $priceList) {
                $detail = [];
                foreach ($products as $prod) {
                    $product = Product::where('code_system', $prod['code_system'])->where('created_at', $prod['created_at'])->active()->first();

                    $stock = StockProduct::where('idproduct', $product->id)->active()->company()->sum('stock_system');

                    $detail[] = [
                        'idpricelist' => $priceList->id,
                        'price' => $product->price,
                        'cost' => $product->cost,
                        'stock' => $stock,
                        'subtotal' => $product->price * $stock,
                        'discount' => 0,
                        'idproduct' => $product->id,
                        'idcompany' => $product->idcompany,
                        'created_at' => $prod['created_at'],
                        'updated_at' => $prod['created_at']
                    ];
                }

                DetailPriceListProduct::insert($detail);
            }

            DB::commit();
            return $this->successResponse("Productos importados satisfactoriamente.", $products);
        } catch (\Throwable $th) {
            DB::rollBack();
            return $this->externalError('durante la actualización de los productos importados.', $th->getMessage());
        }
    }

    public function validateUserImport()
    {
        try {
            $validateImport =  ProductsImport::where('created_by', JWTAuth::user()->id)->where('status_import', 0)->active()->first();
            if($validateImport) return $this->successResponse("El usuario tiene productos para importar.");

            return $this->successResponse("El usuario puede importar.");
        } catch (\Throwable $th) {
            return $this->externalError('durante la validación de un usuario para importar.', $th->getMessage());
        }
    }

    public function validateDataProduct($id)
    {
        try {
            $validate = $this->verifyProduct($id);
            if (!$validate->original['status']) return $validate;

            $product = $validate->original['data']['detail'];
            $nulls = [];

            $requiredFields = ['code', 'name', 'idcategory'];
            // $requiredFields = ['code', 'name', 'idcategory', 'idtype', 'idsupplier', 'idunitmeasure'];
            foreach ($requiredFields as $field) {
                if (!isset($product->$field)) $nulls[] = $field;
            }

            // Al menos uno de los campos requeridos está ausente en los datos importados
            if (!empty($nulls)){
                $nullsString = implode(', ', $nulls);
                return $this->errorResponse("Faltan campos requeridos: $nullsString.", 400, $product->id);
            }

            return $this->successResponse("OK");
        } catch (\Throwable $th) {
            return $this->externalError('durante la validación de productos a importar.', $th->getMessage());
        }
    }

    private function saveImage($old_file, $img, $code, $idcompany)
    {
        try {
            $company = Company::activeForID($idcompany)->first();
            $currentDateTime = Carbon::now()->format('Ymd_His');
            $extension = $img->getClientOriginalExtension();
            $name = "main_image_" . JWTAuth::user()->id . "_" . $currentDateTime.$extension;
            $fileImg = "public/pdf/$company->rut/productos/$code";

            if (isset($old_file)) {
                // Si existe, eliminarlo antes de guardar la nueva imagen
                if (Storage::exists($old_file)) Storage::delete($old_file);
            }

            //Guardamos la imagen
            $image = $img->storeAs($fileImg, $name);
            return $this->successResponse('OK', $image);
        } catch (\Throwable $th) {
            return $this->externalError('durante la creación de un producto.', $th->getMessage());
        }
    }

    private function validateImportProduct($productsImport, $individual = true)
    {
        DB::beginTransaction();
        try {
            $products = [];
            $idsNullable = [];
            $changeStatus = [];

            $now = Carbon::now();

            $correlative = $this->generateCode();

            foreach ($productsImport as $productImport) {
                $validateDataProduct = $this->validateDataProduct($productImport->id);
                if (!$validateDataProduct->original['status']){
                    if ($individual) {
                        return $validateDataProduct;
                    } else {
                        $idsNullable[] = $validateDataProduct->original['data']['detail'];
                    }
                } else {
                    $changeStatus[] = $productImport->id;

                    $products['products'][] = [
                        'code_erp'              => $productImport->code,
                        'code_system'           => $correlative[$productImport->idtype],
                        'barcode'               => $productImport->barcode,
                        'name'                  => $productImport->name,
                        'description'           => $productImport->description,
                        'cost'                  => $productImport->cost,
                        'price'                 => $productImport->price,
                        'alternativeprice'      => $productImport->alternativeprice,
                        'margin'                => $productImport->margin,
                        'offerprice'            => $productImport->offerprice,
                        'minimumstock'          => $productImport->minimumstock,
                        'maximumstock'          => $productImport->maximumstock,
                        'main_image'            => $productImport->main_image,
                        'optional_image1'       => $productImport->optional_image1,
                        'optional_image2'       => $productImport->optional_image2,
                        'optional_image3'       => $productImport->optional_image3,
                        'optional_image4'       => $productImport->optional_image4,
                        'idattributes'          => $productImport->idattributes,
                        'idspecificattributes'  => $productImport->idspecificattributes,
                        'iduses'                => $productImport->iduses,
                        'idspecificuses'        => $productImport->idspecificuses,
                        'idcointype'            => $productImport->idcointype,
                        'idcategory'            => $productImport->idcategory,
                        'idsubcategory'         => $productImport->idsubcategory,
                        'idspecificsubcategory' => $productImport->idspecificsubcategory,
                        'idtype'                => $productImport->idtype,
                        'idsupplier'            => $productImport->idsupplier,
                        'idunitmeasure'         => $productImport->idunitmeasure,
                        'idunitmeasureoptional' => $productImport->idunitmeasureoptional,
                        'idbusinessarea'        => $productImport->idbusinessarea,
                        'idclassification'      => $productImport->idclassification,
                        'idcompany'             => $productImport->idcompany,
                        'created_at' => $now,
                        'updated_at' => $now
                    ];

                    if (isset($productImport->idwarehouses)) {
                        foreach ($productImport->idwarehouses as $warehouse) {
                            $products['warehouses'][] = [
                                'code_system' => $correlative[$productImport->idtype],
                                'id' => $warehouse['id'],
                                'stock' => $warehouse['stock'],
                                'created_at' => $now,
                                'updated_at' => $now
                            ];
                        }
                    }

                    $correlative = $this->generateCorrelative($productImport->idtype, $correlative);
                }
            }

            if (!empty($products)) {
                if (!empty($changeStatus)) ProductsImport::whereIn('id', $changeStatus)->update(['status_import' => 1]);

                DB::commit();
                return $this->successResponse("OK", $products);
            }

            if (!empty($idsNullable)) return $this->errorResponse('Los productos ya fueron importados o faltan campos requeridos.', 400);

        } catch (\Throwable $th) {
            DB::rollBack();
            return $this->externalError('durante la importación de producto.', $th->getMessage());
        }
    }

    public function delete($id)
    {
        DB::beginTransaction();
        try {
            # Verificar validez de producto
            $validate = $this->verifyProduct($id);
            if (!$validate->original['status']) return $validate;

            $product = ProductsImport::find($id);
            $product->status = 2;
            $product->save();

            DB::commit();
            return $this->successResponse('Producto eliminado satisfactoriamente.', $product);
        } catch (\Throwable $th) {
            DB::rollBack();
            return $this->externalError('durante la eliminación de un producto.', $th->getMessage());
        }
    }

    private function verifyProduct($idproduct)
    {
        try {
            $product = ProductsImport::activeForID($idproduct)->first();
            if (!$product) return $this->errorResponse('No se encontró el producto importado.', 400);

            return $this->successResponse('OK', $product);
        } catch (\Throwable $th) {
            return $this->externalError('durante la verificación de la data de un producto.', $th->getMessage());
        }
    }

    private function generateCode()
    {
        $products = Product::selectRaw('idtype, MAX(code_system) as max_code_system')->whereNotNull('code_erp')->active()->groupBy('idtype')->get();
        $types = TypeProduct::active()->get();

        $correlative = [];
        foreach ($types as $type) {
            $codeProduct = $products->where('idtype', $type->id)->values();

            if($codeProduct && isset($codeProduct[0]->max_code_system)) {
                // Obtener el número de la parte numérica del código existente
                $counter = (int) substr($codeProduct[0]->max_code_system, -9);
                $code = substr($codeProduct[0]->max_code_system, 0, -9);

                // Sumar 1 al número existente
                $counter += 1;

                // Formatear el nuevo número a 9 dígitos
                $numberFormat = str_pad($counter, 9, '0', STR_PAD_LEFT);

                // Construir el nuevo código
                $correlative[$type->id] = $code  . $numberFormat;
            } else {
                $correlative[$type->id] = "ERP-$type->code" . '000000001';
            }
        }

        return $correlative;
    }

    private function generateCorrelative($type, $correlative)
    {
        // Obtener el número de la parte numérica del código existente
        $counter = (int) substr($correlative[$type], -9);
        $code = substr($correlative[$type], 0, -9);

        $counter += 1;
        $numberFormat = str_pad($counter, 9, '0', STR_PAD_LEFT);
        $correlative[$type] = $code . $numberFormat;

        return $correlative;
    }
}
