<?php

namespace App\Services;

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

class RanchClosedLotsRankingService
{
    public function __construct(
        private readonly FatteningLotService $fatteningLotService,
        private readonly RanchAnimalProfitabilityService $profitabilityService,
    ) {}

    /**
     * @return array<string, mixed>
     */
    public function generate(?Carbon $from = null, ?Carbon $to = null, string $sortBy = 'net_profit'): array
    {
        $query = FatteningLot::query()
            ->where('status', FatteningLot::STATUS_CLOSED)
            ->with(['animals.cattle']);

        if ($from && $to) {
            $query->whereBetween('closed_at', [$from->toDateString(), $to->toDateString()]);
        }

        $rows = [];
        foreach ($query->orderByDesc('closed_at')->get() as $lot) {
            $rows[] = $this->rowForLot($lot);
        }

        $byConversion = $rows;
        usort($byConversion, function ($a, $b) {
            $cA = $a['feed_conversion_ratio'] ?? 999999;
            $cB = $b['feed_conversion_ratio'] ?? 999999;

            return $cA <=> $cB;
        });

        $byProfit = $rows;
        usort($byProfit, fn ($a, $b) => $b['net_profit'] <=> $a['net_profit']);

        $byProfitPerKg = $rows;
        usort($byProfitPerKg, function ($a, $b) {
            $pA = $a['profit_per_kg_gained'] ?? -999999;
            $pB = $b['profit_per_kg_gained'] ?? -999999;

            return $pB <=> $pA;
        });

        $primary = match ($sortBy) {
            'feed_conversion' => $byConversion,
            'profit_per_kg' => $byProfitPerKg,
            default => $byProfit,
        };

        foreach ($primary as $i => &$row) {
            $row['rank'] = $i + 1;
        }
        unset($row);

        return [
            'from' => $from?->toDateString(),
            'to' => $to?->toDateString(),
            'sort_by' => $sortBy,
            'lots_count' => count($rows),
            'lots' => $primary,
            'rankings' => [
                'by_feed_conversion' => array_map(fn ($r) => $this->rankEntry($r), $byConversion),
                'by_net_profit' => array_map(fn ($r) => $this->rankEntry($r), $byProfit),
                'by_profit_per_kg' => array_map(fn ($r) => $this->rankEntry($r), $byProfitPerKg),
            ],
            'best' => [
                'feed_conversion' => $byConversion[0] ?? null,
                'net_profit' => $byProfit[0] ?? null,
                'profit_per_kg' => $byProfitPerKg[0] ?? null,
            ],
        ];
    }

    /**
     * @return array<string, mixed>
     */
    private function rowForLot(FatteningLot $lot): array
    {
        $evaluation = $this->fatteningLotService->evaluateLot($lot);
        $pricePerKg = $lot->target_price_per_kg
            ?? config('ranch.fattening.default_price_per_kg_projection', 45);

        $projectedIncome = 0.0;
        $realizedIncome = 0.0;
        $totalGain = 0.0;
        $healthCost = 0.0;
        $soldCount = 0;

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

            $exit = $assignment->exit_weight_kg ?? $cattle->exit_weight_kg;
            if ($exit !== null) {
                $projectedIncome += (float) $exit * (float) $pricePerKg;
                $gain = max(0, (float) $exit - (float) $assignment->entry_weight_kg);
                $totalGain += $gain;
            }

            if ($cattle->status === Cattle::STATUS_SOLD) {
                $soldCount++;
                $realizedIncome += $this->realizedIncomeFor($cattle);
            }

            $healthCost += $this->profitabilityService->forAnimal($cattle)['health_cost'];
        }

        $feedCost = (float) $evaluation['total_feed_cost'];
        $totalCost = round($feedCost + $healthCost, 2);
        $netProfit = round($projectedIncome - $totalCost, 2);
        $realizedNet = round($realizedIncome - $totalCost, 2);
        $conversion = $evaluation['feed_conversion_ratio'];
        $target = $lot->target_net_profit !== null ? (float) $lot->target_net_profit : null;

        return [
            'lot_id' => $lot->id,
            'lot_name' => $lot->name,
            'entry_date' => $lot->entry_date->toDateString(),
            'closed_at' => $lot->closed_at?->toDateString(),
            'days_in_confinement' => $evaluation['days_in_confinement'],
            'animals_count' => $lot->animals->count(),
            'animals_sold' => $soldCount,
            'total_weight_gain_kg' => round($totalGain, 2),
            'feed_cost' => round($feedCost, 2),
            'health_cost' => round($healthCost, 2),
            'total_cost' => $totalCost,
            'price_per_kg_used' => (float) $pricePerKg,
            'projected_income' => round($projectedIncome, 2),
            'realized_income' => round($realizedIncome, 2),
            'net_profit' => $netProfit,
            'realized_net_profit' => $realizedNet,
            'profit_per_kg_gained' => $totalGain > 0 ? round($netProfit / $totalGain, 2) : null,
            'feed_conversion_ratio' => $conversion,
            'cost_per_kg_gained' => $totalGain > 0 ? round($totalCost / $totalGain, 2) : null,
            'target_net_profit' => $target,
            'met_target' => $target !== null ? $netProfit >= $target : null,
        ];
    }

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

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

    /**
     * @param  array<string, mixed>  $row
     * @return array<string, mixed>
     */
    private function rankEntry(array $row): array
    {
        return [
            'lot_id' => $row['lot_id'],
            'lot_name' => $row['lot_name'],
            'feed_conversion_ratio' => $row['feed_conversion_ratio'],
            'net_profit' => $row['net_profit'],
            'profit_per_kg_gained' => $row['profit_per_kg_gained'],
        ];
    }

    public function toCsv(array $report): string
    {
        $lines = [
            'Comparativo lotes cerrados',
            'Desde,'.($report['from'] ?? 'todos'),
            'Hasta,'.($report['to'] ?? 'todos'),
            '',
            'Ranking por utilidad proyectada',
            'Rank,Lote,Cierre,Días,Animales,Kg ganados,Conversión alim.,Alimento,Salud,Ingreso proyectado,Ingreso realizado,Utilidad proyectada,Utilidad realizada,$/kg ganado,Meta,¿Cumple meta?',
        ];

        foreach ($report['lots'] as $row) {
            $lines[] = implode(',', [
                $row['rank'] ?? '',
                $this->csvCell($row['lot_name']),
                $row['closed_at'] ?? '',
                $row['days_in_confinement'],
                $row['animals_count'],
                $row['total_weight_gain_kg'],
                $row['feed_conversion_ratio'] ?? '',
                $row['feed_cost'],
                $row['health_cost'],
                $row['projected_income'],
                $row['realized_income'],
                $row['net_profit'],
                $row['realized_net_profit'],
                $row['profit_per_kg_gained'] ?? '',
                $row['target_net_profit'] ?? '',
                $row['met_target'] === null ? '' : ($row['met_target'] ? 'si' : 'no'),
            ]);
        }

        $lines[] = '';
        $lines[] = 'Top conversión alimenticia (menor es mejor)';
        foreach ($report['rankings']['by_feed_conversion'] ?? [] as $i => $entry) {
            $lines[] = ($i + 1).','.$this->csvCell($entry['lot_name']).','.($entry['feed_conversion_ratio'] ?? '');
        }

        return implode("\n", $lines);
    }

    private function csvCell(string $value): string
    {
        return '"'.str_replace('"', '""', (string) $value).'"';
    }
}
