<?php

namespace App\Reports\CashFlowProjection;

use App\Reports\BaseReport;
use Carbon\Carbon;
use Carbon\CarbonInterface;
use Illuminate\Support\Facades\DB;

// PRC-820 (v2) — İleriye dönük nakit-akış projeksiyonu: tekrarlayan gelir
// (aktif servis yenilemeleri, gerçek zamanlama) − tekrarlayan gider
// (RecurringExpense) → N ay aylık net + kümülatif. Accrual değil cash-timing.
class CashFlowProjection extends BaseReport
{
    private const CYCLE_MONTHS = [
        'monthly' => 1,
        'quarterly' => 3,
        'semi_annually' => 6,
        'annually' => 12,
        'biennially' => 24,
        'triennially' => 36,
    ];

    public function getName(): string
    {
        return __('admin/reports.cashflow_name');
    }

    public function getDescription(): string
    {
        return __('admin/reports.cashflow_description');
    }

    public function getCategory(): string
    {
        return __('admin/reports.cashflow_category');
    }

    public function getIcon(): string
    {
        return 'fas fa-chart-line';
    }

    public function getRouteName(): string
    {
        return 'cash-flow-projection';
    }

    public function getRequiredPermission(): string
    {
        return 'generate reports';
    }

    public function getData(array $filters = []): array
    {
        $months = (int) ($filters['months'] ?? 12);
        $months = max(1, min(36, $months));
        $currencyId = $this->resolveCurrencyId($filters['currency'] ?? 'all');

        $today = Carbon::now()->startOfMonth();
        $horizonEnd = $today->copy()->addMonths($months - 1)->endOfMonth();

        // Ay kovalarını hazırla.
        $buckets = [];
        for ($i = 0; $i < $months; $i++) {
            $key = $today->copy()->addMonths($i)->format('Y-m');
            $buckets[$key] = ['income' => 0.0, 'expense' => 0.0];
        }

        $this->projectIncome($buckets, $currencyId, $today, $horizonEnd);
        $this->projectExpenses($buckets, $currencyId, $today, $horizonEnd);

        $cumulative = 0.0;
        $data = [];
        foreach ($buckets as $period => $b) {
            $net = $b['income'] - $b['expense'];
            $cumulative += $net;
            $data[] = [
                'period' => $period,
                'projected_income' => round($b['income'], 2),
                'projected_expense' => round($b['expense'], 2),
                'net_cashflow' => round($net, 2),
                'cumulative' => round($cumulative, 2),
            ];
        }

        return $data;
    }

    /** Aktif servislerin yenileme gelirini projekte et (para birimi müşteriden). */
    private function projectIncome(array &$buckets, ?int $currencyId, CarbonInterface $today, CarbonInterface $horizonEnd): void
    {
        $query = DB::table('client_products')
            ->join('clients', 'client_products.client_id', '=', 'clients.id')
            ->where('client_products.status', 'active')
            ->where('client_products.billingcycle', '!=', 'one_time')
            ->whereNotNull('client_products.next_due_date')
            ->where('client_products.recurring_amount', '>', 0);
        if ($currencyId) {
            $query->where('clients.currency_id', $currencyId);
        }

        $query->select(
            'client_products.recurring_amount',
            'client_products.billingcycle',
            'client_products.next_due_date'
        )->orderBy('client_products.id')->chunk(500, function ($rows) use (&$buckets, $today, $horizonEnd) {
            foreach ($rows as $p) {
                $cycle = self::CYCLE_MONTHS[$p->billingcycle] ?? null;
                if (! $cycle) {
                    continue;
                }
                $this->projectInto($buckets, 'income', Carbon::parse($p->next_due_date), $cycle, (float) $p->recurring_amount, $today, $horizonEnd, null);
            }
        });
    }

    /** Aktif tekrarlayan giderleri projekte et. */
    private function projectExpenses(array &$buckets, ?int $currencyId, CarbonInterface $today, CarbonInterface $horizonEnd): void
    {
        $query = DB::table('recurring_expenses')->where('is_active', true);
        if ($currencyId) {
            $query->where('currency_id', $currencyId);
        }

        $query->select('amount', 'frequency', 'next_run_date', 'end_date')
            ->orderBy('id')->chunk(500, function ($rows) use (&$buckets, $today, $horizonEnd) {
                foreach ($rows as $r) {
                    $cycle = self::CYCLE_MONTHS[$r->frequency] ?? null;
                    if (! $cycle) {
                        continue;
                    }
                    $end = $r->end_date ? Carbon::parse($r->end_date) : null;
                    $this->projectInto($buckets, 'expense', Carbon::parse($r->next_run_date), $cycle, (float) $r->amount, $today, $horizonEnd, $end);
                }
            });
    }

    /**
     * Bir tekrarlayan kalemin oluşumlarını ufuk içindeki aylara dağıt.
     * Geçmiş vade ilk gelecekteki oluşuma ilerletilir; end_date sonrası durur.
     */
    private function projectInto(array &$buckets, string $field, CarbonInterface $start, int $cycleMonths, float $amount, CarbonInterface $today, CarbonInterface $horizonEnd, ?CarbonInterface $endDate): void
    {
        $due = $start->copy()->startOfDay();
        $guard = 0;

        // Geçmişteyse ilk >= bu ay oluşuma ilerlet.
        while ($due->lt($today) && $guard < 800) {
            $due = $due->copy()->addMonthsNoOverflow($cycleMonths);
            $guard++;
        }

        while ($due->lte($horizonEnd) && $guard < 800) {
            if ($endDate && $due->gt($endDate)) {
                break;
            }
            $key = $due->format('Y-m');
            if (isset($buckets[$key])) {
                $buckets[$key][$field] += $amount;
            }
            $due = $due->copy()->addMonthsNoOverflow($cycleMonths);
            $guard++;
        }
    }

    private function resolveCurrencyId(string $currency): ?int
    {
        if ($currency === 'all') {
            return null;
        }

        return DB::table('currencies')->where('code', $currency)->value('id');
    }

    public function getColumns(): array
    {
        return [
            'period' => ['title' => __('admin/reports.cashflow_column_period'), 'type' => 'string', 'sortable' => true],
            'projected_income' => ['title' => __('admin/reports.cashflow_column_income'), 'type' => 'currency', 'sortable' => true],
            'projected_expense' => ['title' => __('admin/reports.cashflow_column_expense'), 'type' => 'currency', 'sortable' => true],
            'net_cashflow' => ['title' => __('admin/reports.cashflow_column_net'), 'type' => 'currency', 'sortable' => true],
            'cumulative' => ['title' => __('admin/reports.cashflow_column_cumulative'), 'type' => 'currency', 'sortable' => true],
        ];
    }

    public function getFilters(): array
    {
        return [
            'months' => [
                'type' => 'select',
                'label' => __('admin/reports.cashflow_filter_months'),
                'required' => false,
                'options' => [
                    '3' => __('admin/reports.cashflow_months_3'),
                    '6' => __('admin/reports.cashflow_months_6'),
                    '12' => __('admin/reports.cashflow_months_12'),
                    '24' => __('admin/reports.cashflow_months_24'),
                ],
                // NOT: PHP sayısal string dizi anahtarını int'e çevirir ('12'→12);
                // detail.blade strict `===` ile karşılaştırır → default int olmalı.
                'default' => 12,
            ],
            'currency' => [
                'type' => 'select',
                'label' => __('admin/reports.cashflow_filter_currency'),
                'required' => false,
                'options' => [
                    'all' => __('admin/reports.cashflow_currency_all'),
                    'TRY' => 'TRY',
                    'USD' => 'USD',
                    'EUR' => 'EUR',
                ],
                'default' => 'all',
            ],
        ];
    }

    public function hasChart(): bool
    {
        return true;
    }

    public function getChartData(array $filters = []): ?array
    {
        $data = $this->getData($filters);

        return [
            'labels' => array_column($data, 'period'),
            'datasets' => [
                [
                    'label' => __('admin/reports.cashflow_column_income'),
                    'data' => array_column($data, 'projected_income'),
                    'backgroundColor' => 'rgba(75, 192, 192, 0.4)',
                    'borderColor' => 'rgba(75, 192, 192, 1)',
                    'borderWidth' => 2,
                ],
                [
                    'label' => __('admin/reports.cashflow_column_expense'),
                    'data' => array_column($data, 'projected_expense'),
                    'backgroundColor' => 'rgba(255, 99, 132, 0.4)',
                    'borderColor' => 'rgba(255, 99, 132, 1)',
                    'borderWidth' => 2,
                ],
                [
                    'label' => __('admin/reports.cashflow_column_cumulative'),
                    'data' => array_column($data, 'cumulative'),
                    'backgroundColor' => 'rgba(54, 162, 235, 0.4)',
                    'borderColor' => 'rgba(54, 162, 235, 1)',
                    'borderWidth' => 2,
                ],
            ],
        ];
    }

    public function getChartType(): string
    {
        return 'line';
    }
}
