<?php
/**
 * Created by PhpStorm.
 * User: abhishek
 * Date: 15/10/25
 * Time: 11:56 PM
 */

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Logging\CloudWatchLoggerFactory;
use Illuminate\Support\Facades\DB;
use App\Slack;
use App\BreLog;
use Carbon\Carbon;
use App\JsonBreApprovalStatusLog;
use Illuminate\Support\Facades\Storage;
use App\ConsumerApplication;

class JsonBREController extends Controller 
{

    private $bre_rules_ignored = [];

    public function __construct(){
        $this->cwFactory = new CloudWatchLoggerFactory();
    }

    public function createBrePayload($id) {
        $payload = array(
            'identifier' => $id,
            'grade' => array(
                'rules' => null
                )
            );
        return $payload;
    }

    public function get_exhaustive_criteria_json($file_path, $application_id){
        try {
            $s3 = Storage::disk('s3');
            $this->cwFactory->logger(json_encode(['title' => 'readFile checking', 'file_path' => $file_path, 'application_id'=>$application_id]));
            if ($s3->exists($file_path)) {
                $this->cwFactory->logger(json_encode(['title' => 'exhaustive_json file readFile exists', 'file_path' => $file_path, 'application_id'=>$application_id]));
                return $s3->get($file_path);
            }
            return false;
        } catch (\Exception $e) {
            $this->cwFactory->logger(json_encode(['title' => 'exhaustive_json file readFile does not exists', 'file_path' => $file_path, 'error' =>$e, 'application_id'=>$application_id]));
            return false;
        }
    }

    public function create_rule_list($application_id, $grade_rule, $req_id, $tableOfOffers, $exhaustive_criteria_json){
        $rule_details = json_decode($grade_rule->rules);
        $rule_details = (array)$rule_details;
        ksort($rule_details);
        $rule_details = (object)$rule_details;

        $rule_list = [];
        $request_data = [];

        foreach ($rule_details as $key => $value) {
            try{
                if (!in_array($exhaustive_criteria_json[$key], $rule_list)) {
                    $rule_list[] = $exhaustive_criteria_json[$key];  // Add the value to the rule list
                    if ($exhaustive_criteria_json[$key]){
                        $fallback_rule = $exhaustive_criteria_json[$key]['fallback'] ?? null;
                        if (is_array($fallback_rule) && isset($fallback_rule['rule'])){
                            $rule_list[] = $exhaustive_criteria_json[$fallback_rule['rule']];
                        }
                    }
                }
            }
            catch (\Exception $e) {
                $this->bre_rules_ignored[] = ['rule' => $key];
                (new Slack(new Request(['title' => 'Skipped a rule', 'fields' => ['error' => $e->getMessage(), 'line' => $e->getLine(), 'trace' => $e->getTraceAsString(), 'application_id' => $application_id], 'group' => Slack::$STAGE_TESTING])))->send();

            }
        }
        return $rule_list;
    }

    public function create_rejection_rule_list($application_id, $rejectRules, $exhaustive_criteria_json) {
        try{
            $rule_list = [];
            foreach ($rejectRules->new_rejection_rules as $key=>$reject_rule) {
                if (in_array($key, ['MAX_SCORE', 'MIN_SCORE'])) {
                    $rule_name = 'rejection_score';
                } else if (in_array($key, ['MAX_AGE', 'MIN_AGE'])) {
                    $rule_name = 'rejection_age';
                } else if ($key == 'NEGATIVE_AREA') {
                    if ($reject_rule) {
                        $rule_name = 'negative_area';
                    }
                } else if ($key == 'APPROVED_PINCODE') {
                    if ($reject_rule) {
                        $rule_name = 'is_pincode_approved';
                    }
                } else {
                    $rule_name = 'rejection_'.strtolower($key);
                }
                if (!in_array($exhaustive_criteria_json[$rule_name], $rule_list)) {
                    $rule_list[] = $exhaustive_criteria_json[$rule_name];
                    if ($exhaustive_criteria_json[$rule_name]){
                        $fallback_rule = $exhaustive_criteria_json[$rule_name]['fallback'] ?? null;
                        if (is_array($fallback_rule) && isset($fallback_rule['rule'])){
                            $rule_list[] = $exhaustive_criteria_json[$fallback_rule['rule']];
                        }
                    }
                }
            }
            return $rule_list;
        } catch (\Exception $e) {
            $this->bre_rules_ignored[] = ['rule' => $key];
            (new Slack(new Request(['title' => 'Skipped a rule', 'fields' => ['error' => $e->getMessage(), 'line' => $e->getLine(), 'trace' => $e->getTraceAsString(), 'application_id' => $application_id], 'group' => Slack::$STAGE_TESTING])))->send();
        }
    }

    public function runJsonBreApproval($application_id, $req_id, $gradeID, $tableOfOffers, $json_bre_for_log=false) {
        try{
            $grade_rules = DB::select("SELECT id, rule_name, rules from approval_rule where grade_id = ? and status = 1", [$gradeID]);
            $bre_api_payload = $this->createBrePayload($application_id);
            $application = ConsumerApplication::with('emi', 'application_element', 'creditReport')->where('id', $application_id)->first();
            $file_path = _env('JSON_BRE_EXHAUSTIVE_JSON');
            $exhaustive_criteria_json = $this->get_exhaustive_criteria_json($file_path, $application_id);
            $exhaustive_criteria_json = json_decode($exhaustive_criteria_json, 1);
            foreach ($grade_rules as $grade_rule) {
                $rule = json_decode($grade_rule->rules, 1);
                $policy = [];
                $policy_name = isset($rule['power_savings']) ? "policy-power-savings-".$grade_rule->id : "policy-".$grade_rule->id;
                $rule_list = $this->create_rule_list($application_id, $grade_rule, $req_id, $tableOfOffers, $exhaustive_criteria_json);
                if($rule_list){
                    $policy['rule'] = $rule_list;
                }
                else{
                    $policy['rule'] = [];
                }
                $policy['payload'] = array( 'identifier' => $application_id
                                            , 'applicant_type' => 1
                                            , 'rule_name' => $grade_rule->rule_name
                                            , 'loan_amount' => isset($application->approved_amount) ? (int)($application->approved_amount) : null
                                            , 'max_loan_amount' => isset($rule['max_loan_amount']) ? (int)($rule['max_loan_amount']) : null
                                            , 'min_loan_amount' => isset($rule['min_loan_amount']) ? (int)($rule['min_loan_amount']) : null
                                            , 'employee_status' => isset($rule['employee_status']) ? ($rule['employee_status']) : null
                                            , 'past_dues' => isset($rule['past_dues']) ? (int)($rule['past_dues']) : null
                                            , 'credit_card_percentage' => isset($rule['credit_card_amount_paid']) ? (int)($rule['credit_card_amount_paid']) : null
                                            , 'age' => isset($rule['age']) ? (int)($rule['age']) : null
                                            , 'ageAtLastEMI' => isset($rule['ageAtLastEMI']) ? (int)($rule['ageAtLastEMI']) : null
                                            , 'monthly_income' => isset($rule['monthly_income']) ? (int)($rule['monthly_income']) : null
                                            , 'score' => isset($rule['score']) ? (int)($rule['score']) : null
                                            , 'tenure' => isset($rule['tenure']) ? (int)($rule['tenure']) : null
                                            , 'cf_foir' => isset($rule['cf_foir']) ? (int)($rule['cf_foir']) : null
                                            , 'foir' => isset($rule['foir']) ? (int)($rule['foir']) : null
                                            , 'case_type' => isset($rule['caseType']) ? ($rule['caseType']) : null
                                            , 'dpd_years_above_0' => isset($rule['dpd_years_above_zero']) ? (int)($rule['dpd_years_above_zero']) : null
                                            , 'dpd_count_above_0' => isset($rule['dpd_count_above_zero']) ? (int)($rule['dpd_count_above_zero']) : null
                                            , 'pre_dpd_years' => isset($rule['pre_dpd_years']) ? (int)($rule['pre_dpd_years']) : null
                                            , 'pre_dpd_count' => isset($rule['pre_dpd_count']) ? (int)($rule['pre_dpd_count']) : null
                                            , 'pre_dpd_keywords' => isset($rule['pre_dpd_keywords']) ? ($rule['pre_dpd_keywords']) : null
                                            , 'post_dpd_year' => isset($rule['post_dpd_year']) ? (int)($rule['post_dpd_year']) : null
                                            , 'post_dpd_count' => isset($rule['post_dpd_count']) ? (int)($rule['post_dpd_count']) : null
                                            , 'post_dpd_keywords' => isset($rule['post_dpd_keywords']) ? ($rule['post_dpd_keywords']) : null
                                            , 'surrogate_percentage' => isset($rule['bureau_surrogate']) ? (int)($rule['bureau_surrogate']) : null
                                            , 'term_loan_amount_paid_percentage' => isset($rule['term_loan_amount_paid']) ? (int)($rule['term_loan_amount_paid']) : null
                                            , 'power_savings' => isset($application->application_element->inverter_power_savings) ? (float)($application->application_element->inverter_power_savings) : null
                                            , 'emi' => isset($application->emi->emi) ? (int)($application->emi->emi) : null
                                            , 'zero_downpayment' => 0
                                            , 'bank_validation' => isset($rule['bank_validation']) ? (int)($rule['bank_validation']) : null);
                $this->cwFactory->logger(json_encode(['title' => 'BRE_API payload addition attempted', 'application_id' => $application_id, 'rule'=>$grade_rule->id]));
                if (isset($policy)){
                    $bre_api_payload['grade']['rules'][$policy_name] = $policy;
                }
            }
            if($json_bre_for_log) {
                // Rejection rules for JSON BRE
                $auto_reject_rule = DB::selectOne("SELECT modifications from auto_reject_rule where grade_id = ? order by created_at desc", [$gradeID]);
                $reject_rules = json_decode($auto_reject_rule->modifications);
                $rejectionPolicy = [];
                $rejectionPolicy['payload'] = array(
                    'identifier' => $application_id,
                    'applicant_type' => 1,
                    'rule_name' => 'auto-rejection-rule',
                    'age_range' => [(int)$reject_rules->new_rejection_rules->MIN_AGE, (int)$reject_rules->new_rejection_rules->MAX_AGE],
                    'monthly_income' => isset($rule['monthly_income']) ? (int)($rule['monthly_income']) : null,
                    'credit_score_range' => [(int)$reject_rules->new_rejection_rules->MIN_SCORE, (int)$reject_rules->new_rejection_rules->MAX_SCORE],
                    'bureau_type' => $application->creditReport[0]->bureau,
                    'dpd_rejection_rules' => $reject_rules->new_rejection_rules->DPD[0],
                    'keyword_rejection_rules' => $reject_rules->new_rejection_rules->KEYWORD[0],
                    'account_status_rejection_rules' => $reject_rules->new_rejection_rules->ACCOUNT_STATUS[0],
                    'principal_write_off_rules' => $reject_rules->new_rejection_rules->PRINCIPAL_WRITTEN_OFF_AMOUNT[0],
                    'total_past_dues_rules' => $reject_rules->new_rejection_rules->TOTAL_PAST_DUES[0],
                    'nach_bounce_rule' => $reject_rules->new_rejection_rules->NACH_BOUNCE
                );
                $pirimidAnalysisAvailable = DB::selectOne("SELECT id, response from pirimid_bank_analysis where application_id = ?", [$application_id]);
                if (isset($pirimidAnalysisAvailable->id)) {
                    $response = json_decode($pirimidAnalysisAvailable->response, 1);
                    $bre_api_payload['pirimid_tracking_id'] = $response['trackingId'];
                }
                $policy_name = "policy-rejection";
                $rule_list = $this->create_rejection_rule_list($application_id, $reject_rules, $exhaustive_criteria_json);
                if($rule_list){
                    $rejectionPolicy['rule'] = $rule_list;
                }
                $bre_api_payload['grade']['rules'][$policy_name] = $rejectionPolicy;
            }
            try{
                $response = $this->runAutoDecisionBRE($bre_api_payload);
                if(!$json_bre_for_log){
                    $this->store_jsonbre_response($response, $req_id);
                }
                return $response;
            }
            catch(\Exception $e) {
                $this->cwFactory->logger(json_encode(['title' => 'BRE_API run Failed', 'application_id' => $application_id, 'req_id'=> $req_id]));
            }
        }
        catch(\Exception $e) {
            $this->cwFactory->logger(json_encode(['title' => 'Create BRE Payload failed', 'application_id' => $application_id, 'error'=>$e->getMessage(), 'line' => $e->getLine(), 'trace' => $e->getTraceAsString()]));
        }
    }

    public function runRejection($application_id, $req_id, $auto_reject_rule, $json_bre_for_log=false, $pirimid_tracking_id='') {
        try{
            $bre_api_payload = $this->createBrePayload($application_id);
            $file_path = _env('JSON_BRE_EXHAUSTIVE_JSON');
            $exhaustive_criteria_json = $this->get_exhaustive_criteria_json($file_path, $application_id);
            $exhaustive_criteria_json = json_decode($exhaustive_criteria_json, 1);

            // $auto_reject_rule = DB::selectOne("SELECT modifications from auto_reject_rule where grade_id = ? order by created_at desc", [$gradeID]);
            $creditReport = DB::selectOne("SELECT id, bureau from credit_report where application_id = ? and applicant_type = 1", [$application_id]);
            $reject_rules = json_decode($auto_reject_rule->modifications);

            $rejectionPolicy = [];
            $rejectionPolicy['payload'] = array(
                'identifier' => $application_id,
                'applicant_type' => 1,
                'rule_name' => 'auto-rejection-rule',
                'age_range' => [(int)$reject_rules->new_rejection_rules->MIN_AGE, (int)$reject_rules->new_rejection_rules->MAX_AGE],
                'monthly_income' => isset($reject_rules->new_rejection_rules->MONTHLY_INCOME) ? (int)($reject_rules->new_rejection_rules->MONTHLY_INCOME) : null,
                'credit_score_range' => [(int)$reject_rules->new_rejection_rules->MIN_SCORE, (int)$reject_rules->new_rejection_rules->MAX_SCORE],
                'bureau_type' => $creditReport->bureau,
                'dpd_rejection_rules' => $reject_rules->new_rejection_rules->DPD[0],
                'keyword_rejection_rules' => $reject_rules->new_rejection_rules->KEYWORD[0],
                'account_status_rejection_rules' => $reject_rules->new_rejection_rules->ACCOUNT_STATUS[0],
                'principal_write_off_rules' => $reject_rules->new_rejection_rules->PRINCIPAL_WRITTEN_OFF_AMOUNT[0],
                'total_past_dues_rules' => $reject_rules->new_rejection_rules->TOTAL_PAST_DUES[0]
            );
            if ($pirimid_tracking_id == ''){
                $pirimidAnalysisAvailable = DB::selectOne("SELECT id, response from pirimid_bank_analysis where application_id = ?", [$application_id]);
                if (isset($pirimidAnalysisAvailable->id)) {
                    $response = json_decode($pirimidAnalysisAvailable->response, 1);
                    $pirimid_tracking_id =  $response['trackingId'];
                }
            }
            if (isset($pirimid_tracking_id) && $pirimid_tracking_id != '') {
                $response = json_decode($pirimidAnalysisAvailable->response, 1);
                $bre_api_payload['pirimid_tracking_id'] = $response['trackingId'];
                $rejectionPolicy['payload']['nach_bounce_rule'] = $reject_rules->new_rejection_rules->NACH_BOUNCE;
            }
            $rule_list = $this->create_rejection_rule_list($application_id, $reject_rules, $exhaustive_criteria_json);
            if($rule_list){
                $rejectionPolicy['rule'] = $rule_list;
            }
            $bre_api_payload['grade']['rules']["policy-rejection"] = $rejectionPolicy;
            $response = $this->runAutoDecisionBRE($bre_api_payload);
            if(!$json_bre_for_log){
                $this->store_jsonbre_response($response, $req_id, true);
            }
            return $response;
        }
        catch(\Exception $e){
            $this->cwFactory->logger(json_encode(['application_id'=> $application_id, 'error'=>$e->getMessage(), 'line' => $e->getLine(), 'trace' => $e->getTraceAsString()]));
        }
    }

    public function store_jsonbre_response($response, $req_id, $rejection=false, $reasonDetails=false) {
        if ($reasonDetails) {
            $this->cwFactory->logger(json_encode(['title' => 'Default check failed', 'reason'=>$reasonDetails['reason'], 'application_id' => $reasonDetails['application_id']]));
            $logv2 = new JsonBreApprovalStatusLog();
            $logv2->rule_id = 0;
            $logv2->application_id = $reasonDetails['application_id'];
            $logv2->reason = $reasonDetails['reason'];
            $logv2->request_batch_id = $req_id;
            $logv2->status = 0;
            $logv2->created_at = Carbon::now()->toDateTimeString();
            $logv2->save();
            return;
        }
        $data = $response->getData();
        $decoded_response = isset($data->res) ? json_decode($data->res) : "";
        $bre_api_payload = isset($data->req) ? $data->req : "";
        $this->cwFactory->logger(json_encode(['title' => 'Storing BRE Logs', 'application_id' => $bre_api_payload->identifier, 'req_id'=> $req_id, 'request'=>$data->req, 'response'=>$data->res, 'rules_ignored'=>$this->bre_rules_ignored]));
        $log = new BreLog();
        $log->application_id = $bre_api_payload->identifier;
        $log->request = json_encode($data->req);
        $log->response = json_encode($decoded_response);
        $log->rule_ignored = json_encode($this->bre_rules_ignored);
        $log->created_at = Carbon::now()->toDateTimeString();
        $log->request_batch_id = $req_id;
        $log->save();
        if($rejection) {
            return;
        }
        if (!empty($decoded_response)) {
            foreach ($decoded_response as $key => $value) {
                // Store the key as a variable
                if ($key === 'status') {
                    continue; // This skips the rest of the loop for this iteration
                }
                $logv2 = new JsonBreApprovalStatusLog();
                $pattern = '/(\d+)$/';
                $matches = [];
                $rule_id = preg_match($pattern, $key, $matches) ? (int)$matches[1] : 0;
                $logv2->rule_id = $rule_id;
                $logv2->application_id = $bre_api_payload->identifier;
                $logv2->failed_condition = isset($value->failed_condition) ? $value->failed_condition->key : "";
                $logv2->reason = isset($value->failed_condition->reason)? $value->failed_condition->reason : "";
                $logv2->request_batch_id = $req_id;
                $logv2->status=(int)$value->policy_status;
                $logv2->created_at = Carbon::now()->toDateTimeString();
                $logv2->save();
            }
        }
    }

    public function getJsonApprovalLogs($id)
    {
        $approvalCheck = JsonBreApprovalStatusLog::where('application_id', $id)->orderBy('created_at', 'desc')->first();
        if (isset($approvalCheck->id)) {
            return JsonBreApprovalStatusLog::with('rule')->where('application_id', $id)->where('request_batch_id', $approvalCheck->request_batch_id)->get();
        }
        return false;
    }

    public function fetchLatestJsonBreResponse($application_id){
        $bre_log = DB::selectOne("SELECT id, response from bre_logs where application_id = ? order by created_at desc limit 1", [$application_id]);
        return $bre_log;
    }

    public function runAutoDecisionBRE($payload) {
        $curl = curl_init();
        curl_setopt_array($curl, array(
            CURLOPT_URL => _env('BRE_API_UAT_URL'),
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 120,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => "POST",
            CURLOPT_POSTFIELDS => json_encode($payload),
            CURLOPT_HTTPHEADER => array(
                "Content-Type: application/json",
            ),
        ));
        $res = curl_exec($curl);
        $data = json_decode($res, 1);
        $httpcode = curl_getinfo($curl, CURLINFO_HTTP_CODE);
        $err = curl_error($curl);
        curl_close($curl);
        return response()->json(['req' => $payload, 'res'=> $res, 'code' => $httpcode], 200);
    }
}