<?php

namespace App\Http\Resources;

use Carbon\Carbon;
use App\Models\Loan;
use App\Helpers\Helper;
use App\Models\Holiday;
use App\Models\AccReceivables;
use App\Helpers\CustomDateFilter;
use Illuminate\Support\Facades\DB;
use App\Models\AccReceivableDetails;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\Resources\Json\JsonResource;

class SummaryResource extends JsonResource
{
    /**
     * @var array|null Date range filter
     */
    protected ?array $dateRange = null;
    
    /**
     * @var string|null Month filter
     */
    protected ?string $monthFilter = null;
    
    /**
     * @var string|null As of date filter
     */
    protected ?string $asOfDate = null;

    /**
     * Initialize filters on construction
     */

    /**
     * @param mixed $resource
     */
    public function __construct($resource)
    {
        parent::__construct($resource);
        $this->initializeFilters();
    }

    /**
     * Initialize and validate filter parameters
     */
    protected function initializeFilters(): void
    {
        $this->dateRange   = request()->date ?? [];
        $this->monthFilter = empty(request()->month) ? null : date('Y-m', strtotime(request()->month));
        $this->asOfDate    = request()->as_of_date ?? '';
    }

    /**
     * Transform the resource into an array.
     *
     * @param \Illuminate\Http\Request $request
     * @return array
     */
    public function toArray($request): array
    {
        $loans = $this->getBaseLoans();
        $latePayment = $this->getLatePayment($loans['balance_principal']);
        $collectionPayment = $this->getCollections();

        return array_merge(
            $this->getBasicInfo(),
            $loans,
            $latePayment,
            $collectionPayment,
        );
    }

    protected function getBasicInfo(): array
    {
        return [
            'id'          => $this->id,
            'co_id'       => $this->id,
            'code'        => $this->code,
            'co_name'     => $this->name,
            'branch_name' => $this->branchs->name_km ?? '',
        ];
    }

    protected function getBaseLoans(): array
    {
        $activeLoans   = $this->getLoansQuery(1)->whereIn('loan_status_id', [2,3]);
        $activeClients = $this->getLoansQuery(2)->where('loan_status_id', 2);
        return [
            'total_client'     => $activeLoans->count(),
            'total_client_new' => $this->getNewLoansCount(),
            'sum_principal'    => $activeLoans->sum('loan_out'),
            'sum_client_new'   => $this->getNewLoansSum(),
            'client_active'    => $activeClients->count(),
            'client_inactive'  => $this->getLoansQuery(2)->where('loan_status_id', 3)->count(),
            'client_complete'  => $this->getLoansQuery(2)->where('loan_status_id', 3)->count(),
            'balance_principal'=> $activeClients->get()->sum('balance_principal'),
            'total_service'    => $activeLoans->sum('total_service'),
        ];
    }

    protected function getLatePayment(float $balancePrincipal): array
    {
        $lateCondition = $this->getLateConditions();
        return [
            'count_day_late'  => $lateCondition['all']['count'] ?? 0,
            'amount_day_late' => $lateCondition['all']['amount'] ?? 0,
            'total_par_late'  => $this->calculatePar($lateCondition['all']['amount'] ?? 0, $balancePrincipal),
            // Less than or equal 30 days
            'count_late_lte_30day'  => $lateCondition['lte30']['count'] ?? 0,
            'amount_late_lte_30day' => $lateCondition['lte30']['amount'] ?? 0,
            'par_late_lte_30day'    => $this->calculatePar($lateCondition['lte30']['amount'] ?? 0, $balancePrincipal),
            // More than 30 days
            'count_late_more_than_30day'  => $lateCondition['gt30']['count'] ?? 0,
            'amount_late_more_than_30day' => $lateCondition['gt30']['amount'] ?? 0,
            'par_late_more_than_30day'    => $this->calculatePar($lateCondition['gt30']['amount'] ?? 0, $balancePrincipal),
            // Term day specific
            'count_late_term_of_days' => $this->getLateConditions(1)['all']['count'] ?? 0,
            'count_late_more_than_30day_of_day' => $this->getLateConditions(1)['gt30']['count'] ?? 0,
        ];
    }

    protected function getCollections(): array
    {
        $normalClient   = $this->getAccDetailSummary(1);
        $writeOffClient = $this->getAccDetailSummary(2);
        $total_serivce  = $this->getLoansQuery(1)->whereIn('loan_status_id', [2,3])->sum('total_service');
        $total_paid     = $total_serivce + $normalClient->paid_interest + $normalClient->paid_operating_service + $normalClient->paid_penalty;

        return [
            'paid_interest'          => $normalClient->paid_interest ?? 0,
            'paid_principal'         => $normalClient->paid_principal ?? 0,
            'paid_operating_service' => $normalClient->paid_operating_service ?? 0,
            'paid_penalty'           => $normalClient->paid_penalty ?? 0,
            'total_write_off'        => ($writeOffClient->received ?? 0) + ($writeOffClient->paid_penalty ?? 0),
            'total_paid'             => $total_paid??0,
        ];
    }

    protected function getLoanFilters(int $type)
    {
        return Loan::query()
            ->where('co_id', $this->id)
            ->whereIn('branch_id', Helper::byBranch())
            ->where('currency_id', Helper::byCurrency())
            ->when(!empty(request()->client_type), function ($query) {
                $query->where('client_type', request()->client_type);
            })
            ->when(!empty(request()->co_id), function ($query) {
                $query->whereIn('co_id', request()->co_id);
            })
            ->where(function($query) use($type){
                $this->applyDateFilters($query,$type,'installment_date');
            });
    }

    protected function getLoansQuery(int $type): Builder
    {
        return $this->getLoanFilters($type);
    }

    protected function getNewLoansCount(): int
    {
        return $this->getNewLoansQuery()->count();
    }

    protected function getNewLoansSum(): float
    {
        return $this->getNewLoansQuery()->sum('loan_out');
    }

    protected function getNewLoansQuery(): Builder
    {
        return $this->getLoanFilters(1)
            ->whereIn('loan_status_id', [2, 3, 5, 7])
            ->whereNotIn('client_id', function ($query) {
                $query->select('client_id')
                    ->from('loans')
                    ->whereIn('loan_status_id', [2, 3, 5, 7])
                    ->groupBy('client_id')
                    ->havingRaw('COUNT(client_id) > 1');
            });
    }

    protected function applyDateFilters(Builder $query, int $type, string $field): void
    {
        $query->when(!empty($this->dateRange), function ($q) use ($field) {
            $q->whereBetween(DB::raw("DATE_FORMAT({$field}, '%Y-%m-%d')"), $this->dateRange);
        })
        ->when(!empty($this->monthFilter), function ($q) use ($field) {
            $q->where(DB::raw("DATE_FORMAT({$field}, '%Y-%m')"), $this->monthFilter);
        })
        ->when(!empty($this->asOfDate), function ($q) use ($field) {
            $q->where(DB::raw("DATE_FORMAT({$field}, '%Y-%m-%d')"), '<=', date('Y-m-d', strtotime($this->asOfDate)));
        })
        ->when(empty($this->dateRange) && empty($this->monthFilter) && empty($this->asOfDate) && $type==1, function ($q) use ($field) {
            $q->whereBetween(DB::raw("DATE_FORMAT({$field}, '%Y-%m-%d')"),CustomDateFilter::thisMonth());
        });
    }

    protected function getLateConditions(?int $termType = null): array
    {
        $result = [
            'lte30' => ['count' => 0, 'amount' => 0], 
            'gt30'  => ['count' => 0, 'amount' => 0], 
            'all'   => ['count' => 0, 'amount' => 0]
        ];
        $lateData    = $this->getClientLateData($termType);
        $lateDetails = $this->processLateData($lateData);
        
        foreach ($lateDetails as $detail) {
            if ($detail['qty'] > 0) {
                $result['all']['count']++;
                $result['all']['amount'] += $detail['amount'];
                
                if ($detail['qty'] < 30) {
                    $result['lte30']['count']++;
                    // $result['lte30']['amount'] += $detail['amount'];
                    $result['lte30']['amount'] += $detail['balance_principal'];
                } 
                else if($detail['qty'] > 30){
                    $result['gt30']['count']++;
                    // $result['gt30']['amount'] += $detail['amount'];
                    $result['gt30']['amount'] += $detail['balance_principal'];
                } 
                else{
                    $result['all']['count']++;
                    $result['all']['amount'] += $detail['amount'];
                }
            }
        }
        return $result;
    }

    protected function getClientLateData(?int $termType): array
    {
        $holidays = $this->getHolidayDates();

        return AccReceivables::query()
            ->where('co_id', $this->id)
            ->whereIn('branch_id', Helper::byBranch())
            ->where('currency_id', Helper::byCurrency())
            ->whereIn('payment_status_id', [2, 3])
            ->whereNull('deleted_at')
            ->where(function ($query) use ($termType) {
                if (!empty(request()->date) && request()->date[0]!='null') {
                    $query->whereBetween(DB::raw("DATE_FORMAT(payment_date, '%Y-%m-%d')"),request()->date);
                } else{
                    $query->where('payment_date', '<', date('Y-m-d'));
                }
                if (!empty(request()->co_id)) {
                    $query->whereIn('co_id', request()->co_id);
                }
                if ($termType === 1) {
                    $query->whereHas('loan', function ($sub) {
                        $sub->where('term_type', 1);
                    });
                }
            })
            ->whereHas('loan', function ($query) {
                $query->whereIn('loan_status_id', [2, 5])
                    ->when(!empty(request()->client_type), function ($q) {
                        $q->where('client_type', request()->client_type);
                    });
            })
            ->get()
            ->map(function ($query) use ($holidays) {
                return $this->calculateLateDays($query, $holidays);
            })
            ->toArray();
    }
    
    /**
     * @param mixed $receivable
     * @param array $holidays
     * @return array
     */
    protected function calculateLateDays($receivable, array $holidays): array
    {
        $totalLate   = 0;
        $today       = Carbon::now()->startOfDay();
        $paymentDate = Carbon::parse($receivable->date_late)->startOfDay();
        $termCondiId = $receivable->loan->term_condi_id ?? 0;
        
        while ($paymentDate->lt($today)) {
            $isHoliday = in_array($paymentDate->format('Y-m-d'), $holidays);
            
            if ($this->isWorkingDay($paymentDate, $termCondiId, $isHoliday)) {
                $totalLate++;
            }
            $paymentDate->addDay();
        }
        $unpaid_principal  = $receivable->principal - $receivable->accReceivableDetails->sum('paid_principal')??0;
        $balance_principal = $receivable->loan->balance_principal??0;
        return [
            'id'     => $receivable->id,
            'loan_id'=> $receivable->loan_id,
            'qty'    => $totalLate,
            'amount' => $unpaid_principal ?? 0,
            'balance_principal' => $balance_principal ?? 0,
        ];
    }

    protected function processLateData(array $lateData): array
    {
        $grouped = [];
        foreach ($lateData as $data) {
            $loanId = $data['loan_id'];
            if (!isset($grouped[$loanId])) {
                $grouped[$loanId] = [
                    'qty'     => $data['qty'],
                    'amount'  => $data['amount'],
                    'id'      => $data['id'],
                    'loan_id' => $loanId,
                    'balance_principal' => $data['balance_principal'],
                ];
            } 
            // else {
            //     $grouped[$loanId]['qty'] = max($grouped[$loanId]['qty'], $data['qty']);
            //     $grouped[$loanId]['amount'] += $data['amount'];
            //     $grouped[$loanId]['balance_principal'] += $data['balance_principal'];
            // }
        }
        return array_values($grouped);
    }

    protected function isWorkingDay(Carbon $date, int $termCondiId, bool $isHoliday): bool
    {
        if ($isHoliday) {
            return false;
        }
        switch ($termCondiId) {
            case 2: // Exclude Saturday and Sunday
                return !$date->isSaturday() && !$date->isSunday();
            case 3: // Exclude Sunday only
                return !$date->isSunday();
            case 4: // No weekend exclusions
                return true;
            default:
                return true;
        }
    }

    protected function getHolidayDates(): array
    {
        return Holiday::select('date')
            ->get()
            ->map(fn($holiday) => Carbon::parse($holiday->date)->format('Y-m-d'))
            ->toArray();
    }

    protected function getAccDetailSummary(int $clientType): object
    {
        return AccReceivableDetails::query()
            ->where('co_id', $this->id)
            ->whereIn('branch_id', Helper::byBranch())
            ->where('currency_id', Helper::byCurrency())
            ->where('client_type', $clientType)
            ->when(!empty(request()->date) && request()->date[0]!='null', function ($query) {
                $query->whereBetween(DB::raw("DATE_FORMAT(received_date, '%Y-%m-%d')"),request()->date);
            })
            ->when(!empty(request()->co_id), function ($query) {
                $query->whereIn('co_id', request()->co_id);
            })
            ->where(function($query){
                $this->applyDateFilters($query,1,'received_date');
            })
            ->selectRaw("
                SUM(received) AS received,
                SUM(paid_interest) AS paid_interest,
                SUM(paid_principal) AS paid_principal,
                SUM(paid_operating_service) AS paid_operating_service,
                SUM(paid_penalty) AS paid_penalty
            ")
            ->first() ?? (object) [
                'received' => 0,
                'paid_interest' => 0,
                'paid_principal' => 0,
                'paid_operating_service' => 0,
                'paid_penalty' => 0,
            ];
    }

    protected function calculatePar(float $amount, float $balancePrincipal): float
    {
        return ($amount > 0 && $balancePrincipal > 0) ? ($amount / $balancePrincipal) * 100 : 0;
    }
}