<?php

namespace App\Services;

use App\Models\Cattle;
use App\Models\CattleLocationMovement;
use App\Models\FatteningLot;
use Carbon\Carbon;
use InvalidArgumentException;

class CattleLocationMovementService
{
    public function __construct(
        private readonly PastureService $pastureService,
    ) {}

    /**
     * @return array{type: string, label: string|null, fattening_lot_id: int|null}
     */
    public function currentLocation(Cattle $cattle): array
    {
        $cattle->loadMissing('activeFatteningLotAssignment.fatteningLot');

        $lot = $cattle->activeFatteningLotAssignment?->fatteningLot;
        if ($lot) {
            return [
                'type' => CattleLocationMovement::TYPE_FATTENING,
                'label' => $lot->name,
                'fattening_lot_id' => $lot->id,
            ];
        }

        if ($cattle->pasture_location) {
            return [
                'type' => CattleLocationMovement::TYPE_PASTURE,
                'label' => $cattle->pasture_location,
                'fattening_lot_id' => null,
            ];
        }

        return [
            'type' => CattleLocationMovement::TYPE_UNASSIGNED,
            'label' => null,
            'fattening_lot_id' => null,
        ];
    }

    public function moveToPasture(
        Cattle $cattle,
        ?int $pastureId,
        ?string $pastureLocation,
        Carbon $movedAt,
        ?int $userId = null,
        ?string $notes = null,
    ): CattleLocationMovement {
        if (! $cattle->isActive()) {
            throw new InvalidArgumentException('Solo se pueden mover animales activos.');
        }

        if ($cattle->activeFatteningAssignment()) {
            throw new InvalidArgumentException('El animal está en engorda; cierre o retire del lote antes de cambiar corral/pasto.');
        }

        $from = $this->currentLocation($cattle);
        $this->pastureService->applyToCattle($cattle, $pastureId, $pastureLocation);
        $cattle->save();

        $to = $this->currentLocation($cattle->fresh());
        $toLabel = $to['label'];
        $toType = $to['type'];

        if ($from['type'] === $toType && $from['label'] === $toLabel) {
            throw new InvalidArgumentException('El animal ya está en esa ubicación.');
        }

        return $this->record(
            $cattle,
            $from,
            ['type' => $toType, 'label' => $toLabel, 'fattening_lot_id' => null],
            CattleLocationMovement::REASON_MANUAL_PASTURE,
            $movedAt,
            $userId,
            $notes,
        );
    }

    public function recordFatteningEntry(Cattle $cattle, FatteningLot $lot, Carbon $movedAt, ?int $userId = null): CattleLocationMovement
    {
        $from = $this->currentLocation($cattle);

        return $this->record(
            $cattle,
            $from,
            [
                'type' => CattleLocationMovement::TYPE_FATTENING,
                'label' => $lot->name,
                'fattening_lot_id' => $lot->id,
            ],
            CattleLocationMovement::REASON_FATTENING_ENTRY,
            $movedAt,
            $userId,
        );
    }

    public function recordFatteningExit(
        Cattle $cattle,
        FatteningLot $lot,
        Carbon $movedAt,
        ?int $userId = null,
        ?string $notes = null,
    ): CattleLocationMovement {
        $cattle->refresh();
        $to = $cattle->pasture_location
            ? [
                'type' => CattleLocationMovement::TYPE_PASTURE,
                'label' => $cattle->pasture_location,
                'fattening_lot_id' => null,
            ]
            : [
                'type' => CattleLocationMovement::TYPE_UNASSIGNED,
                'label' => null,
                'fattening_lot_id' => null,
            ];

        return $this->record(
            $cattle,
            [
                'type' => CattleLocationMovement::TYPE_FATTENING,
                'label' => $lot->name,
                'fattening_lot_id' => $lot->id,
            ],
            $to,
            CattleLocationMovement::REASON_FATTENING_EXIT,
            $movedAt,
            $userId,
            $notes,
        );
    }

    public function recordInitialPasture(Cattle $cattle, Carbon $movedAt, ?int $userId = null): ?CattleLocationMovement
    {
        if (! $cattle->pasture_location) {
            return null;
        }

        return $this->record(
            $cattle,
            ['type' => CattleLocationMovement::TYPE_UNASSIGNED, 'label' => null, 'fattening_lot_id' => null],
            [
                'type' => CattleLocationMovement::TYPE_PASTURE,
                'label' => $cattle->pasture_location,
                'fattening_lot_id' => null,
            ],
            CattleLocationMovement::REASON_CALVING,
            $movedAt,
            $userId,
        );
    }

    /**
     * @param  array{type: string, label: string|null, fattening_lot_id?: int|null}  $from
     * @param  array{type: string, label: string|null, fattening_lot_id?: int|null}  $to
     */
    public function record(
        Cattle $cattle,
        array $from,
        array $to,
        string $reason,
        Carbon $movedAt,
        ?int $userId = null,
        ?string $notes = null,
    ): CattleLocationMovement {
        return CattleLocationMovement::create([
            'cattle_id' => $cattle->id,
            'reason' => $reason,
            'from_type' => $from['type'],
            'from_label' => $from['label'],
            'to_type' => $to['type'],
            'to_label' => $to['label'],
            'fattening_lot_id' => $to['fattening_lot_id'] ?? $from['fattening_lot_id'] ?? null,
            'moved_at' => $movedAt,
            'notes' => $notes,
            'user_id' => $userId,
        ]);
    }

    public function labelForType(?string $type): string
    {
        return match ($type) {
            CattleLocationMovement::TYPE_FATTENING => 'Engorda',
            CattleLocationMovement::TYPE_PASTURE => 'Corral/pasto',
            CattleLocationMovement::TYPE_UNASSIGNED => 'Sin ubicación',
            default => '—',
        };
    }

    public function labelForReason(string $reason): string
    {
        return match ($reason) {
            CattleLocationMovement::REASON_MANUAL_PASTURE => 'Cambio de corral',
            CattleLocationMovement::REASON_FATTENING_ENTRY => 'Entrada a engorda',
            CattleLocationMovement::REASON_FATTENING_EXIT => 'Salida de engorda',
            CattleLocationMovement::REASON_DEACTIVATION => 'Baja',
            CattleLocationMovement::REASON_CALVING => 'Parto / alta',
            default => $reason,
        };
    }
}
