<?php namespace App;

use App\Events\V1\EventAccountAggregatorMonitoringWebhook;
use App\Http\Controllers\MConnector\MerchantApiIntegration;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\DB;
use Carbon\Carbon;

const MONITORING = 'MONITORING';
const UNDERWRITING = 'UNDERWRITING';

/**
 * Created by PhpStorm.
 * User: sunil khorwal
 * Date: 6/6/16
 * Time: 1:52 PM
 */
class AccountAggregator
{
    private static $DEFAULT_FREQUENCY = 7;
    private $request;
    /**
     * @var mixed
     */
    private $request_id;

    private $initiate = '/web/multi-consent/initiate/phone-number';
    private $fetchAnalytics = '/web/fetch/analytics'; //'/web/fetch/analytics'
    private $fetchPdfReport = '/web/fetch'; //'/web/fetch/analytics'
    private $fetchFIRequestMonitoring = '/web/fi-request';
    /**
     * @var string
     */
    private $url;
    /**
     * @var bool|string
     */
    private $response;
    /**
     * @var bool
     */
    private $fetchAnalyticsStatus = false;
    /**
     * @var bool
     */
    private $fiMonitoringRequest = false;
    /**
     * @var int
     */
    private $application_id;
    /**
     * @var string
     */
    private $analyticsFileType;
    /**
     * @var string
     */
    private $method = 'POST';
    private $templateTypes = [
        UNDERWRITING, MONITORING
    ];
    private $fipIds = [];

    /**
     * Slack constructor.
     * @param Request $request
     */
    public function __construct(Request $request){
        $this->request = $request;
    }

    public function initiate(){
        try {
            $this->url = _env('PIRIMID_ACCOUNT_AGGREGATOR_URL') . $this->initiate;
            $this->redirectionUrl = $this->request->has('redirection_url') ? $this->request->get('redirection_url') : "https://creditfair.in/account-aggregator-status";

            if ($link = $this->isAlreadyInitiated()) {
                return response()->json(['status' => 'OK', 'message' => 'Account aggregator already initiated. Link re-shared to customer', 're_share' => 1, 'link' => $link[0], 'redirection_link' => $link[1]], 200);
            }

            $this->save();
            if ($link = $this->process()) {
                return response()->json(['status' => 'OK', 'message' => 'Account aggregator initiated', 'link' => $link], 200);
            }
        }catch (\Exception $e){
            return response()->json(['status' => 'FAIL', 'message' => 'Exception occured'], 200);
        }
        return response()->json(['status' => 'FAIL', 'message' => $this->getError('Failed to initiate Account aggregator')], 200);
    }

    private function isAlreadyInitiated(){
        $accountAggregatorModel = AccountAggregatorModel::where('application_id', $this->request->get('id'))->first();
        if (isset($accountAggregatorModel->id)){
            if (!$accountAggregatorModel->created_at < Carbon::now()->subDay()) {
                // expired
                $this->request_id = $accountAggregatorModel->id;
            } else {
                $response = json_decode($accountAggregatorModel->response);
                $link = $response->redirectionUrl ?? false;
                if ($this->request->has('re_trigger')){
                    $this->sendCommunication($link);
                    return true;
                }
                return ['<table class="table"><tr><td>Account Aggregator Already Completed</td></tr><tr><td><div class="btn btn-primary re-trigger-account-aggregator" data-id="'.$this->request->get('id').'">Proceed on different bank account</td></tr></table>', $link];
            }
        }

        return false;
    }

    private function sendCommunication($link){
        $entity = ['templateid' => '1307175490512635468', 'entityid' => Templates::$entityid];
        $link = Shortio::getUrl(asset('redirect/?link=' . base64_encode($link)));
        $message = 'Dear Customer,<br/>Thank you for submitting your application to Credit Fair. Please login using your registered mobile number at this link: ' . $link . ' to submit your latest bank statement.<br/>Regards,<br/>Credit Fair';
        //$message = 'Dear Customer,<br/>Thank you for submitting your application to Credit Fair. Please login using your registered mobile number at this link: ' . $link . ' to submit your latest bank statement.<br/>Regards,<br/>Team Credit Fair';
        send_sms($this->request->get('mobile_number'), ['message' => $message, 'id' => $this->request->get('id'), 'id_type' => 'application_id', 'args' => [$link], 'templateDetails' => $entity]);
    }

    private function process(){
        $payload = json_encode($this->getPayload());
        $curl = curl_init();
        curl_setopt_array($curl, array(
            CURLOPT_URL => $this->url,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 30,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => $this->method,
            CURLOPT_POSTFIELDS => $payload,
            CURLOPT_HTTPHEADER => array(
                "Content-Type: application/json",
                "API_KEY:"._env('PIRIMID_X_AUTH_TOKEN')
            ),
        ));

        $this->response = curl_exec($curl);
        $contentType = curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
        if ($contentType == 'application/json'){
            $this->response = json_decode($this->response);
        }
        $err = curl_error($curl);

        curl_close($curl);

        (new Slack(new Request(['title'=>'Account Aggregator Res - ','fields'=>['Application ID' => $this->request->get('id'), 'payload' => $payload, 'response' => json_encode($this->response)]])))->send();
        if($err){
            (new Slack(new Request(['title'=>'Account Aggregator Error - ','fields'=>['Application ID' => $this->request->get('id'), 'error' => json_encode($err)]])))->send();
        }

        if ($err) {
            return false;
        }

        return $this->handleResponse();
    }

    public function handleResponse(){
        if(isset($this->response->errorCode)){
            (new Slack(new Request(['title'=>'Account Aggregator Error - ','fields'=>['Application ID' => $this->request->get('id'), 'error' => json_encode($this->response)], 'group' => Slack::$STAGE_TESTING])))->send();
        }
        if(isset($this->response->redirectionUrl)) {
            $this->sendCommunication($this->response->redirectionUrl);
            $this->save($this->response, $this->request_id);
            return $this->response->redirectionUrl ?? false;
        }else if($this->fetchAnalyticsStatus){
            $this->saveAnalyticsFile();
        }else if($this->fiMonitoringRequest){
            return true;
        }
    }

    private function saveAnalyticsFile(){
        if(isset($this->response->errorCode)){
            return true;
        }
        $s3 = Storage::disk('s3');
        $extension = $this->analyticsFileType == 'excel' ? 'xlsx' : $this->analyticsFileType;
        $path = $this->application_id.'/'.$this->request_id.'_account_aggregator.'.$extension;

        $output = $this->response;
        if ($this->analyticsFileType == 'json'){
            $output = json_encode($this->response);
        }

        $s3->put($path, $output);
        $fileName = strtoupper($this->analyticsFileType).' Account Aggregator';
        if ($this->analyticsFileType != 'json') {
            $this->saveToFile($fileName, $path);
        }
    }

    private function saveToFile($fileName, $path): void{
        $consumer_application_file = new ConsumerApplicationFiles();
        $consumer_application_file->consumer_application_id = $this->application_id;
        $consumer_application_file->file_type = $fileName;
        $consumer_application_file->bank_name = '';
        $consumer_application_file->password = '';
        $consumer_application_file->accounttype = '';
        $consumer_application_file->statementtype = '';
        $consumer_application_file->statemenfilettype = '';
        $consumer_application_file->customer_id = '';
        $consumer_application_file->provider = '';
        $consumer_application_file->finbit_analysis_date = '';
        $consumer_application_file->bp_number = '';
        $consumer_application_file->accountUID = '';
        $consumer_application_file->file_path = $path;
        $consumer_application_file->created_by = Auth::user()->id ?? 107;
        $consumer_application_file->image_score = '';
        $consumer_application_file->m_generated = 1;
        $consumer_application_file->applicant_type = 1;
        $consumer_application_file->bureau_report_type = '';
        $consumer_application_file->bureau_report_score = '';
        $consumer_application_file->is_masked = 0;
        $consumer_application_file->save();
    }

    private function save($response = array(), $id = false){
        if (isset($this->request_id) && !$id) {
            return;
        }
        if ($id){
            $accountAggregator = AccountAggregatorModel::find($id);
            $data = [
                'redirectionUrl' => $response->redirectionUrl,
                'vuaId' => $response->vuaId,
                'accountAggregatorId' => $response->accountAggregatorId,
                'response' => json_encode($response),
                // 'monitoring_fip_id' => $this->fipIds[0] ?? '',
                // 'underwriting_fip_id' => $this->fipIds[1] ?? ''

                'monitoring_fip_id' => '',
                'underwriting_fip_id' => ''
            ];

            if(isset($response->consents)) {
                foreach ($response->consents as $_consents) {

                    $trackingId = $_consents->trackingId;
                    $referenceId = $_consents->referenceId;

                    $this->updateReferenceAndTrackingId($trackingId, $referenceId, $_consents->templateName);
                }
            }

        }else{
            /*$accountAggregator = AccountAggregatorModel::where('application_id', $this->request->get('id'))->first(['id']);
            if(isset($accountAggregator->id)){
                $this->request_id = $accountAggregator->id;
                return true;
            }else {
                $accountAggregator = new AccountAggregatorModel();
            }*/

            $is_monitoring = in_array(MONITORING, $this->templateTypes) ? 1 : 0;

            $accountAggregator = new AccountAggregatorModel();
            $data = [
                'application_id' => $this->request->get('id'),
                'user_id' => $this->request->get('user_id') ? $this->request->get('user_id') : (Auth::user()->id ?? 107),
                'is_monitoring' => $is_monitoring
            ];
        }

        $accountAggregator->fill($data);
        $accountAggregator->save();

        $this->request_id = $accountAggregator->id;
    }

    private function getError($error){
        return $this->response->errorMessage ?? $error;
    }

    private function getPayload(){

        if ($this->fiMonitoringRequest){

            $to_date = Carbon::now('UTC')->format("Y-m-d\TH:i:s.v\Z");
            $from_date = Carbon::now('UTC')->subMonths(3)->format("Y-m-d\TH:i:s.v\Z");

            $accountAggregator = AccountAggregatorModel::where('id', $this->request->get('account_aggregator_id'))->first(['referenceId', 'response']);

            $referenceId = isset($accountAggregator->referenceId) && $accountAggregator->referenceId ? $accountAggregator->referenceId : $this->request->get('referenceId');
            $res = json_decode($accountAggregator->response, 1);
            if (isset($res['consents'])){
                foreach($res['consents'] as $consent){
                    if (isset($consent['templateName']) && $consent['templateName'] == 'MONITORING') {
                        $referenceId = $consent['referenceId'];
                    }
                }
            }


            return [
                    "trackingId" => $this->request->get('trackingId'),
                    "referenceId" => $referenceId,//isset($accountAggregator->referenceId) && $accountAggregator->referenceId ? $accountAggregator->referenceId : $this->request->get('referenceId'),
                    "fiDataFrom" => $from_date,
                    "fiDataTo" => $to_date
            ];
        }

        if (empty($this->fipIds)){
            $mapi = new MerchantApiIntegration($this->request);
            // $request_id_underwriting = 'C-'.$mapi->guidv4();
            // $request_id_monitoring = 'M-'.$mapi->guidv4();
            $request_id_underwriting = '';
            $request_id_monitoring = '';

            $this->fipIds = [$request_id_monitoring, $request_id_underwriting];
        }

        return [
                "phoneNumber" => $this->request->get('mobile_number'),
                "templateTypes" => $this->templateTypes,
                "trackingId" => $this->request_id.'_'.$this->request->get('id'),
                "fipIds" => $this->fipIds,
                "redirectionUrl" => isset($this->redirectionUrl) ? $this->redirectionUrl : null

        ];
    }

    public function fetchAnalytics(){

        $this->fetchAnalyticsStatus = true;
        $consents = $this->request->get('consents');
        if($consents) {

            foreach($consents as $_consents){

                $trackingId = $_consents['trackingId'];
                $referenceId = $_consents['referenceId'];

                $this->updateReferenceAndTrackingId($trackingId, $referenceId, $_consents['templateName']);
            }

            $segment = explode('_', $consents[0]['trackingId']);
            $trackingId = $consents[0]['trackingId'];
            $referenceId = $consents[0]['referenceId'];
        }else{
            $segment = explode('_', $this->request->get('trackingId'));
            $trackingId = $this->request->get('trackingId');
            $referenceId = $this->request->get('referenceId');

            $this->updateReferenceAndTrackingId($trackingId, $referenceId);
        }


        $this->application_id = $segment[1];
        $this->request_id = $segment[0];
        $this->analyticsFileType = 'json';
        $this->method = 'GET';

        $this->url = _env('PIRIMID_ACCOUNT_AGGREGATOR_URL').$this->fetchAnalytics.'/'.$this->analyticsFileType.'?trackingId='.$trackingId.'&referenceId='.$referenceId;
        $this->process();

        $this->analyticsFileType = 'excel';

        $this->url = _env('PIRIMID_ACCOUNT_AGGREGATOR_URL').$this->fetchAnalytics.'/'.$this->analyticsFileType.'?trackingId='.$trackingId.'&referenceId='.$referenceId;
        $this->process();

        $this->analyticsFileType = 'pdf';

        $this->url = _env('PIRIMID_ACCOUNT_AGGREGATOR_URL').$this->fetchPdfReport.'/'.$this->analyticsFileType.'?trackingId='.$trackingId.'&referenceId='.$referenceId;
        $this->process();


        $application = ConsumerApplicationElement::where('application_id', $this->application_id)->first(['id','mandatory_document_list']);
        $consumerApplication = DB::select('select id, merchant_id from consumer_application where id ='.$this->application_id);
        $fKey = '3_month_bank_statement';

        $nString = str_replace(','.$fKey, '', $application->mandatory_document_list);
        $nString = str_replace($fKey.',', '', $nString);
        $nString = str_replace($fKey, '', $nString);
        ConsumerApplicationElement::where('id', $application->id)->update(['mandatory_document_list' => $nString]);
        // SSE Mconnect Integration Block
    }

    public function saveConsent() {
        $trackingId = $this->request['trackingId'];
        if (isset($trackingId)) {
            DB::table('account_aggregator')
                ->where('trackingId', $this->request['trackingId'])
                ->update(['consent_status' => 1]);
        }
    }

    public function updateReferenceAndTrackingIdIfNot($account_aggregator_id){
        $monitoring = AccountAggregatorModel::find($account_aggregator_id);
        if(isset($monitoring->response)) {
            $response = json_decode($monitoring->response);
            foreach ($response->consents as $_consents) {

                $trackingId = $_consents->trackingId;
                $referenceId = $_consents->referenceId;

                $this->updateReferenceAndTrackingId($trackingId, $referenceId, $_consents->templateName);
            }
        }
    }

    private function updateReferenceAndTrackingId($trackingId, $referenceId, $templateName = false){
        $isUpdate = false;
        $account_aggregator_details = explode('_', $trackingId);
        $account_aggregator_id = $account_aggregator_details[0] ?? 0;
        //$application_id = $account_aggregator_details[1];
        if($account_aggregator_id) {
            $account_aggregator = AccountAggregatorModel::find($account_aggregator_id);
            if ($templateName == 'UNDERWRITING'){
                if (!$account_aggregator->u_trackingId || $account_aggregator->u_trackingId != $trackingId){
                    $account_aggregator->u_trackingId = $trackingId;
                    $isUpdate = true;
                }
                if (!$account_aggregator->u_referenceId || $account_aggregator->u_referenceId != $referenceId) {
                    $account_aggregator->u_referenceId = $referenceId;
                    $isUpdate = true;
                }
            }else{
                if(!$account_aggregator->trackingId || $account_aggregator->trackingId != $trackingId){
                    $account_aggregator->trackingId = $trackingId;
                    $isUpdate = true;
                }
                if(!$account_aggregator->referenceId || $account_aggregator->referenceId != $referenceId){
                    $account_aggregator->referenceId = $referenceId;
                    $isUpdate = true;
                }
            }
            if($isUpdate) {
                $account_aggregator->save();
            }
        }
    }

    public function isTrancheDisbursement($id){
        return DisbursementLogs::where('application_id', $id)->count() >= 1;
    }

    public function autoFetchStatementForMonitoring()
    {
        $requestId = (new MerchantApiIntegration($this->request))->guidv4();

        $this->templateTypes = [MONITORING];

        // $this->fipIds = ['M-' . $requestId];
        $this->fipIds = [''];

        $applicationId = $this->request->get('application_id');

        $aa = AccountAggregatorModel::where('application_id', $applicationId)
            ->where('is_monitoring', 1)
            ->first(['id', 'trackingId', 'referenceId']);

        if ($aa) {
            return $this->createRequestForMonitoring([
                'account_aggregator_id' => $aa->id,
                'trackingId'            => $aa->trackingId,
                'referenceId'           => $aa->referenceId
            ]);
        }

        return response()->json([
            'status'  => 'FAIL',
            'message' => 'No AA initiated for monitoring'
        ]);
    }

    private function createRequestForMonitoring(array $data)
    {
        $accountAggregator = new AccountAggregator(new Request($data));
        $response = $accountAggregator->createMonitoringRequest();

        if ($response === false) {
            return response()->json([
                'status'  => 'FAIL',
                'message' => 'AA already initiated for monitoring'
            ]);
        }

        return $response;
    }

    public function initMonitoring($account_aggregator)
    {

        $accountAggregatorMonitoring = AccountAggregatorMonitoring::firstOrNew([
            'account_aggregator_id' => $account_aggregator->id
        ]);

        $accountAggregatorMonitoring->fill([
            'user_id'         => Auth::check() ? Auth::id() : 107
        ]);

        $accountAggregatorMonitoring->save();

        return $this->createRequestForMonitoring([
            'account_aggregator_id' => $account_aggregator->id,
            'trackingId'            => $account_aggregator->trackingId,
            'referenceId'           => $account_aggregator->referenceId
        ]);
    }

    public function updateAutoFetch(){
        $nextFetchDate = $this->request->get('next_fetch_date');
        $monitoringId = $this->request->get('account_aggregator_monitoring_id');

        $monitoring = AccountAggregatorMonitoring::find($monitoringId);

        $this->request->merge(['account_aggregator_id' => $monitoring->account_aggregator_id]);

        return $this->updateExistingMonitoringQueue($nextFetchDate);
    }

    public function updateSubsidyStatus()
    {
        $monitoringId = $this->request->get('account_aggregator_monitoring_id');
        $isSubsidyReceived = $this->request->get('is_subsidy_received');

        $monitoring = AccountAggregatorMonitoring::find($monitoringId);

        $this->updateReferenceAndTrackingIdIfNot($monitoring->account_aggregator_id);

        if ($monitoring && Auth::check()) {
            $monitoring->fill([
                'is_subsidy_received' => $isSubsidyReceived,
                'user_id'             => Auth::id(),
                'created_at'          => now()->toDateTimeString(),
                'status'              => (int)$isSubsidyReceived === 2 ? 1 : 0
            ])->save();

            if ((int)$isSubsidyReceived === 2) {
                $accountAggregator = AccountAggregatorModel::where('id', $monitoring->account_aggregator_id)->first(['id', 'trackingId', 'referenceId']);
                $this->createRequestForMonitoring([
                    'account_aggregator_id' => $monitoring->account_aggregator_id,
                    'trackingId'            => $accountAggregator->trackingId,
                    'referenceId'           => $accountAggregator->referenceId,
                    'next_fetch_date'       => now()->addDays($this->getMonitoringFrequency())->toDateTimeString()
                ]);
            }else{
                $job = JobProvider::where('application_id', $monitoring->account_aggregator_id)
                    ->where('from', 'create_monitoring_request')
                    ->first(['id', 'available_at', 'is_available']);
                $job->is_available = 1;
                $job->save();
            }

            return $monitoring->id;
        }

        return false;
    }

    private function updateExistingMonitoringQueue($nextFetchDate = null){
        $job = JobProvider::where('application_id', $this->request->get('account_aggregator_id'))
            ->where('from', 'create_monitoring_request')
            ->first(['id', 'available_at', 'is_available']);

        if ($job) {

            $availableAt = $nextFetchDate
                ? Carbon::parse($nextFetchDate)->toDateTimeString()
                : Carbon::now()->parse($job->available_at)->addDays($this->getMonitoringFrequency())->toDateTimeString();

            $job->available_at = $availableAt;
            $job->is_available = 0;
            $job->save();

            return response()->json([
                'status' => 'OK',
                'message' => 'Account aggregator monitoring request updated'
            ], 200);
        }
        return false;
    }

    private function createMonitoringRequest()
    {
        $accountAggregatorId = $this->request->get('account_aggregator_id');

        if($res = $this->updateExistingMonitoringQueue()){
            return $res;
        }

        $payload = [[
            'payload'        => json_encode($this->request->all()),
            'from'           => 'create_monitoring_request',
            'application_id' => $accountAggregatorId,
            'available_at'   => Carbon::now()->addDays($this->getMonitoringFrequency())->toDateTimeString(),
            'created_at'     => Carbon::now()->toDateTimeString()
        ]];

        (new Jobs())->insertQueue($payload);

        return response()->json([
            'status' => 'OK',
            'message' => 'Account aggregator monitoring request initiated'
        ], 200);
    }

    public function processMonitoringRequest()
    {
        $this->fiMonitoringRequest = true;
        $this->url = env('PIRIMID_ACCOUNT_AGGREGATOR_URL') . $this->fetchFIRequestMonitoring;

        $success = $this->process();

        return response()->json([
            'status'  => $success ? 'OK' : 'FAIL',
            'message' => $success ? 'Account aggregator initiated' : 'Account aggregator failed'
        ], 200);
    }


    public function getMonitoringFrequency(){
        $account_aggregator_config =  DB::table('account_aggregator_config')->first(['auto_fetch_frequency']);
        return $account_aggregator_config->auto_fetch_frequency ?? self::$DEFAULT_FREQUENCY;
    }
}
