<?php

namespace App\Services;

use App\Models\Cattle;
use App\Models\CattleHealthEvent;
use App\Models\WeightRecord;
use Carbon\Carbon;

class CattleLifeSheetService
{
    public function __construct(
        private readonly CattleLifecycleSummaryService $lifecycleSummaryService,
        private readonly RanchAnimalProfitabilityService $profitabilityService,
        private readonly CattleLocationMovementService $locationMovementService,
        private readonly CattleInvestmentTimelineService $investmentTimelineService,
    ) {}

    /**
     * Hoja de vida consolidada: pesajes, engorda, progenie y baja.
     *
     * @return array<string, mixed>
     */
    public function build(Cattle $cattle): array
    {
        $globalGdpDefault = (float) config('ranch.weight_performance.min_gdp_kg_per_day', 0.5);

        $cattle->load([
            'mother:id,ear_tag,breed,status',
            'offspring' => fn ($q) => $q->orderBy('ear_tag'),
            'weightRecords' => fn ($q) => $q->orderByDesc('weighed_at'),
            'fatteningLotAssignments.fatteningLot',
            'saleIncome',
            'activeFatteningLotAssignment.fatteningLot',
            'healthEvents' => fn ($q) => $q->with(['materialType:id,name', 'materialExpense:id,total_cost'])->orderByDesc('event_date'),
            'calvingEventsAsMother' => fn ($q) => $q->with('calf:id,ear_tag,gender,status')->orderByDesc('calving_date'),
            'locationMovements' => fn ($q) => $q->with('fatteningLot:id,name')->limit(50),
        ]);

        $latestWeight = $cattle->weightRecords->first();
        $firstWeight = $cattle->weightRecords->last();

        $metrics = [
            'age_months' => $cattle->birth_date
                ? (int) $cattle->birth_date->diffInMonths(Carbon::today())
                : null,
            'last_weighed_at' => $latestWeight?->weighed_at?->toDateString(),
            'last_weight_kg' => $latestWeight ? (float) $latestWeight->weight_kg : null,
            'last_daily_weight_gain' => $latestWeight?->daily_weight_gain !== null
                ? (float) $latestWeight->daily_weight_gain
                : null,
            'target_min_gdp_kg_per_day' => $cattle->target_min_gdp_kg_per_day !== null
                ? (float) $cattle->target_min_gdp_kg_per_day
                : null,
            'effective_min_gdp_kg_per_day' => $cattle->resolvedMinGdpKgPerDay($globalGdpDefault),
            'gdp_target_source' => $cattle->gdpTargetSource($globalGdpDefault),
            'total_weighings' => $cattle->weightRecords->count(),
            'lifetime_weight_gain_kg' => $this->lifetimeGain($cattle, $latestWeight, $firstWeight),
            'current_fattening_lot' => $cattle->activeFatteningLotAssignment?->fatteningLot?->name,
            'pasture_location' => $cattle->pasture_location,
            'location_label' => $this->locationLabel($cattle),
            'health_events_count' => $cattle->healthEvents->count(),
            'illness_events_count' => $cattle->healthEvents
                ->where('event_type', CattleHealthEvent::TYPE_ILLNESS)
                ->count(),
            'profitability' => $this->profitabilityFor($cattle),
            'development_chart' => $this->developmentChart($cattle, $globalGdpDefault),
            'investment_timeline' => $this->investmentTimelineService->generate($cattle),
        ];

        $fatteningStints = $cattle->fatteningLotAssignments
            ->sortBy('entry_date')
            ->map(function ($assignment) use ($cattle) {
                $end = $assignment->exit_date ?? $assignment->removed_at ?? Carbon::today();
                $exitWeight = $assignment->exit_weight_kg ?? $cattle->exit_weight_kg;

                return [
                    'lot_id' => $assignment->fattening_lot_id,
                    'lot_name' => $assignment->fatteningLot?->name,
                    'entry_date' => $assignment->entry_date->toDateString(),
                    'exit_date' => $assignment->exit_date?->toDateString(),
                    'days_in_confinement' => max(1, $assignment->entry_date->diffInDays($end)),
                    'entry_weight_kg' => (float) $assignment->entry_weight_kg,
                    'exit_weight_kg' => $exitWeight !== null ? (float) $exitWeight : null,
                    'weight_gain_kg' => $exitWeight !== null
                        ? round((float) $exitWeight - (float) $assignment->entry_weight_kg, 2)
                        : null,
                    'is_active' => $assignment->removed_at === null && $assignment->exit_date === null,
                ];
            })
            ->values()
            ->all();

        $closureSummary = null;
        if (! $cattle->isActive() && $cattle->exit_weight_kg !== null) {
            $closureSummary = $this->lifecycleSummaryService->forDeactivation(
                $cattle,
                (float) $cattle->exit_weight_kg,
                $cattle->deactivated_at ?? Carbon::today()
            );
        }

        return [
            'animal' => [
                'id' => $cattle->id,
                'ear_tag' => $cattle->ear_tag,
                'display_ear_tag' => $cattle->display_ear_tag,
                'ear_tag_type' => $cattle->ear_tag_type,
                'breed' => $cattle->breed,
                'gender' => $cattle->gender,
                'birth_date' => $cattle->birth_date?->toDateString(),
                'status' => $cattle->status,
                'notes' => $cattle->notes,
                'photo_url' => $cattle->photo_url,
                'target_min_gdp_kg_per_day' => $cattle->target_min_gdp_kg_per_day !== null
                    ? (float) $cattle->target_min_gdp_kg_per_day
                    : null,
                'pasture_id' => $cattle->pasture_id,
                'pasture_location' => $cattle->pasture_location,
                'mother' => $cattle->mother,
                'deactivated_at' => $cattle->deactivated_at?->toDateString(),
                'deactivation_reason' => $cattle->deactivation_reason,
                'death_cause' => $cattle->death_cause,
                'exit_weight_kg' => $cattle->exit_weight_kg !== null ? (float) $cattle->exit_weight_kg : null,
                'sale_total_price' => $cattle->sale_total_price !== null ? (float) $cattle->sale_total_price : null,
                'sale_price_per_kg' => $cattle->sale_price_per_kg !== null ? (float) $cattle->sale_price_per_kg : null,
                'sale_date' => $cattle->sale_date?->toDateString(),
            ],
            'metrics' => $metrics,
            'weight_records' => $cattle->weightRecords->map(fn (WeightRecord $w) => [
                'id' => $w->id,
                'weighed_at' => $w->weighed_at->toDateString(),
                'weight_kg' => (float) $w->weight_kg,
                'weight_gain_kg' => $w->weight_gain_kg !== null ? (float) $w->weight_gain_kg : null,
                'days_since_last' => $w->days_since_last,
                'daily_weight_gain' => $w->daily_weight_gain !== null ? (float) $w->daily_weight_gain : null,
                'notes' => $w->notes,
            ])->values()->all(),
            'fattening_stints' => $fatteningStints,
            'offspring' => $cattle->offspring->map(fn (Cattle $child) => [
                'id' => $child->id,
                'ear_tag' => $child->ear_tag,
                'status' => $child->status,
                'gender' => $child->gender,
                'exit_weight_kg' => $child->exit_weight_kg !== null ? (float) $child->exit_weight_kg : null,
            ])->values()->all(),
            'calving_events' => $cattle->calvingEventsAsMother->map(fn ($event) => [
                'id' => $event->id,
                'calving_date' => $event->calving_date->toDateString(),
                'birth_weight_kg' => $event->birth_weight_kg !== null ? (float) $event->birth_weight_kg : null,
                'pasture_location' => $event->pasture_location,
                'calf' => $event->calf ? [
                    'id' => $event->calf->id,
                    'ear_tag' => $event->calf->ear_tag,
                    'gender' => $event->calf->gender,
                    'status' => $event->calf->status,
                ] : null,
            ])->values()->all(),
            'offspring_performance' => $this->offspringPerformance($cattle),
            'sale_incomes' => $cattle->saleIncome->map(fn ($row) => [
                'sale_date' => $row->sale_date?->toDateString(),
                'exit_weight_kg' => (float) $row->exit_weight_kg,
                'total_price' => (float) $row->total_price,
                'price_per_kg' => $row->price_per_kg !== null ? (float) $row->price_per_kg : null,
            ])->values()->all(),
            'closure_summary' => $closureSummary,
            'location_movements' => $cattle->locationMovements->map(fn ($movement) => [
                'id' => $movement->id,
                'moved_at' => $movement->moved_at->toDateString(),
                'reason' => $movement->reason,
                'reason_label' => $this->locationMovementService->labelForReason($movement->reason),
                'from_type_label' => $this->locationMovementService->labelForType($movement->from_type),
                'from_label' => $movement->from_label,
                'to_type_label' => $this->locationMovementService->labelForType($movement->to_type),
                'to_label' => $movement->to_label,
                'notes' => $movement->notes,
            ])->values()->all(),
            'health_events' => $cattle->healthEvents->map(fn (CattleHealthEvent $e) => [
                'id' => $e->id,
                'event_type' => $e->event_type,
                'event_date' => $e->event_date->toDateString(),
                'title' => $e->title,
                'product_name' => $e->product_name,
                'cost' => $e->cost !== null ? (float) $e->cost : null,
                'outcome' => $e->outcome,
                'notes' => $e->notes,
                'material' => $e->materialType?->name,
                'material_expense_id' => $e->material_expense_id,
                'expense_recorded' => $e->material_expense_id !== null,
                'expense_total' => $e->materialExpense ? (float) $e->materialExpense->total_cost : null,
            ])->values()->all(),
        ];
    }

    /**
     * @return array<string, mixed>|null
     */
    private function profitabilityFor(Cattle $cattle): array
    {
        $row = $this->profitabilityService->forAnimal($cattle);

        return [
            'income' => $row['income'],
            'feed_cost' => $row['feed_cost'],
            'health_cost' => $row['health_cost'],
            'total_cost' => $row['total_cost'],
            'money_invested' => $row['total_cost'],
            'net_profit' => $row['net_profit'],
            'margin_pct' => $row['margin_pct'],
            'weight_gain_kg' => $row['weight_gain_kg'],
            'cost_per_kg_gained' => $row['cost_per_kg_gained'],
            'profit_per_kg_gained' => $row['profit_per_kg_gained'],
        ];
    }

    /**
     * Serie cronológica para gráfica de peso y GDP en el expediente.
     *
     * @return array<string, mixed>
     */
    private function developmentChart(Cattle $cattle, float $globalGdpDefault): array
    {
        $points = $cattle->weightRecords
            ->sortBy('weighed_at')
            ->values()
            ->map(fn (WeightRecord $w) => [
                'date' => $w->weighed_at->toDateString(),
                'weight_kg' => (float) $w->weight_kg,
                'daily_weight_gain' => $w->daily_weight_gain !== null ? (float) $w->daily_weight_gain : null,
            ])
            ->all();

        return [
            'points' => $points,
            'target_min_gdp_kg_per_day' => $cattle->resolvedMinGdpKgPerDay($globalGdpDefault),
        ];
    }

    private function locationLabel(Cattle $cattle): string
    {
        if ($cattle->activeFatteningLotAssignment?->fatteningLot) {
            return 'Engorda: '.$cattle->activeFatteningLotAssignment->fatteningLot->name;
        }

        if ($cattle->pasture_location) {
            return 'Corral/pasto: '.$cattle->pasture_location;
        }

        return 'Sin ubicación registrada';
    }

    private function lifetimeGain(Cattle $cattle, ?WeightRecord $latest, ?WeightRecord $first): ?float
    {
        if ($cattle->exit_weight_kg !== null && $first) {
            return round((float) $cattle->exit_weight_kg - (float) $first->weight_kg, 2);
        }

        if ($latest && $first && $latest->id !== $first->id) {
            return round((float) $latest->weight_kg - (float) $first->weight_kg, 2);
        }

        return null;
    }

    /**
     * @return array<string, mixed>|null
     */
    private function offspringPerformance(Cattle $cattle): ?array
    {
        if ($cattle->gender !== 'female' || $cattle->offspring->isEmpty()) {
            return null;
        }

        $offspring = $cattle->offspring;
        $dead = $offspring->where('status', Cattle::STATUS_DEAD)->count();
        $total = $offspring->count();

        $weights = $offspring
            ->map(fn (Cattle $c) => $c->exit_weight_kg ?? WeightRecord::query()
                ->where('cattle_id', $c->id)
                ->orderByDesc('weighed_at')
                ->value('weight_kg'))
            ->filter()
            ->map(fn ($w) => (float) $w);

        $offspringIds = $offspring->pluck('id');
        $illnessEvents = CattleHealthEvent::query()
            ->whereIn('cattle_id', $offspringIds)
            ->where('event_type', CattleHealthEvent::TYPE_ILLNESS)
            ->count();

        $threshold = (int) config('ranch.health_alerts.offspring_illness_threshold', 2);

        return [
            'offspring_count' => $total,
            'offspring_active' => $offspring->where('status', Cattle::STATUS_ACTIVE)->count(),
            'offspring_dead' => $dead,
            'offspring_illness_events' => $illnessEvents,
            'health_risk' => $illnessEvents >= $threshold,
            'mortality_rate_pct' => $total > 0 ? round(($dead / $total) * 100, 1) : 0,
            'avg_offspring_weight_kg' => $weights->isNotEmpty()
                ? round($weights->avg(), 2)
                : null,
        ];
    }
}
