<?php

namespace App\Http\Controllers\api;

use Exception;
use Carbon\Carbon;
use App\Traits\LoanTrait;
use App\Traits\PaymentTrait;
use App\Models\Application\Loan;
use App\Models\Application\Status;
use App\Models\Application\Payment;
use App\Http\Controllers\Controller;
use App\Http\Requests\PaymentRequest;

class PaymentController extends Controller
{
    use PaymentTrait, LoanTrait;

    protected $MESSAGES = [
        'ALL' => 'WARNING.PAYMENT_ALL',
        'PAYMENT' => 'WARNING.PAYMENT_ADD',
        'EXTRA' => 'WARNING.PAYMENT_EXTRA',
        'UPDATE' => 'WARNING.PAYMENT_UPDATE',
        'REMOVE' => 'WARNING.PAYMENT_REMOVE',
        'DESTROY' => 'WARNING.PAYMENT_DESTROY',
        'CURRENT' => 'WARNING.PAYMENT_CURRENT',
        'NOT_EXTRA' => 'WARNING.PAYMENT_EXTRA',
        'NOT_ADDITIONAL' => 'WARNING.PAYMENT_ADDITIONAL',
        'EXTRA_DUPLICATED' => 'WARNING.PAYMENT_EXTRA_DUPLICATED',
    ];

    public function __construct()
    {
        $this->CHANNEL = 'payment_errors';
    }

    public function update(PaymentRequest $request)
    {
        try {
            $payment = Payment::findOrFail($request->id);

            $payment->update([
                'fk_status' => $this->getStatus($request, $payment)->id,
                'payment' => $request->payment,
                'paymentdate' => $request->paymentdate,
                'fk_userupdater' => $this->user()->id
            ]);

            $payment = Payment::findOrFail($request->id);
            $loan = Loan::findOrFail($payment->fk_loan);

            // if ($request->payment == 0) {
            //     $this->createExtra($loan);
            // }

            $this->determineLoanStatus($payment);
            $this->determineClientStatus($loan);
            $this->determineEndorsementStatus($loan);

            return $this->responseJson(null, true, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES['UPDATE'], null, 501);
        }
    }

    public function destroy(PaymentRequest $request)
    {
        try {
            $payment = Payment::findOrFail($request->id)->load(['loan']);
            $period = $payment->loan->loan_type->period;

            if ($period != 30) {
                if ($payment->paymentnumber <= $payment->loan->duration) {
                    return $this->responseJson($this->MESSAGES['NOT_EXTRA'], null, 501);
                }
            }

            $payment->update([
                'fk_userupdater' => $this->user()->id,
                'is_active' => false
            ]);

            if ($period === 30) {
                $payment->loan->update([
                    'duration' => $period === 30 ? $payment->loan->duration - 1 : $payment->loan->duration,
                    'fk_userupdater' => $this->user()->id,
                ]);
            }

            $this->determineLoanStatus($payment);

            return $this->responseJson(null, true, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES['DESTROY'], null, 501);
        }
    }

    public function remove(PaymentRequest $request)
    {
        try {
            $payment = Payment::findOrFail($request->id);
            $defaultStatus = Status::where('statusname', 'PENDIENTE')->first();

            $payment->update([
                'fk_status' => $defaultStatus->id,
                'payment' => null,
                'paymentdate' => null,
                'fk_userupdater' => $this->user()->id
            ]);

            $loan = Loan::findOrFail($payment->fk_loan);

            $this->determineLoanStatus($payment);
            $this->determineClientStatus($loan);
            $this->determineEndorsementStatus($loan);

            return $this->responseJson(null, true, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES['REMOVE'], null, 501);
        }
    }

    public function createExtraPayment(PaymentRequest $request)
    {
        try {
            $loan = Loan::findOrFail($request->fk_loan)->loadCount(['payments']);

            if ($loan->payments_count > $loan->duration) {
                return $this->responseJson($this->MESSAGES['EXTRA_DUPLICATED'], null, 501);
            }

            $this->createExtra($loan);

            return $this->responseJson(null, true, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES['EXTRA'], null, 501);
        }
    }

    public function payCurrent(PaymentRequest $request)
    {
        try {
            $loan = Loan::findOrFail($request->fk_loan)->load(['loan_type']);
            $currenDate = Carbon::now();

            if ($loan->loan_type->alloweddays != 'ALL') {
                $currenDate = $this->adjustCurrentDate($loan->loan_type->alloweddays);
            }

            $initDate = (clone $currenDate)->addDays(-2)->format('Y-m-d');
            $endDate = (clone $currenDate)->addDays(+4)->format('Y-m-d');

            $loan->load(['payments' => fn($payments) => $payments->whereBetween('duedate', [$initDate, $endDate])]);

            if ($loan->payments) {
                $payment = $loan->payments->first();

                $defaultStatus = Status::where('statusname', 'PUNTUAL')->first();

                $payment->update([
                    'fk_status' => $defaultStatus->id,
                    'payment' => $loan->weeklypayment,
                    // 'paymentdate' => Carbon::now()->format('Y-m-d'),
                    'paymentdate' => Carbon::createFromTimestamp($payment->duedate)->format('Y-m-d'),
                    'fk_userupdater' => $this->user()->id
                ]);
            }

            $this->determineLoanStatus($payment);
            $this->determineClientStatus($loan);
            $this->determineEndorsementStatus($loan);

            return $this->responseJson(null, true, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES['CURRENT'], null, 501);
        }
    }

    public function payLoan(PaymentRequest $request)
    {
        try {
            $loan = Loan::findOrFail($request->fk_loan);
            $loan->load(['payments' => fn($payments) => $payments
                ->where('is_active', true)
                ->where('payment', '<', $loan->weeklypayment)
                ->orWhereNull('payment'), 'loan_type']);

            if ($loan->payments) {
                $defaultStatus = Status::where('statusname', 'PUNTUAL')->first();
                $recoveredStatus = Status::where('statusname', 'RECUPERADO')->first();

                foreach ($loan->payments as $payment) {
                    if (in_array($payment->status->statusname, ['RECUPERADO', 'FALLA', 'INCOMPLETO'])) {
                        $payment->update([
                            'payment' =>  $loan->loan_type->period == 30 && $payment->paymentnumber == $loan->duration ? $loan->loanamount + $loan->weeklypayment : $loan->weeklypayment,
                            'paymentdate' => Carbon::createFromTimestamp($payment->duedate)->format('Y-m-d'),
                            'fk_status' => $recoveredStatus->id,
                            'fk_userupdater' => $this->user()->id
                        ]);
                    } else {
                        $payment->update([
                            'payment' =>  $loan->loan_type->period == 30 && $payment->paymentnumber == $loan->duration ? $loan->loanamount + $loan->weeklypayment : $loan->weeklypayment,
                            'paymentdate' => Carbon::createFromTimestamp($payment->duedate)->format('Y-m-d'),
                            'fk_status' => $defaultStatus->id,
                            'fk_userupdater' => $this->user()->id
                        ]);
                    }
                }

                $loanDefaultStatus = Status::where('statusname', 'SOLICITUD FINALIZADA')->first();
                $loan->update([
                    'fk_status' =>  $loanDefaultStatus->id,
                    'fk_userupdater' => $this->user()->id
                ]);

                $this->determineClientStatus($loan);
                $this->determineEndorsementStatus($loan);
            }

            return $this->responseJson(null, true, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES['ALL'], null, 501);
        }
    }

    protected function addPayment(PaymentRequest $request)
    {
        try {
            $loan = Loan::findOrFail($request->fk_loan)->load(['loan_type']);

            if ($loan->loan_type->period != 30) {
                return $this->responseJson($this->MESSAGES['NOT_ADDITIONAL'], null, 501);
            }

            $loanEndDate = Carbon::createFromTimestamp($loan->loanenddate)->addMonth()->format('Y-m-d');

            $paymentDefaultStatus = Status::where('statusname', 'PENDIENTE')->first();

            Payment::create([
                'fk_loan' => $loan->id,
                'paymentnumber' => $loan->payments()->count() + 1,
                'duedate' => $loanEndDate,
                'fk_status' => $paymentDefaultStatus->id,
                'fk_usercreator' => $this->user()->id,
            ]);

            $loan->update([
                'loanenddate' => $loanEndDate,
                'duration' => $loan->payments()->count(),
                'fk_userupdater' => $this->user()->id
            ]);

            return $this->responseJson(null, true, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES['PAYMENT'], null, 501);
        }
    }

    public function bulkPaymentUpdate(PaymentRequest $request)
    {
        try {
            $payments = $request->get('payments[]');
            $loan = null;
            $payment = null;

            if (isset($payments) && count($payments)) {
                foreach ($payments as $item) {
                    $item = json_decode(json_encode($item));

                    if (!$item->payment || !$item->paymentdate) {
                        continue;
                    }
                    $payment = Payment::findOrFail($item->id);
                    $loan = Loan::findOrFail($payment->fk_loan);

                    $payment->update([
                        'fk_status' => $this->getStatus($item, $payment)->id,
                        'payment' => $item->payment,
                        'paymentdate' => $item->paymentdate,
                        'fk_userupdater' => $this->user()->id
                    ]);

                    if ($item->payment == 0) {
                        $this->createExtra($loan);
                    }
                }

                if ($loan && $payment) {
                    $this->determineLoanStatus($payment);
                    $this->determineClientStatus($loan);
                    $this->determineEndorsementStatus($loan);
                }
            }


            return $this->responseJson(null, true, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES[''], null, 501);
        }
    }
}
