<?php

namespace App\Traits;

use stdClass;
use Carbon\Carbon;
use App\Models\Application\Loan;
use App\Models\Application\Group;
use App\Models\Bussiness\Collaborator;

trait ReportTrait
{
    private $loans = null, $startDate = null, $endDate = null;

    protected function createGroupItem($data, $isZone = false)
    {
        $item = new stdClass();

        $group = Group::findOrFail($data->fk_group);
        $promoter = Collaborator::findOrFail($group->fk_promoter);

        $item->fk_group = $group->id;
        $item->groupname = explode(' ', $promoter->firstname ?? '')[0] . ' ' . $group->groupname;

        $this->startDate = Carbon::createFromDate($data->reportdate)->subDay(2);
        $this->endDate = Carbon::createFromDate($data->reportdate)->addDays(4);

        $this->loans = Loan::where([
            ['fk_site', $data->fk_site],
            ['fk_office', $data->fk_office],
            ['fk_group', $data->fk_group],
            ['visible', true],
            ['available', true],
            ['is_active', true]
        ]);

        $item->reportdate = $data->reportdate->format('Y-m-d');
        $item->amountdue = $this->getDueAmount();
        $item->amountfailed = $this->getAmountByStatus('failed');
        $item->subtotalamount = $item->amountdue - $item->amountfailed;
        $item->amountrecovered = $this->getAmountByStatus('recovered');
        $item->totalamount = $item->subtotalamount + $item->amountrecovered;
        $item->incomingamount = $this->getAmountByStatus('incoming');
        $item->outgoingamount = $this->getAmountByStatus('outgoing');
        $item->extraweekamount = $this->getAmountByStatus('extraweek');
        $item->totaldeliveryamount = $item->totalamount + $item->extraweekamount - $item->incomingamount + $item->outgoingamount;
        $item->nextamountdue = $this->getDueAmount(true);
        $item->salesamount = $this->getSales();
        $item->commissionpercent = $isZone ? ($group->zone->executive->commissionpercent ?? 0) : ($promoter->commissionpercent ?? 0);
        $item->commissionamount = $item->salesamount * ($item->commissionpercent / 100);
        $item->specialsalesamount = $this->getSales(true);
        $item->nextdate = $data->reportdate->addWeek()->format('Y-m-d');

        return $item;
    }

    /**
     * Retrieve the total sales amount within a specified date range.
     *
     * This method calculates the total sales amount by summing the 'loanamount' 
     * of loans within the specified date range. It can filter the loans based 
     * on whether they are special or not, determined by the presence of a 
     * 'fk_collaborator'.
     *
     * @param bool $isSpecial Optional. If true, only include loans with a 
     *                        non-null 'fk_collaborator'. If false, only include 
     *                        loans with a null 'fk_collaborator'. Default is false.
     * 
     * @return float The total sales amount within the specified date range.
     */

    private function getSales($isSpecial = false)
    {
        $startDate = (clone $this->startDate)->format('Y-m-d');
        $endDate = (clone $this->endDate)->format('Y-m-d');

        $sales = (clone $this->loans)->whereBetween('loandate', [$startDate, $endDate])
            ->where('hasspecial', $isSpecial)->whereHas('status', function ($status) {
                $status->whereNotIn('statusname', ['SOLICITUD REESTRUCTURADA']);
            })->get()->sum('loanamount');

        return $sales;
    }

    private function getDueAmount($next = false)
    {
        $startDate = (clone $this->startDate)->format('Y-m-d');
        $endDate = (clone $this->endDate)->format('Y-m-d');

        $dueAmount = (clone $this->loans)->when(Carbon::now()->between($this->startDate, $this->endDate), function ($query) {
            $query->whereHas('status', fn($status) => $status->where('statusname', 'SOLICITUD ACTIVA'));
        })->whereHas('payments', function ($query) use ($next, $startDate, $endDate) {
            $query->when($next, function ($query) use ($startDate, $endDate) {
                $startDate = (clone $this->startDate)->addWeek()->format('Y-m-d');
                $endDate = (clone $this->endDate)->addWeek()->format('Y-m-d');

                $query->whereBetween('duedate', [$startDate, $endDate])
                    ->whereHas('status', fn($status) => $status->whereNotIn('statusname', ['ADELANTADO POR RENOVACION', 'ADELANTADO']));
            }, function ($query) {
                $query->whereBetween('duedate', [$this->startDate, $this->endDate])
                    ->whereHas('status', fn($status) => $status->whereNotIn('statusname', ['ADELANTADO POR RENOVACION', 'ADELANTADO']));
            });
        })->get()->sum(function ($loan) use ($startDate, $endDate, $next) {
            if ($next) {
                $startDate = (clone $this->startDate)->addWeek()->format('Y-m-d');
                $endDate = (clone $this->endDate)->addWeek()->format('Y-m-d');
            }

            $weeklyPayment = $loan->weeklypayment;

            if ($loan->loan_type->period === 30) {
                $payments = (clone $loan)->payments;

                foreach ($payments as $payment) {
                    if (Carbon::createFromTimestamp($payment->duedate)->between($startDate, $endDate)) {
                        $lastPayment = $payment;
                        break;
                    }
                }

                if (isset($lastPayment)) {
                    if ($loan->duration === $lastPayment->paymentnumber) {
                        $weeklyPayment += $loan->loanamount;
                    }
                }
            }

            return $weeklyPayment;
        });

        return $dueAmount;
    }

    private function getAmountByStatus($status)
    {
        $startDate = (clone $this->startDate)->format('Y-m-d');
        $endDate = (clone $this->endDate)->format('Y-m-d');

        if ($status == 'outgoing') {
            $startDate = (clone $this->startDate)->subWeek()->format('Y-m-d');
            $endDate = (clone $this->endDate)->subWeek()->format('Y-m-d');
        }

        $amount = (clone $this->loans)->whereHas('payments', function ($query) use ($status, $startDate, $endDate) {
            $query->when(in_array($status, ['failed', 'outgoing']), function ($query) use ($startDate, $endDate) {
                $query->whereBetween('duedate', [$startDate, $endDate]);
            }, function ($query) use ($startDate, $endDate) {
                $query->whereBetween('paymentdate', [$startDate, $endDate]);
            });
        });

        switch ($status) {
            case 'failed':
                $amount = $amount->whereHas('payments', fn($payments) =>
                $payments->whereBetween('duedate', [$startDate, $endDate])->whereHas('status', fn($status) => $status->whereIn('statusname', ['FALLA', 'INCOMPLETO', 'RECUPERADO'])))
                    ->get()->sum(function ($loan) {
                        return $loan->payments->sum(function ($payment) {
                            switch ($payment->status->statusname) {
                                case 'FALLA':
                                    return $payment->loan->weeklypayment;
                                    break;
                                case 'RECUPERADO':
                                    return $payment->payment;
                                    break;
                                case 'INCOMPLETO':
                                    return $payment->loan->weeklypayment - $payment->payment;
                                    break;
                                default:
                                    return 0;
                            }
                        });
                    });
                break;
            case 'recovered':
                $amount = $amount->whereHas('payments', fn($payments) => $payments
                    ->whereBetween('paymentdate', [$startDate, $endDate])
                    ->whereHas('status', fn($status) => $status->where('statusname', 'RECUPERADO')))->get()
                    ->sum(function ($loan) {
                        return $loan->payments->sum(function ($payment) {
                            return $payment->status->statusname === 'RECUPERADO' ? $payment->payment : 0;
                        });
                    });;

                break;
            case 'incoming':
                $amount = $amount->whereHas('payments', function ($payments) use ($startDate, $endDate) {
                    $payments->whereBetween('paymentdate', [$startDate, $endDate])
                        ->whereHas('status', fn($status) => $status->whereIn('statusname', ['ADELANTADO']));
                })->get()
                    ->sum(function ($loan) {
                        return $loan->payments->sum(function ($payment) {
                            return in_array($payment->status->statusname, ['ADELANTADO'])
                                && Carbon::createFromTimestamp($payment->paymentdate)->between($this->startDate, $this->endDate)
                                ? $payment->payment : 0;
                        });
                    });
                break;
            case 'outgoing':
                $amount = $amount->whereHas('payments', function ($payments) use ($startDate, $endDate) {
                    $payments->whereBetween('duedate', [$startDate, $endDate])
                        ->whereHas('status', fn($status) => $status->whereIn('statusname', ['ADELANTADO']));
                })->get()
                    ->sum(function ($loan) {
                        return $loan->payments->sum(function ($payment) {
                            return in_array($payment->status->statusname, ['ADELANTADO'])
                                && Carbon::createFromTimestamp($payment->duedate)->between($this->startDate, $this->endDate)
                                ? $payment->payment : 0;
                        });
                    });
                break;
            case 'extraweek':
                $amount = $amount->whereHas('payments', function ($payments) use ($startDate, $endDate) {
                    $payments->whereBetween('paymentdate', [$startDate, $endDate])->whereColumn('paymentnumber', '>', 'duration');
                })->sum('weeklypayment');
                break;
        }

        return $amount;
    }
}
