<?php

namespace App\Services;

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

class RanchAnimalProfitabilityService
{
    public function __construct(
        private readonly CattleLifecycleSummaryService $lifecycleSummaryService,
    ) {}

    /**
     * Rentabilidad: ingreso por venta − alimento asignado (engorda) − salud.
     * Con rango de fechas filtra animales vendidos en el periodo; los costos son acumulados de su ciclo.
     *
     * @return array<string, mixed>
     */
    public function generate(?Carbon $from = null, ?Carbon $to = null, string $statusFilter = 'sold'): array
    {
        $query = Cattle::query()
            ->with([
                'saleIncome',
                'healthEvents.materialExpense',
                'fatteningLotAssignments',
                'activeFatteningLotAssignment.fatteningLot',
            ]);

        if ($statusFilter !== 'all') {
            $query->where('status', $statusFilter);
        }

        if ($from && $to && $statusFilter === Cattle::STATUS_SOLD) {
            $query->whereBetween('sale_date', [$from->toDateString(), $to->toDateString()]);
        }

        $rows = [];
        $totals = [
            'income' => 0.0,
            'feed_cost' => 0.0,
            'health_cost' => 0.0,
            'total_cost' => 0.0,
            'net_profit' => 0.0,
            'weight_gain_kg' => 0.0,
        ];

        foreach ($query->orderBy('ear_tag')->get() as $cattle) {
            $row = $this->rowForAnimal($cattle, $from, $to);
            $rows[] = $row;
            foreach (['income', 'feed_cost', 'health_cost', 'total_cost', 'net_profit'] as $key) {
                $totals[$key] += $row[$key];
            }
            if ($row['weight_gain_kg'] !== null) {
                $totals['weight_gain_kg'] += $row['weight_gain_kg'];
            }
        }

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

        foreach ($totals as $key => $value) {
            $totals[$key] = round($value, 2);
        }

        $totals['cost_per_kg_gained'] = $totals['weight_gain_kg'] > 0
            ? round($totals['total_cost'] / $totals['weight_gain_kg'], 2)
            : null;
        $totals['profit_per_kg_gained'] = $totals['weight_gain_kg'] > 0
            ? round($totals['net_profit'] / $totals['weight_gain_kg'], 2)
            : null;

        return [
            'from' => $from?->toDateString(),
            'to' => $to?->toDateString(),
            'status_filter' => $statusFilter,
            'costs_scope' => ($from && $to) ? 'health_in_period_feed_lifetime' : 'lifetime',
            'animals_count' => count($rows),
            'totals' => $totals,
            'animals' => $rows,
            'fattening_lots' => $this->lotSummaries($rows),
        ];
    }

    /**
     * @return array<string, mixed>
     */
    public function forAnimal(Cattle $cattle, ?Carbon $from = null, ?Carbon $to = null): array
    {
        $cattle->loadMissing([
            'saleIncome',
            'healthEvents.materialExpense',
            'fatteningLotAssignments',
            'activeFatteningLotAssignment.fatteningLot',
        ]);

        return $this->rowForAnimal($cattle, $from, $to);
    }

    /**
     * @return array<string, mixed>
     */
    private function rowForAnimal(Cattle $cattle, ?Carbon $from, ?Carbon $to): array
    {
        $income = $this->incomeFor($cattle);
        $feedCost = $this->lifecycleSummaryService->totalAllocatedFeedCost($cattle);
        $healthCost = $this->healthCostFor($cattle, $from, $to);
        $totalCost = round($feedCost + $healthCost, 2);
        $netProfit = round($income - $totalCost, 2);
        $marginPct = $income > 0 ? round(($netProfit / $income) * 100, 1) : null;
        $weightGain = $this->weightGainKg($cattle);
        $costPerKg = ($weightGain !== null && $weightGain > 0)
            ? round($totalCost / $weightGain, 2)
            : null;
        $profitPerKg = ($weightGain !== null && $weightGain > 0)
            ? round($netProfit / $weightGain, 2)
            : null;

        return [
            'cattle_id' => $cattle->id,
            'ear_tag' => $cattle->ear_tag,
            'breed' => $cattle->breed,
            'status' => $cattle->status,
            'sale_date' => $cattle->sale_date?->toDateString(),
            'exit_weight_kg' => $cattle->exit_weight_kg !== null ? (float) $cattle->exit_weight_kg : null,
            'weight_gain_kg' => $weightGain,
            'income' => $income,
            'feed_cost' => $feedCost,
            'health_cost' => $healthCost,
            'total_cost' => $totalCost,
            'net_profit' => $netProfit,
            'margin_pct' => $marginPct,
            'cost_per_kg_gained' => $costPerKg,
            'profit_per_kg_gained' => $profitPerKg,
            'profitable' => $netProfit > 0,
            'current_lot' => $cattle->activeFatteningLotAssignment?->fatteningLot?->name,
        ];
    }

    private function weightGainKg(Cattle $cattle): ?float
    {
        $exit = $cattle->exit_weight_kg !== null
            ? (float) $cattle->exit_weight_kg
            : (float) (WeightRecord::query()
                ->where('cattle_id', $cattle->id)
                ->orderByDesc('weighed_at')
                ->value('weight_kg') ?? 0);

        if ($exit <= 0) {
            return null;
        }

        $firstWeight = WeightRecord::query()
            ->where('cattle_id', $cattle->id)
            ->orderBy('weighed_at')
            ->value('weight_kg');

        if ($firstWeight !== null) {
            $gain = $exit - (float) $firstWeight;

            return $gain > 0 ? round($gain, 2) : null;
        }

        $firstAssignment = $cattle->fatteningLotAssignments->sortBy('entry_date')->first();
        if ($firstAssignment) {
            $gain = $exit - (float) $firstAssignment->entry_weight_kg;

            return $gain > 0 ? round($gain, 2) : null;
        }

        return null;
    }

    public function toCsv(array $report): string
    {
        $lines = [
            'Rentabilidad por animal',
            'Desde,'.($report['from'] ?? 'todos'),
            'Hasta,'.($report['to'] ?? 'todos'),
            'Estatus,'.$report['status_filter'],
            '',
            'Totales',
            'Ingresos,'.$report['totals']['income'],
            'Alimento,'.$report['totals']['feed_cost'],
            'Salud,'.$report['totals']['health_cost'],
            'Costo total,'.$report['totals']['total_cost'],
            'Utilidad neta,'.$report['totals']['net_profit'],
            'Kg ganados (suma),'.($report['totals']['weight_gain_kg'] ?? 0),
            'Costo por kg ganado,'.($report['totals']['cost_per_kg_gained'] ?? ''),
            'Utilidad por kg ganado,'.($report['totals']['profit_per_kg_gained'] ?? ''),
            '',
            'Por animal',
            'Arete,Raza,Estatus,Fecha venta,Peso salida kg,Kg ganados,Ingreso,Alimento,Salud,Costo total,Utilidad,Margen %,Costo/kg ganado,Utilidad/kg ganado',
        ];

        foreach ($report['animals'] as $row) {
            $lines[] = implode(',', [
                $this->csvCell($row['ear_tag']),
                $this->csvCell($row['breed'] ?? ''),
                $row['status'],
                $row['sale_date'] ?? '',
                $row['exit_weight_kg'] ?? '',
                $row['weight_gain_kg'] ?? '',
                $row['income'],
                $row['feed_cost'],
                $row['health_cost'],
                $row['total_cost'],
                $row['net_profit'],
                $row['margin_pct'] ?? '',
                $row['cost_per_kg_gained'] ?? '',
                $row['profit_per_kg_gained'] ?? '',
            ]);
        }

        if (! empty($report['fattening_lots'])) {
            $lines[] = '';
            $lines[] = 'Por lote de engorda';
            $lines[] = 'Lote,Estatus,Animales,Ingreso,Alimento,Salud,Utilidad';
            foreach ($report['fattening_lots'] as $lot) {
                $lines[] = implode(',', [
                    $this->csvCell($lot['lot_name']),
                    $lot['status'],
                    $lot['animals_count'],
                    $lot['income'],
                    $lot['feed_cost'],
                    $lot['health_cost'],
                    $lot['net_profit'],
                ]);
            }
        }

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

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

    private function incomeFor(Cattle $cattle): float
    {
        if ($cattle->status !== Cattle::STATUS_SOLD) {
            return 0.0;
        }

        if ($cattle->sale_total_price !== null) {
            return (float) $cattle->sale_total_price;
        }

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

    private function healthCostFor(Cattle $cattle, ?Carbon $from, ?Carbon $to): float
    {
        $query = $cattle->healthEvents()->with('materialExpense');

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

        $total = 0.0;
        foreach ($query->get() as $event) {
            if ($event->materialExpense) {
                $total += (float) $event->materialExpense->total_cost;
            } elseif ($event->cost !== null) {
                $total += (float) $event->cost;
            }
        }

        return round($total, 2);
    }

    /**
     * @param  list<array<string, mixed>>  $animalRows
     * @return list<array<string, mixed>>
     */
    private function lotSummaries(array $animalRows): array
    {
        $summaries = [];

        foreach (FatteningLot::query()->orderBy('name')->get() as $lot) {
            $cattleIds = $lot->animals()->pluck('cattle_id');
            $subset = collect($animalRows)->whereIn('cattle_id', $cattleIds);

            $lotFeed = (float) MaterialExpense::query()
                ->where('fattening_lot_id', $lot->id)
                ->sum('total_cost');

            $income = (float) $subset->sum('income');
            $health = (float) $subset->sum('health_cost');

            $summaries[] = [
                'lot_id' => $lot->id,
                'lot_name' => $lot->name,
                'status' => $lot->status,
                'animals_count' => $cattleIds->count(),
                'income' => round($income, 2),
                'feed_cost' => round($lotFeed, 2),
                'health_cost' => round($health, 2),
                'total_cost' => round($lotFeed + $health, 2),
                'net_profit' => round($income - $lotFeed - $health, 2),
            ];
        }

        return $summaries;
    }
}
