<?php

namespace App\Services;

use App\Models\Cattle;
use App\Models\CattleHealthEvent;
use App\Models\FatteningLotAnimal;
use App\Models\MaterialExpense;
use Carbon\Carbon;

class CattleInvestmentTimelineService
{
    public function __construct(
        private readonly RanchAnimalProfitabilityService $profitabilityService,
    ) {}

    /**
     * Línea de tiempo de gastos (alimento prorrateado por fecha de gasto del lote + salud por evento).
     *
     * @return array<string, mixed>
     */
    public function generate(Cattle $cattle): array
    {
        $cattle->loadMissing([
            'fatteningLotAssignments.fatteningLot',
            'healthEvents.materialExpense',
            'saleIncome',
        ]);

        $entries = [];

        foreach ($cattle->fatteningLotAssignments as $assignment) {
            $expenses = MaterialExpense::query()
                ->where('fattening_lot_id', $assignment->fattening_lot_id)
                ->orderBy('expense_date')
                ->get();

            foreach ($expenses as $expense) {
                $date = $expense->expense_date->copy()->startOfDay();
                if (! $this->cattlePresentInLotOnDate($assignment, $date)) {
                    continue;
                }

                $share = $this->allocateExpenseShare($expense, $date);
                if ($share <= 0) {
                    continue;
                }

                $entries[] = [
                    'date' => $date->toDateString(),
                    'category' => 'feed',
                    'amount' => $share,
                    'label' => 'Alimento · '.($assignment->fatteningLot?->name ?? 'Engorda'),
                    'reference_id' => $expense->id,
                ];
            }
        }

        foreach ($cattle->healthEvents as $event) {
            $amount = $this->healthEventCost($event);
            if ($amount <= 0) {
                continue;
            }

            $entries[] = [
                'date' => $event->event_date->toDateString(),
                'category' => 'health',
                'amount' => $amount,
                'label' => 'Salud · '.$event->title,
                'reference_id' => $event->id,
            ];
        }

        if ($cattle->status === Cattle::STATUS_SOLD) {
            $income = $this->incomeAmount($cattle);
            $saleDate = $cattle->sale_date?->toDateString()
                ?? $cattle->deactivated_at?->toDateString();
            if ($income > 0 && $saleDate) {
                $entries[] = [
                    'date' => $saleDate,
                    'category' => 'income',
                    'amount' => $income,
                    'label' => 'Ingreso por venta',
                    'reference_id' => null,
                ];
            }
        }

        usort($entries, fn ($a, $b) => [$a['date'], $a['category']] <=> [$b['date'], $b['category']]);

        $byDate = [];
        foreach ($entries as $entry) {
            $d = $entry['date'];
            if (! isset($byDate[$d])) {
                $byDate[$d] = ['feed' => 0.0, 'health' => 0.0, 'income' => 0.0];
            }
            $byDate[$d][$entry['category']] += (float) $entry['amount'];
        }

        ksort($byDate);

        $series = [];
        $cumFeed = 0.0;
        $cumHealth = 0.0;
        $cumIncome = 0.0;

        foreach ($byDate as $date => $day) {
            $feed = round($day['feed'], 2);
            $health = round($day['health'], 2);
            $income = round($day['income'], 2);
            $dailyInvested = round($feed + $health, 2);

            $cumFeed = round($cumFeed + $feed, 2);
            $cumHealth = round($cumHealth + $health, 2);
            $cumIncome = round($cumIncome + $income, 2);
            $cumInvested = round($cumFeed + $cumHealth, 2);

            $series[] = [
                'date' => $date,
                'feed' => $feed,
                'health' => $health,
                'income' => $income,
                'daily_invested' => $dailyInvested,
                'cumulative_feed' => $cumFeed,
                'cumulative_health' => $cumHealth,
                'cumulative_invested' => $cumInvested,
                'cumulative_income' => $cumIncome,
                'cumulative_net' => round($cumIncome - $cumInvested, 2),
            ];
        }

        $profitability = $this->profitabilityService->forAnimal($cattle);

        return [
            'events' => $entries,
            'series' => $series,
            'totals' => [
                'feed_cost' => $profitability['feed_cost'],
                'health_cost' => $profitability['health_cost'],
                'money_invested' => $profitability['total_cost'],
                'income' => $profitability['income'],
                'net_profit' => $profitability['net_profit'],
            ],
            'has_data' => count($series) > 0,
        ];
    }

    private function cattlePresentInLotOnDate(FatteningLotAnimal $assignment, Carbon $date): bool
    {
        if ($assignment->entry_date->gt($date)) {
            return false;
        }

        $end = $assignment->exit_date ?? $assignment->removed_at;
        if ($end && $end->lt($date)) {
            return false;
        }

        return true;
    }

    private function allocateExpenseShare(MaterialExpense $expense, Carbon $date): float
    {
        $count = $this->countAnimalsInLotOnDate((int) $expense->fattening_lot_id, $date);

        return round((float) $expense->total_cost / max(1, $count), 2);
    }

    private function countAnimalsInLotOnDate(int $lotId, Carbon $date): int
    {
        $dateStr = $date->toDateString();

        return FatteningLotAnimal::query()
            ->where('fattening_lot_id', $lotId)
            ->where('entry_date', '<=', $dateStr)
            ->where(function ($q) use ($dateStr) {
                $q->whereNull('exit_date')->orWhere('exit_date', '>=', $dateStr);
            })
            ->where(function ($q) use ($dateStr) {
                $q->whereNull('removed_at')->orWhere('removed_at', '>=', $dateStr);
            })
            ->count();
    }

    private function healthEventCost(CattleHealthEvent $event): float
    {
        if ($event->materialExpense) {
            return round((float) $event->materialExpense->total_cost, 2);
        }

        if ($event->cost !== null) {
            return round((float) $event->cost, 2);
        }

        return 0.0;
    }

    private function incomeAmount(Cattle $cattle): float
    {
        if ($cattle->sale_total_price !== null) {
            return round((float) $cattle->sale_total_price, 2);
        }

        return round((float) $cattle->saleIncome->sum('total_price'), 2);
    }
}
