<?php

namespace App\Services;

use App\Models\FatteningLot;
use App\Models\WeightRecord;
use Carbon\Carbon;

class RanchOpenLotsGdpAlertService
{
    /**
     * @return array<string, mixed>
     */
    public function generate(?float $defaultMinGdp = null, ?int $daysWithoutWeigh = null): array
    {
        $defaultMinGdp ??= (float) config('ranch.weight_performance.min_gdp_kg_per_day', 0.5);
        $daysWithoutWeigh ??= (int) config('ranch.weight_performance.days_without_weigh_alert', 30);
        $today = Carbon::today();

        $lots = FatteningLot::query()
            ->open()
            ->with(['activeAnimals.cattle'])
            ->orderBy('name')
            ->get();

        $lotRows = [];
        $allAnimalAlerts = [];

        foreach ($lots as $lot) {
            $lotTarget = $lot->target_min_gdp_kg_per_day !== null
                ? (float) $lot->target_min_gdp_kg_per_day
                : $defaultMinGdp;

            $animalRows = [];
            $gdpValues = [];

            foreach ($lot->activeAnimals as $assignment) {
                $cattle = $assignment->cattle;
                if (! $cattle) {
                    continue;
                }

                $animalRow = $this->animalRow($assignment, $cattle, $lot, $lotTarget, $defaultMinGdp, $daysWithoutWeigh, $today);
                $animalRows[] = $animalRow;

                if ($animalRow['daily_weight_gain'] !== null) {
                    $gdpValues[] = (float) $animalRow['daily_weight_gain'];
                }

                if ($animalRow['alert_type'] !== null) {
                    $allAnimalAlerts[] = $animalRow;
                }
            }

            $avgGdp = count($gdpValues) > 0 ? round(array_sum($gdpValues) / count($gdpValues), 4) : null;
            $belowCount = collect($animalRows)->where('alert_type', 'low_gdp')->count();
            $meetsLotTarget = $avgGdp !== null ? $avgGdp >= $lotTarget : null;
            $noGdpData = count($animalRows) > 0 && count($gdpValues) === 0;

            $lotRows[] = [
                'lot_id' => $lot->id,
                'lot_name' => $lot->name,
                'target_min_gdp_kg_per_day' => $lotTarget,
                'animals_count' => count($animalRows),
                'animals_with_gdp' => count($gdpValues),
                'animals_below_gdp' => $belowCount,
                'avg_daily_weight_gain' => $avgGdp,
                'meets_lot_gdp_target' => $meetsLotTarget,
                'has_gdp_alert' => $belowCount > 0 || $meetsLotTarget === false || $noGdpData,
                'animals' => $animalRows,
            ];
        }

        $lotsWithAlerts = array_values(array_filter($lotRows, fn ($l) => $l['has_gdp_alert']));

        usort($lotsWithAlerts, fn ($a, $b) => ($b['animals_below_gdp'] ?? 0) <=> ($a['animals_below_gdp'] ?? 0));

        return [
            'default_min_gdp_kg_per_day' => $defaultMinGdp,
            'days_without_weigh_alert' => $daysWithoutWeigh,
            'lots_count' => count($lotRows),
            'lots_with_alerts_count' => count($lotsWithAlerts),
            'animal_alerts_count' => count($allAnimalAlerts),
            'lots' => $lotRows,
            'lots_with_alerts' => $lotsWithAlerts,
            'animal_alerts' => $allAnimalAlerts,
        ];
    }

    public function lotsWithAlertsCount(): int
    {
        return $this->generate()['lots_with_alerts_count'];
    }

    /**
     * @return array<string, mixed>
     */
    private function animalRow(
        $assignment,
        $cattle,
        FatteningLot $lot,
        float $lotTarget,
        float $globalDefault,
        int $daysWithoutWeigh,
        Carbon $today,
    ): array {
        $animalTarget = $cattle->resolvedMinGdpKgPerDay($globalDefault);
        $latest = WeightRecord::query()
            ->where('cattle_id', $cattle->id)
            ->orderByDesc('weighed_at')
            ->first();

        $daysSinceWeigh = $latest ? $latest->weighed_at->diffInDays($today) : null;
        $gdp = $latest?->daily_weight_gain !== null ? (float) $latest->daily_weight_gain : null;

        $row = [
            'cattle_id' => $cattle->id,
            'ear_tag' => $cattle->ear_tag,
            'lot_id' => $lot->id,
            'lot_name' => $lot->name,
            'target_min_gdp_kg_per_day' => $animalTarget,
            'lot_target_min_gdp_kg_per_day' => $lotTarget,
            'gdp_target_source' => $cattle->gdpTargetSource($globalDefault),
            'last_weighed_at' => $latest?->weighed_at?->toDateString(),
            'last_weight_kg' => $latest ? (float) $latest->weight_kg : null,
            'daily_weight_gain' => $gdp,
            'days_since_weigh' => $daysSinceWeigh,
            'days_in_lot' => $assignment->daysInLot(),
            'alert_type' => null,
            'alert_message' => null,
        ];

        if (! $latest) {
            $row['alert_type'] = 'never_weighed';
            $row['alert_message'] = 'Sin pesajes en engorda';
        } elseif ($daysSinceWeigh !== null && $daysSinceWeigh > $daysWithoutWeigh) {
            $row['alert_type'] = 'stale_weigh';
            $row['alert_message'] = "Sin pesar hace {$daysSinceWeigh} días";
        } elseif ($gdp === null) {
            $row['alert_type'] = 'no_gdp';
            $row['alert_message'] = 'Faltan dos pesajes para calcular GDP';
        } elseif ($gdp < $animalTarget) {
            $row['alert_type'] = 'low_gdp';
            $row['alert_message'] = 'GDP '.number_format($gdp, 2).' < meta '.number_format($animalTarget, 2).' kg/día';
        }

        return $row;
    }
}
