<?php

namespace App\Services;

use App\Http\Resources\InternalGuideResource;
use App\Models\Company;
use App\Models\DocumentOfSale;
use App\Models\InternalGuide;
use App\Models\InternalGuideDetail;
use App\Models\Product;
use App\Models\StockProduct;
use App\Models\Store;
use App\Models\Supplier;
use App\Traits\HasResponse;
use Illuminate\Support\Facades\DB;

class InternalGuideService
{
    use HasResponse;

    public function list($withPagination)
    {
        $internalGuide = InternalGuide::internalGuideFilters()->orderBy('id', 'desc');

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

        $internalGuide = InternalGuideResource::collection($internalGuide->load('store', 'document', 'supplier', 'internalGuideDetail'));

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

    public function register($params)
    {
        DB::beginTransaction();
        try {
            # Verificar llaves foráneas
            $validate = $this->checkFK($params);
            if (!$validate->original['status']) return $validate;

            $internalGuide = InternalGuide::create([
                'correlative'       => $this->generareCorrelative($params['idcompany'], $params['operation'], $params['date']),
                'date'              => $params['date'],
                'operation'         => $params['operation'],
                'idstore'           => $params['idstore'],
                'iddocument'        => $params['iddocument'],
                'folio'             => $params['folio'],
                'idsupplier'        => $params['idsupplier'],
                'idcompany'         => $params['idcompany'],
                'internal_number'   => $params['internal_number'],
                'observation'       => $params['observation']
            ]);

            # Estructura de data para el detalle
            if (isset($params['detail'])) {

                # Revisión de data e inserción en una variable
                $structureInternalGuideDetail = $this->structureInternalGuideDetail($internalGuide->id, $params['detail']);
                if (!$structureInternalGuideDetail->original['status']) return $structureInternalGuideDetail;
                $structureInternalGuideDetail = $structureInternalGuideDetail->original['data']['detail'];

                InternalGuideDetail::insert($structureInternalGuideDetail['insert']);
                $internalGuide->update(['total' => $structureInternalGuideDetail['total']]);

                # Actualizar o crear registro de stock
                foreach ($structureInternalGuideDetail['insert'] as $key => $register) {

                    # Verificar que el código no este siendo usado por otro producto
                    $product = Product::where('code_system', $register['code'])->first();
                    $stock = StockProduct::where('idproduct', $product->id)
                        ->where('idwarehouse', $params['idstore'])->company()->active()->first();

                    if ($stock) {
                        # Actualizo
                        $stock->increment('stock_system', $register['count']);
                        $stock->update(['last_update'   => now()]);
                    } else {
                        # Genero un registro
                        StockProduct::create([
                            'stock_system'      => $register['count'],
                            'registration_date' => now(),
                            'last_update'       => now(),
                            'idproduct'         => $product->id,
                            'idwarehouse'       => $params['idstore'],
                            'idcompany'         => $params['idcompany']
                        ]);
                    }
                }
            }

            DB::commit();
            return $this->successResponse('Guía interna creada satisfactoriamente.', $internalGuide);
        } catch (\Throwable $th) {
            DB::rollBack();
            throw $th;
            return $this->externalError('durante la creación de una guía interna.', $th->getMessage());
        }
    }

    private function generareCorrelative($idCompany, $documentTypeCode, $date)
    {
        $company = Company::where('id', $idCompany)->first();
        $rutCompany = $company->rut;
        switch ($documentTypeCode) {
            case '1':
                $documentType = 500;
                break;

            case '2':
                $documentType = 501;
                break;
            default:
                return $this->errorResponse('El tipo de documento seleccionado es inválido.', 400);
                break;
        }
        # Formato de fecha sin guiones
        $formattedDate = str_replace('-', '', $date);

        # Obtener el último correlativo para la compañía y el tipo de documento
        $lastCorrelative = InternalGuide::company()
            ->where('iddocument', $documentTypeCode)->active()->max('correlative');

        # Extraer el número del último correlativo
        preg_match('/(\d+)$/', $lastCorrelative, $matches);
        $lastNumber = $matches[1] ?? 0;

        # Incrementar el número
        $newNumber = $lastNumber + 1;

        # Formatear el resultado con ceros a la izquierda
        $formattedCorrelative = sprintf('%05d', $newNumber);

        # Construir el correlativo completo
        $fullCorrelative = "{$rutCompany}-{$documentType}-{$formattedDate}-{$formattedCorrelative}";

        return $fullCorrelative;
    }

    private function structureInternalGuideDetail($idInternalGuide, $detail)
    {
        $data = [
            'insert'    => [],
            'total'     => 0
        ];

        foreach ($detail as $value) {

            # Verificar que el código no este siendo usado por otro producto
            $product = Product::where('code_system', $value['code'])
                ->company()->active()->first();

            if (!$product) return $this->errorResponse('El código ' . $value['code'] . ' no se encuentra registrado en su empresa, por favor primero registrelo.', 400);

            switch ($value['discount_type']) {
                case '1':
                    # Porcentaje
                    $discount = $value['count'] * $value['cost'] * ($value['discount_amount'] / 100);
                    $total = $value['count'] * $value['cost'] - $discount;
                    break;

                case '2':
                    # Costo
                    $discount = $value['discount_amount'];
                    $total = $value['count'] * $value['cost'] - $discount;
                    break;

                default:
                    return $this->errorResponse('Tipo de descuento inválido.', 400);
                    break;
            }

            $data['insert'][] = [
                'idinternal_guide'  =>  $idInternalGuide,
                'code'              =>  $value['code'],
                'idproduct'         =>  $product->id,
                'detail'            =>  $value['detail'],
                'count'             =>  $value['count'],
                'cost'              =>  $value['cost'],
                'discount_type'     =>  $value['discount_type'],
                'discount_amount'   =>  $value['discount_amount'],
                'discount'          =>  $discount,
                'total'             =>  $total
            ];

            $data['total'] += $total;
        }
        return $this->successResponse('OK', $data);
    }

    public function delete($id, $params)
    {
        DB::beginTransaction();
        try {
            #Validar registro
            $internalGuide = InternalGuide::activeForID($id)->first();
            if (!$internalGuide) return $this->errorResponse('El registro seleccionado no esta disponible.', 400);

            if (!isset($params['reason']))  return $this->errorResponse('Es necesario especificar una razón de la anulación.', 400);

            $internalGuide = InternalGuide::find($id);
            $internalGuide->update([
                'observation_cancellation'  => $params['reason'],
                'status'    => 2
            ]);

            # Reducir stock añadido al momento de ingresar la guía interna

            $internalGuideDetail = InternalGuideDetail::where('idinternal_guide', $id)
                ->active()->get();

            foreach ($internalGuideDetail as $key => $register) {

                # Eliminar el detalle
                $register->update(['status' => 2]);

                # Buscar el stock
                $stock = StockProduct::where('idproduct', $register->idproduct)->active()->first();

                # Reducir según el detalle del producto
                $stock->decrement('stock_system', $register->count);
            }

            DB::commit();
            return $this->successResponse('Guía de ingreso eliminada satisfactoriamente.', $internalGuide);
        } catch (\Throwable $th) {
            DB::rollBack();
            return $this->externalError('durante la eliminación de una guía de ingreso.', $th->getMessage());
        }
    }

    private function checkFK($params)
    {
        # Verificar almacén
        if (isset($params['idstore'])) {
            $store = Store::activeForID($params['idstore'])->company()->first();
            if (!$store) return $this->errorResponse('El almacén seleccionado es inválido.', 400);
        }

        # Verificar documento
        if (isset($params['iddocument'])) {
            $document = DocumentOfSale::activeForID($params['iddocument'])->first();
            if (!$document) return $this->errorResponse('El documento seleccionado es inválido.', 400);
        }

        # Verificar proveedor
        if (isset($params['idsupplier'])) {
            $supplier = Supplier::activeForID($params['idsupplier'])->first();
            if (!$supplier) return $this->errorResponse('El proveedor seleccionado es inválido.', 400);
        }

        return $this->successResponse('OK');
    }
}
