<?php

namespace App\Services;

use App\Models\Cattle;
use App\Models\WeightRecord;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Builder;

class RanchInventoryReportService
{
    /**
     * @param  array<string, mixed>  $filters
     * @return array<string, mixed>
     */
    public function generate(array $filters = []): array
    {
        $statusFilter = $filters['status'] ?? 'active';
        $locationType = $filters['location_type'] ?? null;
        $pastureId = isset($filters['pasture_id']) ? (int) $filters['pasture_id'] : null;
        $pastureLocation = isset($filters['pasture_location']) ? trim((string) $filters['pasture_location']) : null;
        $breed = isset($filters['breed']) ? trim((string) $filters['breed']) : null;
        $gender = $filters['gender'] ?? null;

        $query = $this->baseQuery($statusFilter, $locationType, $pastureId, $pastureLocation, $breed, $gender);

        $animals = $query->orderBy('ear_tag')->get();
        $rows = [];
        $today = Carbon::today();

        foreach ($animals as $cattle) {
            $rows[] = $this->rowFor($cattle, $today);
        }

        return [
            'filters' => [
                'status' => $statusFilter,
                'location_type' => $locationType,
                'pasture_id' => $pastureId,
                'pasture_location' => $pastureLocation ?: null,
                'breed' => $breed ?: null,
                'gender' => $gender,
            ],
            'totals' => [
                'head_count' => count($rows),
                'in_fattening' => collect($rows)->where('location_type', 'fattening')->count(),
                'in_pasture' => collect($rows)->where('location_type', 'pasture')->count(),
                'unassigned' => collect($rows)->where('location_type', 'unassigned')->count(),
            ],
            'by_location' => $this->groupByLocation($rows),
            'by_breed' => $this->groupCount($rows, 'breed'),
            'by_gender' => $this->groupCount($rows, 'gender'),
            'animals' => $rows,
        ];
    }

    /**
     * Resumen ligero para el panel (solo activos).
     *
     * @return list<array{location_key: string, location_label: string, head_count: int}>
     */
    public function activeLocationSummary(int $limit = 10): array
    {
        $report = $this->generate(['status' => 'active']);

        return array_slice($report['by_location'], 0, $limit);
    }

      /**
     * @param  array<string, mixed>  $report
     */
    public function toCsv(array $report): string
    {
        $lines = [
            'Inventario de ganado',
            'Estatus,'.($report['filters']['status'] ?? 'active'),
            'Ubicación tipo,'.($report['filters']['location_type'] ?? 'todos'),
            'Corral/pasto filtro,'.($report['filters']['pasture_location'] ?? ''),
            'Total cabezas,'.($report['totals']['head_count'] ?? 0),
            '',
            'Resumen por ubicación',
            'Ubicación,Cabezas',
        ];

        foreach ($report['by_location'] as $group) {
            $lines[] = $this->csvCell($group['location_label']).','.$group['head_count'];
        }

        $lines[] = '';
        $lines[] = 'Detalle';
        $lines[] = 'Arete,Raza,Género,Edad meses,Madre,Estatus,Tipo ubicación,Ubicación,Último peso kg,Fecha peso,GDP kg/día,Días sin pesar,Crías';

        foreach ($report['animals'] as $row) {
            $lines[] = implode(',', [
                $this->csvCell($row['ear_tag']),
                $this->csvCell($row['breed'] ?? ''),
                $row['gender_label'],
                $row['age_months'] ?? '',
                $this->csvCell($row['mother_ear_tag'] ?? ''),
                $row['status_label'],
                $row['location_type_label'],
                $this->csvCell($row['location_label']),
                $row['last_weight_kg'] ?? '',
                $row['last_weighed_at'] ?? '',
                $row['daily_weight_gain'] ?? '',
                $row['days_since_weigh'] ?? '',
                $row['offspring_count'] ?? '',
            ]);
        }

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

  /**
     * @return Builder<Cattle>
     */
    private function baseQuery(
        string $statusFilter,
        ?string $locationType,
        ?int $pastureId,
        ?string $pastureLocation,
        ?string $breed,
        ?string $gender,
    ): Builder {
        $query = Cattle::query()
            ->with([
                'mother:id,ear_tag',
                'pasture:id,name',
                'activeFatteningLotAssignment.fatteningLot:id,name',
            ])
            ->withCount('offspring');

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

        if ($breed !== null && $breed !== '') {
            $query->where('breed', 'like', '%'.$breed.'%');
        }

        if ($gender) {
            $query->where('gender', $gender);
        }

        if ($pastureId) {
            $query->where('pasture_id', $pastureId);
        } elseif ($pastureLocation !== null && $pastureLocation !== '') {
            $query->where('pasture_location', 'like', '%'.$pastureLocation.'%');
        }

        if ($locationType === 'fattening') {
            $query->whereHas('activeFatteningLotAssignment');
        } elseif ($locationType === 'pasture') {
            $query->whereDoesntHave('activeFatteningLotAssignment')
                ->whereNotNull('pasture_location')
                ->where('pasture_location', '!=', '');
        } elseif ($locationType === 'unassigned') {
            $query->whereDoesntHave('activeFatteningLotAssignment')
                ->where(function (Builder $q) {
                    $q->whereNull('pasture_location')->orWhere('pasture_location', '');
                });
        }

        return $query;
    }

    /**
     * @return array<string, mixed>
     */
    private function rowFor(Cattle $cattle, Carbon $today): array
    {
        $latest = WeightRecord::query()
            ->where('cattle_id', $cattle->id)
            ->orderByDesc('weighed_at')
            ->first();

        $location = $this->resolveLocation($cattle);
        $genders = config('ranch.genders', []);
        $statuses = config('ranch.cattle_statuses', []);

        $ageMonths = null;
        if ($cattle->birth_date) {
            $ageMonths = (int) $cattle->birth_date->diffInMonths($today);
        }

        $daysSinceWeigh = $latest
            ? $latest->weighed_at->diffInDays($today)
            : null;

        return [
            'cattle_id' => $cattle->id,
            'ear_tag' => $cattle->ear_tag,
            'breed' => $cattle->breed,
            'gender' => $cattle->gender,
            'gender_label' => $genders[$cattle->gender] ?? $cattle->gender,
            'birth_date' => $cattle->birth_date?->toDateString(),
            'age_months' => $ageMonths,
            'mother_ear_tag' => $cattle->mother?->ear_tag,
            'status' => $cattle->status,
            'status_label' => $statuses[$cattle->status] ?? $cattle->status,
            'location_type' => $location['type'],
            'location_type_label' => $location['type_label'],
            'location_label' => $location['label'],
            'pasture_location' => $cattle->pasture_location,
            'fattening_lot_id' => $cattle->activeFatteningLotAssignment?->fattening_lot_id,
            'last_weight_kg' => $latest ? (float) $latest->weight_kg : null,
            'last_weighed_at' => $latest?->weighed_at?->toDateString(),
            'daily_weight_gain' => $latest?->daily_weight_gain !== null ? (float) $latest->daily_weight_gain : null,
            'days_since_weigh' => $daysSinceWeigh,
            'offspring_count' => (int) $cattle->offspring_count,
        ];
    }

    /**
     * @return array{type: string, type_label: string, label: string}
     */
    private function resolveLocation(Cattle $cattle): array
    {
        $lot = $cattle->activeFatteningLotAssignment?->fatteningLot;
        if ($lot) {
            return [
                'type' => 'fattening',
                'type_label' => 'Engorda',
                'label' => $lot->name,
            ];
        }

        if ($cattle->pasture_location) {
            return [
                'type' => 'pasture',
                'type_label' => 'Corral/pasto',
                'label' => $cattle->pasture_location,
            ];
        }

        return [
            'type' => 'unassigned',
            'type_label' => 'Sin ubicación',
            'label' => 'Sin ubicación',
        ];
    }

    /**
     * @param  list<array<string, mixed>>  $rows
     * @return list<array<string, mixed>>
     */
    private function groupByLocation(array $rows): array
    {
        $groups = [];
        foreach ($rows as $row) {
            $key = $row['location_type'].':'.$row['location_label'];
            if (! isset($groups[$key])) {
                $groups[$key] = [
                    'location_key' => $key,
                    'location_type' => $row['location_type'],
                    'location_type_label' => $row['location_type_label'],
                    'location_label' => $row['location_label'],
                    'head_count' => 0,
                ];
            }
            $groups[$key]['head_count']++;
        }

        $list = array_values($groups);
        usort($list, fn ($a, $b) => $b['head_count'] <=> $a['head_count'] ?: strcmp($a['location_label'], $b['location_label']));

        return $list;
    }

    /**
     * @param  list<array<string, mixed>>  $rows
     * @return list<array{key: string|null, head_count: int}>
     */
    private function groupCount(array $rows, string $field): array
    {
        $counts = [];
        foreach ($rows as $row) {
            $key = $row[$field] ?? '—';
            $key = $key === '' || $key === null ? '—' : (string) $key;
            $counts[$key] = ($counts[$key] ?? 0) + 1;
        }

        $list = [];
        foreach ($counts as $key => $count) {
            $list[] = ['key' => $key, 'head_count' => $count];
        }
        usort($list, fn ($a, $b) => $b['head_count'] <=> $a['head_count']);

        return $list;
    }

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