<?php

namespace App\Http\Controllers\api;

use Exception;
use Carbon\Carbon;
use App\Traits\UserLoggedTrait;
use App\Traits\RequestLoanTrait;
use Illuminate\Support\Facades\DB;
use App\Models\Application\Status;
use App\Http\Requests\RLoanRequest;
use App\Models\Application\LoanType;
use App\Http\Controllers\Controller;
use App\Jobs\RequestMailJob;
use App\Mail\RequestLoanMail;
use App\Models\Application\RequestLoan;
use App\Models\Administration\OfficeUser;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Mail;
use stdClass;

class RequestLoanController extends Controller
{
    use UserLoggedTrait, RequestLoanTrait;

    private $MESSAGES = [
        'SHOW' => 'WARNING.REQUESTLOAN_SHOW',
        'SAME' => 'WARNING.REQUESTLOAN_SAME',
        'INDEX' => 'WARNING.REQUESTLOAN_INDEX',
        'STORE' => 'WARNING.REQUESTLOAN_STORE',
        'UPDATE' => 'WARNING.REQUESTLOAN_UPDATE',
        'DESTROY' => 'WARNING.REQUESTLOAN_DESTROY',
        'REJECTED' => 'WARNING.REQUESTLOAN_REJECT',
        'APPROVED' => 'WARNING.REQUESTLOAN_APPROVE',
        'REGISTERED' => 'WARNING.REQUESTLOAN_REGISTERED',
        'NOT-OWNER' => 'WARNING.REQUESTLOAN_INVALID_OWNER',
        'INVALID-DELETE' => 'WARNING.REQUESTLOAN_INVALID_DELETE',
        'INVALID-UPDATE' => 'WARNING.REQUESTLOAN_INVALID_UPDATE',
        'NOT-PERMISSION' => 'WARNING.REQUESTLOAN_NOT_PERMISSION'
    ];

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

    public function index(RLoanRequest $request)
    {
        try {
            $isSystemAdministrador = $this->hasSystemAdministrator();
            $currentdate = Carbon::now();

            if ($currentdate->isFriday()) {
                $currentdate->addDay();
            } elseif (!$currentdate->isSaturday()) {
                $currentdate->previous(Carbon::SATURDAY);
            }

            $init = $currentdate->subDays(2)->format('Y-m-d');
            $end = $currentdate->addDays(6)->format('Y-m-d');

            $requestLoans = RequestLoan::with(['loan_type', 'client', 'request_client', 'group', 'status', 'user_creator', 'user_updater'])->where([
                ['fk_site', $request->fk_site],
                ['fk_office', $request->fk_office],
                ['is_active', !$this->hasSpecial($request->fk_office)]
            ])->when(!$isSystemAdministrador, function ($query) use ($request, $init, $end) {
                $query->where('visible', true)->whereBetween('loandate', [$init, $end]);

                $userRole = OfficeUser::where([
                    ['fk_office', $request->fk_office],
                    ['fk_user', $this->user()->id],
                    ['is_active', true]
                ])->first();

                $collaborator = $this->user()->collaborator;

                switch ($userRole->role->rolename) {
                    case 'SUPERVISOR':
                        $query->where(function ($query) use ($collaborator) {
                            $query->where('fk_supervisor', $collaborator->id ?? 0)->orWhere('fk_usercreator', $this->user()->id ?? 0);
                        });
                        break;
                    case 'EJECUTIVO':
                    case 'EXECUTIVE':
                        $query->where(function ($query) use ($collaborator) {
                            $query->where('fk_executive', $collaborator->id ?? 0)->orWhere('fk_usercreator', $this->user()->id ?? 0);
                        });
                        break;
                    case 'PROMOTER':
                    case 'PROMOTOR':
                        $query->where('group', function ($query) use ($collaborator) {
                            $query->where('fk_promoter', $collaborator->id ?? 0);
                        })->orWhere('fk_usercreator', $this->user()->id);
                        break;
                }
            })->orderBy('loandate', 'DESC')->get();

            if (!$isSystemAdministrador) {
                $this->makeFieldHidden($requestLoans, ['visible', 'user_creator', 'user_updater']);
            }

            $requestLoans = $requestLoans->map(function ($loan) {
                $client = [
                    'id' => $loan->request_client->id ?? null,
                    'fk_client' => $loan->client->id ?? null,
                    'fullname' => $loan->client->fullname ?? $loan->request_client->fullname,
                ];

                unset($loan->client);
                unset($loan->request_client);

                $loan->client = $client;
                return $loan;
            });

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

    public function store(RLoanRequest $request)
    {
        try {
            DB::beginTransaction();

            $loantype = LoanType::findOrFail($request->fk_loan_type);
            $loandate = Carbon::createFromDate($request->loandate);
            $loanenddate = Carbon::createFromDate($request->loanenddate);
            $defaultLoanStatus = Status::where('statusname', 'SOLICITUD EN APROBACIÓN')->first();

            $allowedday = strtoupper($loantype->alloweddays);

            if (strtoupper($loandate->format('l')) !== $allowedday && $allowedday != 'ALL') {
                $loandate = $loandate->previous($this->convertToCarbonDay($allowedday));
            }

            $loandate = $loandate->format('Y-m-d');
            $loanenddate = $loanenddate->format('Y-m-d');
            $loans = json_decode(json_encode($request->loans));

            foreach ($loans as $loan) {
                $item = RequestLoan::create([
                    'fk_site' => $request->fk_site,
                    'fk_office' => $request->fk_office,
                    'fk_loan_type' => $request->fk_loan_type,
                    'fk_group' => $request->fk_group,
                    'fk_promoter' => $request->fk_promoter,
                    'fk_request_client' => $loan->fk_request_client,
                    'fk_client' => $loan->fk_client,
                    'duration' => $request->duration,
                    'loandate' => $loandate,
                    'loanenddate' => $loanenddate,
                    'loanamount' => $loan->loanamount,
                    'weeklypayment' => $loan->weeklypayment,
                    'realamount' => $loan->realamount,
                    'fk_executive' => $request->fk_executive,
                    'fk_supervisor' => $request->fk_supervisor,
                    'fk_collaborator' => $request->fk_collaborator,
                    'fk_status' => $defaultLoanStatus->id,
                    'firstpay' => $request->firstpay ?? false,
                    'fk_usercreator' => $this->user()->id,
                ]);

                $this->addRequestLoanDetails($item, $loan);

                if ($loan->endorsements) {
                    $this->addRequestLoanEndorsements($item, $loan->endorsements);
                }

                if ($loan->guarantees) {
                    $this->addRequestLoanGuarantees($item, $loan->guarantees);
                }
            }

            DB::commit();

            Cache::put('created_at', now());
            Cache::forget('job_id');

            $job = (new RequestMailJob())->delay(now()->addMinute());
            dispatch($job);

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

    public function update(RLoanRequest $request)
    {
        try {
            $loan = RequestLoan::findOrFail($request->id);

            if (in_array($loan->status->statusname, ['SOLICITUD REGISTRADA', 'SOLICITUD APROBADA'])) {
                return $this->responseJson($this->MESSAGES['INVALID-UPDATE'], null, 501);
            }

            DB::beginTransaction();

            $loan->update([
                'fk_loan_type' => $request->fk_loan_type,
                'fk_group' => $request->fk_group,
                'fk_promoter' => $request->fk_promoter,
                'fk_request_client' => $request->fk_request_client,
                'fk_client' => $request->fk_client,
                'duration' => $request->duration,
                'loandate' => $request->loandate,
                'loanenddate' => $request->loanenddate,
                'loanamount' => $request->loanamount,
                'weeklypayment' => $request->weeklypayment,
                'realamount' => $request->realamount,
                'fk_executive' => $request->fk_executive,
                'fk_supervisor' => $request->fk_supervisor,
                'fk_collaborator' => $request->fk_collaborator,
                'firstpay' => $request->firstpay,
                'fk_userupdater' => $this->user()->id,
            ]);

            $loan->details->update([
                'renewpayment' => $request->renewpayment,
                'paperworkpayment' => $request->paperworkpayment,
                'otherpayment' => $request->otherpayment,
                'interesrate' => $request->interesrate,
                'fk_userupdater' => $this->user()->id,
            ]);

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

    public function show(RLoanRequest $request)
    {
        try {
            $loan = RequestLoan::select(
                'id',
                'duration',
                'loandate',
                'loanenddate',
                'loanamount',
                'weeklypayment',
                'firstpay',
                'fk_loan_type',
                'fk_group',
                'fk_executive',
                'fk_supervisor',
                'fk_promoter'
            )->findOrFail($request->id);

            $loan->load([
                'loan_type',
                'group',
                'executive' => function ($query) {
                    $query->select('id', DB::raw("CONCAT(firstname, ' ', lastname, ' ', secondlastname) as fullname"));
                },
                'promoter' => function ($query) {
                    $query->select('id', DB::raw("CONCAT(firstname, ' ', lastname, ' ', secondlastname) as fullname"));
                },
                'supervisor' => function ($query) {
                    $query->select('id', DB::raw("CONCAT(firstname, ' ', lastname, ' ', secondlastname) as fullname"));
                },
                'details'
            ]);

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

    public function destroy(RLoanRequest $request)
    {
        try {
            $requestLoan = RequestLoan::findOrFail($request->id);

            if (in_array($requestLoan->status->statusname, ['SOLICITUD REGISTRADA'])) {
                return $this->responseJson($this->MESSAGES['INVALID-DELETE'], null, 501);
            }

            if (!$this->hasSystemAdministrator()) {
                if ($requestLoan->fk_usercreator != $this->user()->id) {
                    return $this->responseJson($this->MESSAGES['NOT-OWNER'], null, 501);
                }
            }

            DB::beginTransaction();

            $requestLoan->update([
                'is_active' => $this->hasSpecial($request->fk_office),
                'fk_userudapter' => $this->user()->id
            ]);

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

    public function approveOrRejectRequest(RLoanRequest $request)
    {
        try {
            DB::beginTransaction();

            if (!$this->hasSystemAdministrator()) {
                $role = $this->user_role($request->fk_office);

                if (!in_array($role->rolename, ['EXEUTIVE', 'EJECUTIVO'])) {
                    DB::rollBack();
                    return $this->responseJson($this->MESSAGES['NOT-PERMISSION'], null, 501);
                }
            }

            $loan = RequestLoan::findOrFail($request->id);

            if ($loan->status->statusname == 'SOLICITUD REGISTRADA') {
                return $this->responseJson($this->MESSAGES['REGISTERED'], null, 501);
            }

            $status = Status::where('statusname', (isset($request->approved) && $request->approved ? 'SOLICITUD APROBADA' : 'SOLICITUD RECHAZADA'))->first();

            if ($loan->status->id == $status->id) {
                return $this->responseJson($this->MESSAGES['SAME'], null, 501);
            }

            $loan->update([
                'fk_status' => $status->id,
                'fk_userupdater' => $this->user()->id
            ]);

            DB::commit();
            return $this->responseJson(null, true, 201);
        } catch (Exception $ex) {
            DB::rollBack();

            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES[$request->approved ? 'APPROVED' : 'REJECTED'], null, 501);
        }
    }

    protected function send()
    {
        $data = new stdClass();
        $mail = new stdClass();
        $today = now()->format('Y-m-d');

        $requests = RequestLoan::with(['client', 'request_client'])->whereDate('created_at', $today)->where([
            ['is_active', 1]
        ]);

        if (!$requests->exists()) return;

        // MailData
        $mail->subject = 'Requests craeted';
        $mail->to = 'adrian.arellano@outlook.com';

        // Data
        $data->mail = $mail;
        $data->requestsCount = $requests->count();
        $data->requests = $requests->get();

        // Create Mail
        $mail = new RequestLoanMail($data);

        // Send Mail
        Mail::to($data->mail->to)->send($mail);
    }
}
