<?php

namespace App\Http\Controllers\Ranch;

use App\Http\Controllers\Controller;
use App\Models\ConfiguracionEmpresa;
use App\Services\RanchActiveCostAlertService;
use App\Services\RanchCalvingReportService;
use App\Services\RanchClosedLotsRankingService;
use App\Services\RanchInventoryReportService;
use App\Services\RanchOpenLotsGdpAlertService;
use App\Services\RanchOpenLotsProjectionService;
use App\Services\RanchAnimalProfitabilityService;
use App\Services\RanchMotherPerformanceService;
use App\Services\RanchPeriodReportService;
use App\Services\WeightPerformanceReportService;
use Carbon\Carbon;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\StreamedResponse;

class RanchReportController extends Controller
{
    public function __construct(
        private readonly RanchPeriodReportService $reportService,
        private readonly WeightPerformanceReportService $weightPerformanceService,
        private readonly RanchMotherPerformanceService $motherPerformanceService,
        private readonly RanchAnimalProfitabilityService $animalProfitabilityService,
        private readonly RanchActiveCostAlertService $activeCostAlertService,
        private readonly RanchClosedLotsRankingService $closedLotsRankingService,
        private readonly RanchInventoryReportService $inventoryReportService,
        private readonly RanchOpenLotsProjectionService $openLotsProjectionService,
        private readonly RanchCalvingReportService $calvingReportService,
        private readonly RanchOpenLotsGdpAlertService $openLotsGdpAlertService,
    ) {}

    public function openLotsGdpAlerts(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'min_gdp' => ['nullable', 'numeric', 'min:0'],
            'days_without_weigh' => ['nullable', 'integer', 'min:1', 'max:365'],
        ]);

        return response()->json([
            'data' => $this->openLotsGdpAlertService->generate(
                isset($validated['min_gdp']) ? (float) $validated['min_gdp'] : null,
                isset($validated['days_without_weigh']) ? (int) $validated['days_without_weigh'] : null,
            ),
        ]);
    }

    public function calvingsReport(Request $request): JsonResponse
    {
        return response()->json([
            'data' => $this->calvingReportService->generate($this->calvingFilters($request)),
        ]);
    }

    public function calvingsReportCsv(Request $request): StreamedResponse
    {
        $filters = $this->calvingFilters($request);
        $report = $this->calvingReportService->generate($filters);
        $suffix = ($filters['from'] ?? null) && ($filters['to'] ?? null)
            ? $filters['from'].'_'.$filters['to']
            : 'completo';

        return response()->streamDownload(function () use ($report) {
            echo "\xEF\xBB\xBF".$this->calvingReportService->toCsv($report);
        }, 'partos-'.$suffix.'.csv', [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    private function calvingFilters(Request $request): array
    {
        return $request->validate([
            'from' => ['nullable', 'date'],
            'to' => ['nullable', 'date', 'after_or_equal:from'],
            'mother_id' => ['nullable', 'integer', 'exists:cattle,id'],
            'pasture_id' => ['nullable', 'integer', 'exists:pastures,id'],
        ]);
    }

    public function openLotsProjection(): JsonResponse
    {
        return response()->json([
            'data' => $this->openLotsProjectionService->generate(),
        ]);
    }

    public function openLotsProjectionCsv(): StreamedResponse
    {
        $report = $this->openLotsProjectionService->generate();

        return response()->streamDownload(function () use ($report) {
            echo "\xEF\xBB\xBF".$this->openLotsProjectionService->toCsv($report);
        }, 'engorda-abierta-proyeccion.csv', [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    public function inventory(Request $request): JsonResponse
    {
        $validated = $this->inventoryFilters($request);

        return response()->json([
            'data' => $this->inventoryReportService->generate($validated),
        ]);
    }

    public function inventoryCsv(Request $request): StreamedResponse
    {
        $validated = $this->inventoryFilters($request);
        $report = $this->inventoryReportService->generate($validated);
        $suffix = $validated['status'] ?? 'active';

        return response()->streamDownload(function () use ($report) {
            echo "\xEF\xBB\xBF".$this->inventoryReportService->toCsv($report);
        }, 'inventario-'.$suffix.'.csv', [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    /**
     * @return array<string, mixed>
     */
    private function inventoryFilters(Request $request): array
    {
        return $request->validate([
            'status' => ['nullable', 'string', 'in:active,sold,dead,consumed,all'],
            'location_type' => ['nullable', 'string', 'in:fattening,pasture,unassigned'],
            'pasture_id' => ['nullable', 'integer', 'exists:pastures,id'],
            'pasture_location' => ['nullable', 'string', 'max:120'],
            'breed' => ['nullable', 'string', 'max:80'],
            'gender' => ['nullable', 'string', 'in:male,female'],
        ]);
    }

    public function periodBalance(Request $request): JsonResponse
    {
        $report = $this->buildReport($request);

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

    public function weightPerformance(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'min_gdp' => ['nullable', 'numeric', 'min:0'],
            'days_without_weigh' => ['nullable', 'integer', 'min:1', 'max:365'],
        ]);

        $report = $this->weightPerformanceService->generate(
            isset($validated['min_gdp']) ? (float) $validated['min_gdp'] : null,
            isset($validated['days_without_weigh']) ? (int) $validated['days_without_weigh'] : null,
        );

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

    public function animalProfitability(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'from' => ['nullable', 'date'],
            'to' => ['nullable', 'date', 'after_or_equal:from'],
            'status' => ['nullable', 'string', 'in:sold,active,all'],
        ]);

        $from = isset($validated['from']) ? Carbon::parse($validated['from']) : null;
        $to = isset($validated['to']) ? Carbon::parse($validated['to']) : null;

        return response()->json([
            'data' => $this->animalProfitabilityService->generate(
                $from,
                $to,
                $validated['status'] ?? 'sold',
            ),
        ]);
    }

    public function closedLotsRanking(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'from' => ['nullable', 'date'],
            'to' => ['nullable', 'date', 'after_or_equal:from'],
            'sort_by' => ['nullable', 'string', 'in:net_profit,feed_conversion,profit_per_kg'],
        ]);

        $from = isset($validated['from']) ? Carbon::parse($validated['from']) : null;
        $to = isset($validated['to']) ? Carbon::parse($validated['to']) : null;

        return response()->json([
            'data' => $this->closedLotsRankingService->generate(
                $from,
                $to,
                $validated['sort_by'] ?? 'net_profit',
            ),
        ]);
    }

    public function closedLotsRankingCsv(Request $request): StreamedResponse
    {
        $validated = $request->validate([
            'from' => ['nullable', 'date'],
            'to' => ['nullable', 'date', 'after_or_equal:from'],
            'sort_by' => ['nullable', 'string', 'in:net_profit,feed_conversion,profit_per_kg'],
        ]);

        $from = isset($validated['from']) ? Carbon::parse($validated['from']) : null;
        $to = isset($validated['to']) ? Carbon::parse($validated['to']) : null;

        $report = $this->closedLotsRankingService->generate(
            $from,
            $to,
            $validated['sort_by'] ?? 'net_profit',
        );

        $suffix = ($report['from'] && $report['to']) ? $report['from'].'_'.$report['to'] : 'completo';

        return response()->streamDownload(function () use ($report) {
            echo "\xEF\xBB\xBF".$this->closedLotsRankingService->toCsv($report);
        }, 'lotes-cerrados-'.$suffix.'.csv', [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    public function activeCostAlerts(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'max_cost_per_kg' => ['nullable', 'numeric', 'min:0'],
            'min_weight_gain_kg' => ['nullable', 'numeric', 'min:0'],
        ]);

        return response()->json([
            'data' => $this->activeCostAlertService->generate(
                isset($validated['max_cost_per_kg']) ? (float) $validated['max_cost_per_kg'] : null,
                isset($validated['min_weight_gain_kg']) ? (float) $validated['min_weight_gain_kg'] : null,
            ),
        ]);
    }

    public function animalProfitabilityPdf(Request $request)
    {
        $validated = $request->validate([
            'from' => ['nullable', 'date'],
            'to' => ['nullable', 'date', 'after_or_equal:from'],
            'status' => ['nullable', 'string', 'in:sold,active,all'],
        ]);

        $from = isset($validated['from']) ? Carbon::parse($validated['from']) : null;
        $to = isset($validated['to']) ? Carbon::parse($validated['to']) : null;

        $report = $this->animalProfitabilityService->generate(
            $from,
            $to,
            $validated['status'] ?? 'sold',
        );

        $empresa = ConfiguracionEmpresa::obtenerConfiguracion()?->nombre_empresa ?? 'Control Ganadero';
        $suffix = ($report['from'] && $report['to']) ? $report['from'].'_'.$report['to'] : 'completo';

        $pdf = \Barryvdh\DomPDF\Facade\Pdf::loadView('pdf.ranch_animal_profitability', [
            'report' => $report,
            'empresa' => $empresa,
        ])->setPaper('letter', 'landscape');

        return $pdf->download('rentabilidad-'.$suffix.'.pdf');
    }

    public function animalProfitabilityCsv(Request $request): StreamedResponse
    {
        $validated = $request->validate([
            'from' => ['nullable', 'date'],
            'to' => ['nullable', 'date', 'after_or_equal:from'],
            'status' => ['nullable', 'string', 'in:sold,active,all'],
        ]);

        $from = isset($validated['from']) ? Carbon::parse($validated['from']) : null;
        $to = isset($validated['to']) ? Carbon::parse($validated['to']) : null;

        $report = $this->animalProfitabilityService->generate(
            $from,
            $to,
            $validated['status'] ?? 'sold',
        );

        $suffix = ($report['from'] && $report['to'])
            ? $report['from'].'_'.$report['to']
            : 'completo';

        return response()->streamDownload(function () use ($report) {
            echo "\xEF\xBB\xBF".$this->animalProfitabilityService->toCsv($report);
        }, 'rentabilidad-'.$suffix.'.csv', [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    public function motherPerformance(Request $request): JsonResponse
    {
        $validated = $request->validate([
            'high_mortality_pct' => ['nullable', 'numeric', 'min:0', 'max:100'],
        ]);

        $report = $this->motherPerformanceService->generate(
            isset($validated['high_mortality_pct']) ? (float) $validated['high_mortality_pct'] : null,
        );

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

    public function periodBalanceCsv(Request $request): StreamedResponse
    {
        $report = $this->buildReport($request);
        $filename = 'balance-'.$report['from'].'_'.$report['to'].'.csv';

        return response()->streamDownload(function () use ($report) {
            echo "\xEF\xBB\xBF".$this->reportService->toCsv($report);
        }, $filename, [
            'Content-Type' => 'text/csv; charset=UTF-8',
        ]);
    }

    public function periodBalancePdf(Request $request)
    {
        $report = $this->buildReport($request);
        $empresa = ConfiguracionEmpresa::obtenerConfiguracion()?->nombre_empresa ?? 'Control Ganadero';

        $pdf = \Barryvdh\DomPDF\Facade\Pdf::loadView('pdf.ranch_period_balance', [
            'report' => $report,
            'empresa' => $empresa,
        ])->setPaper('letter', 'portrait');

        return $pdf->download('balance-'.$report['from'].'_'.$report['to'].'.pdf');
    }

    /**
     * @return array<string, mixed>
     */
    private function buildReport(Request $request): array
    {
        $validated = $request->validate([
            'from' => ['required', 'date'],
            'to' => ['required', 'date', 'after_or_equal:from'],
        ]);

        return $this->reportService->generate(
            Carbon::parse($validated['from']),
            Carbon::parse($validated['to'])
        );
    }
}
