<?php

namespace App\Services;

use App\Models\Item;
use App\Models\Receiving;
use App\Models\ReceivingItem;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;

class ReceivingService
{
    protected InventoryService $inventoryService;
    protected AuditTrailService $auditTrail;
    protected DocumentNumberService $documentNumberService;
    protected SyncOutboxService $syncOutboxService;

    public function __construct(
        InventoryService $inventoryService,
        AuditTrailService $auditTrail,
        DocumentNumberService $documentNumberService,
        SyncOutboxService $syncOutboxService
    )
    {
        $this->inventoryService = $inventoryService;
        $this->auditTrail = $auditTrail;
        $this->documentNumberService = $documentNumberService;
        $this->syncOutboxService = $syncOutboxService;
    }

    public function getAll()
    {
        $query = Receiving::with('items.item')->useFilters()->dynamicPaginate();

        return $query;
    }

    public function getById($id)
    {
        return Receiving::with('items.item')->find($id);
    }

    public function getItemsByIds(array $ids)
    {
        return Item::with(['category', 'uom'])
            ->whereIn('id', $ids)
            ->useFilters()
            ->dynamicPaginate();
    }

    public function create($data)
    {
        $userId = Auth::user()->id;
        $farmId = Auth::user()->farm_id;

        $documentNo = $this->documentNumberService->generate(
            prefix: 'RCV',
            table: 'receivings',
        );

        $receiving = Receiving::create([
            'document_no'    => $documentNo,
            'sync_uuid' => (string) Str::uuid(),
            'origin_server_code' => $this->serverCode(),
            'synced_status' => 'unsynced',
            'receiving_date' => $data['receiving_date'],
            'supplier' => $data['supplier'] ?? null,
            'type' => $data['type'],
            'mode' => $data['mode'],
            'external_reference' => $data['external_reference'] ?? null,
            'remarks' => $data['remarks'] ?? null,
            'user_id' => $userId,
            'farm_id' => $farmId,
        ]);

        foreach ($data['items'] as $item) {
            $itemModel = Item::findOrFail($item['item_id']);

            $receivingItem = ReceivingItem::create([
                'sync_uuid' => (string) Str::uuid(),
                'origin_server_code' => $this->serverCode(),
                'synced_status' => 'unsynced',
                'receiving_id' => $receiving->id,
                'item_id' => $item['item_id'],
                'item_name_snapshot' => $item['item_name'] ?? $itemModel->name,
                'quantity' => $item['quantity'],
                'unit_cost' => $item['unit_cost'] ?? 0,
            ]);

            // 3. Create inventory movement (IN)
            $this->inventoryService->in([
                'farm_id' => $farmId,
                'item_id' => $item['item_id'],
                'quantity' => $item['quantity'],
                'unit_cost' => $item['unit_cost'] ?? 0,
                'reference_type' => 'receiving',
                'reference_id' => $receiving->id,
                'remarks' => $data['remarks'] ?? null,
                'user_id' => $userId,
            ]);
        }

        $this->auditTrail->log(
            __("models.inventory"),
            __("models.receiving"),
            $receiving->id,
            __("models.lc_created"),
            $receiving,
            null,
            $data
        );

        $this->syncOutboxService->queueModel(
            aggregateType: 'receiving',
            action: 'created',
            model: $receiving,
            payload: $this->buildSyncPayload($receiving->fresh('items'))
        );

        return $receiving->load('items');
    }

    public function update($id, $data)
    {
        $userId = Auth::user()->id;
        $farmId = Auth::user()->farm_id;

        $receiving = Receiving::where('id', $id)
            ->where('user_id', $userId)
            ->where('farm_id', $farmId)
            ->first();

        $oldReceiving = $receiving->toArray();

        if (!$receiving->sync_uuid) {
            $receiving->forceFill([
                'sync_uuid' => (string) Str::uuid(),
                'origin_server_code' => $receiving->origin_server_code ?: $this->serverCode(),
            ])->save();
        }

        $receiving->update([
            'receiving_date' => $data['receiving_date'],
            'supplier'       => $data['supplier'] ?? null,
            'type'           => $data['type'],
            'mode'           => $data['mode'],
            'remarks'        => $data['remarks'] ?? null,
        ]);

        foreach ($data['items'] as $item) {
            $itemModel = Item::findOrFail($item['item_id']);

            $existingItem = ReceivingItem::where('receiving_id', $receiving->id)
                ->where('item_id', $item['item_id'])
                ->first();

            $oldQuantity = $existingItem?->quantity ?? 0;
            $oldUnitCost = $existingItem?->unit_cost ?? 0;
            $newQuantity = $item['quantity'];
            $newUnitCost = $item['unit_cost'] ?? 0;

            // Update or create the receiving item
            $receivingItem = ReceivingItem::updateOrCreate(
                [
                    'receiving_id' => $receiving->id,
                    'item_id'      => $item['item_id'],
                ],
                [
                    'item_name_snapshot' => $item['item_name'] ?? $itemModel->name,
                    'quantity'  => $newQuantity,
                    'unit_cost' => $newUnitCost,
                ]
            );

            if (!$receivingItem->sync_uuid) {
                $receivingItem->forceFill([
                    'sync_uuid' => (string) Str::uuid(),
                    'origin_server_code' => $receivingItem->origin_server_code ?: $this->serverCode(),
                    'synced_status' => 'unsynced',
                ])->save();
            }

            if ($existingItem && ($oldQuantity !== $newQuantity || $oldUnitCost !== $newUnitCost)) {
                // Existing item, values changed — update the movement directly
                $this->inventoryService->inUpdate([
                    'farm_id'        => $farmId,
                    'item_id'        => $item['item_id'],
                    'quantity'       => $newQuantity,
                    'unit_cost'      => $newUnitCost,
                    'reference_type' => 'receiving',
                    'reference_id'   => $receiving->id,
                    'remarks'        => $data['remarks'] ?? null,
                ]);
            } elseif (!$existingItem) {
                // New item line — create a fresh movement
                $this->inventoryService->in([
                    'farm_id'        => $farmId,
                    'item_id'        => $item['item_id'],
                    'quantity'       => $newQuantity,
                    'unit_cost'      => $newUnitCost,
                    'reference_type' => 'receiving',
                    'reference_id'   => $receiving->id,
                    'remarks'        => $data['remarks'] ?? null,
                    'user_id'        => $userId,
                ]);
            }
        }

        $this->auditTrail->log(
            __("models.inventory"),
            __("models.receiving"),
            $receiving->id,
            __("models.lc_updated"),
            $receiving,
            $oldReceiving,
            $data
        );

        $receiving->update([
            'synced_status' => 'unsynced',
            'synced_at' => null,
            'last_sync_error' => null,
        ]);

        $this->syncOutboxService->queueModel(
            aggregateType: 'receiving',
            action: 'updated',
            model: $receiving,
            payload: $this->buildSyncPayload($receiving->fresh('items'))
        );

        return $receiving->load('items');
    }

    private function buildSyncPayload(Receiving $receiving): array
    {
        return [
            'sync_uuid' => $receiving->sync_uuid,
            'origin_server_code' => $receiving->origin_server_code,
            'document_no' => $receiving->document_no,
            'farm_id' => $receiving->farm_id,
            'receiving_date' => $receiving->receiving_date,
            'supplier' => $receiving->supplier,
            'type' => $receiving->type,
            'mode' => $receiving->mode,
            'external_reference' => $receiving->external_reference,
            'remarks' => $receiving->remarks,
            'user_id' => $receiving->user_id,
            'items' => $receiving->items->map(fn (ReceivingItem $item) => [
                'sync_uuid' => $item->sync_uuid,
                'origin_server_code' => $item->origin_server_code,
                'item_id' => $item->item_id,
                'item_name' => $item->item_name_snapshot,
                'quantity' => $item->quantity,
                'unit_cost' => $item->unit_cost,
            ])->values()->all(),
        ];
    }

    private function serverCode(): string
    {
        return strtoupper(config('app.server_code') ?: 'LOCAL');
    }
}
