<?php

namespace App\Http\Controllers\Ranch;

use App\Http\Controllers\Controller;
use App\Models\Cattle;
use App\Models\CattleHealthEvent;
use App\Models\ConfiguracionEmpresa;
use App\Models\FatteningLot;
use App\Models\FatteningLotFeedIntake;
use App\Services\FatteningLotAnalyticsService;
use App\Services\FatteningLotFeedIntakeService;
use App\Services\FatteningLotService;
use App\Services\FeedStockService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use InvalidArgumentException;

class FatteningLotController extends Controller
{
    public function __construct(
        private readonly FatteningLotService $fatteningLotService,
        private readonly FatteningLotFeedIntakeService $feedIntakeService,
        private readonly FatteningLotAnalyticsService $analyticsService,
        private readonly FeedStockService $feedStockService,
    ) {}

    public function index(Request $request): JsonResponse
    {
        $query = FatteningLot::query()
            ->withCount(['activeAnimals as animals_count'])
            ->orderByDesc('entry_date');

        if ($request->filled('status')) {
            $query->where('status', $request->string('status'));
        }

        $paginator = $query->paginate(20);
        $paginator->getCollection()->transform(function (FatteningLot $lot) {
            $analytics = $this->analyticsService->forLot($lot);
            $breakdown = $analytics['investment_breakdown'] ?? [];
            $projection = $analytics['projection'] ?? [];

            $lot->setAttribute('summary', [
                'total_investment' => $breakdown['total'] ?? null,
                'feed_cost' => $breakdown['feed_cost'] ?? null,
                'health_cost' => $breakdown['health_cost'] ?? null,
                'estimated_sale_total' => $projection['projected_income'] ?? null,
                'estimated_net_profit' => $projection['projected_net_profit'] ?? null,
            ]);

            return $lot;
        });

        return response()->json(['data' => $paginator]);
    }

    public function store(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'name' => ['required', 'string', 'max:150'],
            'description' => ['nullable', 'string'],
            'entry_date' => ['required', 'date'],
            'planned_duration_days' => ['nullable', 'integer', 'min:1', 'max:730'],
            'pasture_cost_per_animal_month' => ['nullable', 'numeric', 'min:0'],
            'notes' => ['nullable', 'string'],
            'target_net_profit' => ['nullable', 'numeric', 'min:0'],
            'target_price_per_kg' => ['nullable', 'numeric', 'min:0'],
            'target_min_gdp_kg_per_day' => ['nullable', 'numeric', 'min:0'],
        ]);

        $lot = FatteningLot::create($validated + [
            'status' => FatteningLot::STATUS_OPEN,
            'planned_duration_days' => $validated['planned_duration_days']
                ?? config('ranch.fattening.default_duration_days', 90),
            'pasture_cost_per_animal_month' => $validated['pasture_cost_per_animal_month']
                ?? config('ranch.fattening.default_pasture_cost_per_animal_month', 100),
        ]);

        return response()->json(['data' => $lot], 201);
    }

    public function show(FatteningLot $fatteningLot): JsonResponse
    {
        $fatteningLot->load(['activeAnimals.cattle', 'expenses.materialType']);
        $evaluation = $this->fatteningLotService->evaluateLot($fatteningLot);
        $feedInvestment = $this->feedIntakeService->investmentSummary($fatteningLot);
        $analytics = $this->analyticsService->forLot($fatteningLot);

        $weightByCattle = collect($analytics['weight_series'] ?? [])->keyBy('cattle_id');

        $animals = $this->mapAnimalsWithFinancials(
            $fatteningLot,
            $fatteningLot->activeAnimals,
            $evaluation,
            $feedInvestment,
            $weightByCattle,
            $this->fatteningLotService->healthCostBreakdownForLot($fatteningLot),
            collect($analytics['projection']['animals'] ?? [])->keyBy('cattle_id'),
        );

        $cattleIds = $fatteningLot->animals()->pluck('cattle_id');
        $healthEvents = CattleHealthEvent::query()
            ->whereIn('cattle_id', $cattleIds)
            ->with([
                'cattle:id,ear_tag',
                'materialType:id,name,unit',
                'materialExpense:id,total_cost,quantity,unit_cost,expense_date',
            ])
            ->orderByDesc('event_date')
            ->get();

        return response()->json([
            'data' => $fatteningLot,
            'meta' => [
                'evaluation' => $evaluation,
                'feed_investment' => $feedInvestment,
                'animals' => $animals,
                'analytics' => $analytics,
                'health_events' => $healthEvents,
                'health_cost' => $evaluation['health_cost'] ?? null,
                'feed_inventory' => $this->feedStockService->feedInventory(),
                'days_in_confinement' => $fatteningLot->daysInConfinement(),
                'default_price_per_kg' => config('ranch.fattening.default_price_per_kg_projection', 45),
                'default_min_gdp_kg_per_day' => config('ranch.fattening.default_min_gdp_kg_per_day', 0.5),
                'default_duration_days' => config('ranch.fattening.default_duration_days', 90),
            ],
        ]);
    }

    public function update(Request $request, FatteningLot $fatteningLot): JsonResponse
    {
        if ($fatteningLot->status !== FatteningLot::STATUS_OPEN) {
            return response()->json(['message' => 'Solo se pueden editar lotes abiertos.'], 422);
        }

        $validated = $request->validate([
            'name' => ['sometimes', 'string', 'max:150'],
            'description' => ['nullable', 'string'],
            'notes' => ['nullable', 'string'],
            'planned_duration_days' => ['nullable', 'integer', 'min:1', 'max:730'],
            'pasture_cost_per_animal_month' => ['nullable', 'numeric', 'min:0'],
            'target_net_profit' => ['nullable', 'numeric', 'min:0'],
            'target_price_per_kg' => ['nullable', 'numeric', 'min:0'],
            'target_min_gdp_kg_per_day' => ['nullable', 'numeric', 'min:0'],
        ]);

        $fatteningLot->update($validated);

        return response()->json(['data' => $fatteningLot->fresh()]);
    }

    public function previewClose(Request $request, FatteningLot $fatteningLot): JsonResponse
    {
        $validated = $request->validate([
            'exit_weights' => ['required', 'array'],
            'exit_weights.*.cattle_id' => ['required', 'integer'],
            'exit_weights.*.exit_weight_kg' => ['required', 'numeric', 'min:0.01'],
            'price_per_kg' => ['nullable', 'numeric', 'min:0.01'],
        ]);

        $map = [];
        foreach ($validated['exit_weights'] as $row) {
            $map[(int) $row['cattle_id']] = (float) $row['exit_weight_kg'];
        }

        if (isset($validated['price_per_kg'])) {
            $fatteningLot->target_price_per_kg = $validated['price_per_kg'];
        }

        return response()->json([
            'data' => $this->fatteningLotService->previewClose($fatteningLot, $map),
        ]);
    }

    public function addAnimal(Request $request, FatteningLot $fatteningLot): JsonResponse
    {
        $validated = $request->validate([
            'cattle_id' => ['required_without:ear_tag', 'integer', 'exists:cattle,id'],
            'ear_tag' => ['required_without:cattle_id', 'string'],
            'entry_weight_kg' => ['required', 'numeric', 'min:0.01'],
            'entry_date' => ['nullable', 'date'],
        ]);

        $cattle = isset($validated['cattle_id'])
            ? Cattle::findOrFail($validated['cattle_id'])
            : Cattle::where('ear_tag', $validated['ear_tag'])->firstOrFail();

        try {
            $assignment = $this->fatteningLotService->addAnimal(
                $fatteningLot,
                $cattle,
                (float) $validated['entry_weight_kg'],
                isset($validated['entry_date']) ? \Carbon\Carbon::parse($validated['entry_date']) : null
            );
        } catch (InvalidArgumentException $e) {
            return response()->json(['message' => $e->getMessage()], 422);
        }

        return response()->json(['data' => $assignment->load('cattle')], 201);
    }

    public function closeAndSell(Request $request, FatteningLot $fatteningLot): JsonResponse
    {
        $validated = $request->validate([
            'exit_weights' => ['required', 'array'],
            'exit_weights.*.cattle_id' => ['required', 'integer'],
            'exit_weights.*.exit_weight_kg' => ['required', 'numeric', 'min:0.01'],
            'price_per_kg' => ['required', 'numeric', 'min:0.01'],
            'sale_date' => ['nullable', 'date'],
        ]);

        $map = [];
        foreach ($validated['exit_weights'] as $row) {
            $map[(int) $row['cattle_id']] = (float) $row['exit_weight_kg'];
        }

        $preview = $this->fatteningLotService->previewClose(
            tap($fatteningLot)->setAttribute('target_price_per_kg', $validated['price_per_kg']),
            $map
        );

        try {
            $lot = $this->fatteningLotService->closeAndSellLot(
                $fatteningLot,
                $map,
                (float) $validated['price_per_kg'],
                isset($validated['sale_date']) ? \Carbon\Carbon::parse($validated['sale_date']) : null,
                auth()->id()
            );
        } catch (InvalidArgumentException $e) {
            return response()->json(['message' => $e->getMessage()], 422);
        }

        $evaluation = $this->fatteningLotService->evaluateLot($lot);

        return response()->json([
            'data' => $lot,
            'meta' => [
                'evaluation' => $evaluation,
                'close_preview' => $preview,
            ],
        ]);
    }

    public function feedInventory(): JsonResponse
    {
        return response()->json(['data' => $this->feedStockService->feedInventory()]);
    }

    public function pdf(FatteningLot $fatteningLot)
    {
        $fatteningLot->load(['activeAnimals.cattle', 'animals.cattle']);
        $evaluation = $this->fatteningLotService->evaluateLot($fatteningLot);
        $feedInvestment = $this->feedIntakeService->investmentSummary($fatteningLot);
        $analytics = $this->analyticsService->forLot($fatteningLot);
        $projection = $analytics['projection'] ?? null;

        $weightByCattle = collect($analytics['weight_series'] ?? [])->keyBy('cattle_id');
        $assignments = $fatteningLot->status === FatteningLot::STATUS_OPEN
            ? $fatteningLot->activeAnimals
            : $fatteningLot->animals;

        $animals = $this->mapAnimalsWithFinancials(
            $fatteningLot,
            $assignments,
            $evaluation,
            $feedInvestment,
            $weightByCattle,
            $this->fatteningLotService->healthCostBreakdownForLot($fatteningLot),
            collect($projection['animals'] ?? [])->keyBy('cattle_id'),
        )->values()->all();

        $intakes = FatteningLotFeedIntake::query()
            ->where('fattening_lot_id', $fatteningLot->id)
            ->with(['items.materialType'])
            ->orderByDesc('intake_date')
            ->limit(60)
            ->get()
            ->map(fn ($i) => $i->toArray())
            ->all();

        $empresa = ConfiguracionEmpresa::obtenerConfiguracion()?->nombre_empresa ?? 'Control Ganadero';
        $slug = preg_replace('/[^A-Za-z0-9_-]+/', '-', $fatteningLot->name);

        $pdf = \Barryvdh\DomPDF\Facade\Pdf::loadView('pdf.ranch_fattening_lot', [
            'lot' => $fatteningLot,
            'evaluation' => $evaluation,
            'projection' => $projection,
            'animals' => $animals,
            'intakes' => $intakes,
            'feedInventory' => $this->feedStockService->feedInventory(),
            'empresa' => $empresa,
            'generated_at' => now()->format('Y-m-d H:i'),
        ])->setPaper('letter', 'portrait');

        return $pdf->download('lote-engorda-'.$slug.'.pdf');
    }

    public function close(Request $request, FatteningLot $fatteningLot): JsonResponse
    {
        $validated = $request->validate([
            'exit_weights' => ['required', 'array'],
            'exit_weights.*.cattle_id' => ['required', 'integer'],
            'exit_weights.*.exit_weight_kg' => ['required', 'numeric', 'min:0.01'],
        ]);

        $map = [];
        foreach ($validated['exit_weights'] as $row) {
            $map[(int) $row['cattle_id']] = (float) $row['exit_weight_kg'];
        }

        $preview = $this->fatteningLotService->previewClose($fatteningLot, $map);

        try {
            $lot = $this->fatteningLotService->closeLot($fatteningLot, $map);
        } catch (InvalidArgumentException $e) {
            return response()->json(['message' => $e->getMessage()], 422);
        }

        $evaluation = $this->fatteningLotService->evaluateLot($lot);

        return response()->json([
            'data' => $lot,
            'meta' => [
                'evaluation' => $evaluation,
                'close_preview' => $preview,
            ],
        ]);
    }

    /**
     * @param  \Illuminate\Support\Collection<int, \App\Models\FatteningLotAnimal>  $assignments
     * @param  array<string, mixed>  $evaluation
     * @param  array<string, mixed>  $feedInvestment
     * @param  \Illuminate\Support\Collection<int, array<string, mixed>>  $weightByCattle
     * @return \Illuminate\Support\Collection<int, array<string, mixed>>
     */
    private function mapAnimalsWithFinancials(
        FatteningLot $lot,
        $assignments,
        array $evaluation,
        array $feedInvestment,
        $weightByCattle,
        array $healthBreakdown = ['by_cattle' => []],
        $projectionByCattle = null,
    ) {
        $heads = max(1, $evaluation['active_animals_count'] ?? $assignments->count());
        $feedTotal = (float) ($feedInvestment['total_cost'] ?? 0);
        $pastureTotal = (float) ($evaluation['pasture_cost'] ?? 0);
        $pricePerKg = (float) ($lot->target_price_per_kg
            ?? config('ranch.fattening.default_price_per_kg_projection', 45));
        $projectionByCattle ??= collect();

        return $assignments->map(function ($assignment) use (
            $weightByCattle,
            $heads,
            $feedTotal,
            $pastureTotal,
            $pricePerKg,
            $healthBreakdown,
            $projectionByCattle,
        ) {
            $weightRow = $weightByCattle->get($assignment->cattle_id);
            $gain = $assignment->exit_weight_kg !== null
                ? max(0, (float) $assignment->exit_weight_kg - (float) $assignment->entry_weight_kg)
                : ($weightRow['weight_gain_kg'] ?? null);

            $feedShare = $feedTotal / $heads;
            $pastureShare = $pastureTotal / $heads;
            $healthRow = $healthBreakdown['by_cattle'][$assignment->cattle_id] ?? ['actual' => 0, 'projected' => 0];
            $healthActual = (float) $healthRow['actual'];
            $healthProjected = (float) $healthRow['projected'];
            $investment = round($feedShare + $pastureShare + $healthActual, 2);
            $projectedInvestment = round($feedShare + $pastureShare + $healthProjected, 2);
            $saleWeight = (float) ($weightRow['current_weight_kg'] ?? $assignment->entry_weight_kg);
            $estimatedSaleTotal = round($saleWeight * $pricePerKg, 2);
            $estimatedNetProfit = round($estimatedSaleTotal - $investment, 2);

            $projectionRow = $projectionByCattle->get($assignment->cattle_id);
            $projectedSaleTotal = $projectionRow
                ? round((float) $projectionRow['exit_weight_kg'] * $pricePerKg, 2)
                : null;
            $projectedNetProfit = $projectedSaleTotal !== null
                ? round($projectedSaleTotal - $projectedInvestment, 2)
                : null;

            $gainKg = $gain !== null ? (float) $gain : null;
            $gainValue = $gainKg !== null ? round($gainKg * $pricePerKg, 2) : null;

            return [
                ...$assignment->toArray(),
                'weight_gain_kg' => $gain,
                'current_weight_kg' => $weightRow['current_weight_kg'] ?? null,
                'estimated_feed_cost' => round($feedShare, 2),
                'estimated_pasture_cost' => round($pastureShare, 2),
                'estimated_health_cost' => round($healthActual, 2),
                'projected_health_cost' => round($healthProjected, 2),
                'estimated_total_investment' => $investment,
                'projected_total_investment' => $projectedInvestment,
                'price_per_kg_used' => $pricePerKg,
                'estimated_sale_total' => $estimatedSaleTotal,
                'estimated_net_profit' => $estimatedNetProfit,
                'projected_sale_total' => $projectedSaleTotal,
                'projected_net_profit' => $projectedNetProfit,
                'weight_gain_summary' => [
                    'total_kg' => $gainKg !== null ? round($gainKg, 2) : null,
                    'price_per_kg' => $pricePerKg,
                    'value_at_price' => $gainValue,
                    'investment' => $investment,
                    'net_profit' => $gainValue !== null ? round($gainValue - $investment, 2) : null,
                ],
            ];
        });
    }
}
