<?php

namespace App\Http\Controllers;

use App\ConsumerApplicationFiles;
use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
use App\ABFL\Services\Services;
use App\AccountAggregator;
use App\AdminLoginLogs;
use App\Avanse\Services\AvanseServices;
use App\CallLogs;
use App\CampaignsJobProvider;
use App\Events\V1\EventPayTMUpdate;
use App\Http\Controllers\Admin\AccountingController;
use App\Http\Controllers\Admin\RepaymentController;
use App\KarzaAPI;
use App\Centers;
use App\CityModel;
use App\Comments;
use App\ConsumerApplicationElement;
use App\DigitalSignDetails;
use App\Events\V1\EventAccountAggregatorWebhook;
use App\Events\V1\EventPirimidWebhook;
use App\Events\V1\EventDigioWebhook;
use App\Events\V1\EventAutoDecision;
use App\Events\V1\EventHypothecation;
use App\FCMNotification;
use App\finBoxIntegration;
use App\GCMRegistraion;
use App\Http\Controllers\Admin\UtkarshController;
use App\Http\Controllers\TestingController;
use App\Http\Controllers\Admin\ConsumerApplicationController;
use App\Http\Controllers\Admin\ConsumerDocument;
use App\Http\Controllers\Service\ServiceController;
use App\Http\Controllers\Admin\EMICalculation;
use App\JobProvider;
use App\KMGP2P;
use App\Leads;
use App\Lenders;
use App\Lib\Common\Utility;
use App\LoanHypothecation\LoanHypothecation;
use App\LoanHypothecation\DigioHypothecation;
use App\LoanHypothecationModel;
use App\LoanManagement;
use App\MandatoryDocumentListLogs;
use App\Merchant;
use App\MerchantLocation;
use App\MerchantOffers;
use App\Method\Homeville\Payload\Payload;
use App\OnfidoIntegration;
use App\OnlinePayment;
use App\OtpManager;
use App\PaymentEmi;
use App\PaymentTransaction;
use App\ProFormaInvoice;
use App\ProjectLead\ProjectLeads;
use App\PWAApplicationForm;
use App\QueueLog;
use App\Shortio;
use App\Slack;
use App\StatusLogs;
use App\TelecomDetails;
use App\Templates;
use App\Trello;
use App\User;
use App\UserTypes;
use App\Utkarsh\Utkarsh;
use App\UtkarshKYCData;
use App\WebHookLog;
use Hashids\Hashids;
use App\ConsumerApplication;
use App\ConsumerApplicationAdditional;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Config;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Lang;
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Redirect;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\Session;
use Illuminate\Support\Facades\Storage;
use Illuminate\Validation\Rules\In;
use App\Events\V1\EventPostFormCompleted;
use App\Logging\CloudWatchLoggerFactory;
use App\DigioVKYC;
use App\UserGeolocation;
use App\ConsentLog;
use Illuminate\Support\Facades\App;
use App\KYCDetails;
use App\Vendor;
use App\Console\Commands\VendorWebhook;
use App\Http\Controllers\WebhookController;
use App\Http\Controllers\Admin\autoBRE;
use App\Imports\TestImport;
use App\Http\Controllers\Grade\GradeController;

/**
 * Mirrors the JSON rule-engine logic for auto-updating a consumer
 * application's offer via the mConnect flow.
 *
 * NOTE: the webhook/merchant-notification step from the original JSON
 * (webhook_row lookup, webhookPayload, mConnectQueueInput) has been
 * intentionally omitted, per request.
 *
 * NOTE: the original JSON snippet was truncated right after building
 * `reviseJobPayload` — before the actual UPDATE/INSERT statements that
 * persist the new offer and queue the document-revision job. Steps 11-12
 * below are a logical completion of that intent (apply the new offer,
 * log the audit comment, queue the revise job) based on context, NOT
 * lifted verbatim from the source. Table/column names there
 * (`application_comments`, `jobs.payload`, etc.) are reasonable guesses —
 * adjust them to match your actual schema.
 */
class WhatsappAgentController
{
    /**
     * @param Request $request      expects $request->offer_id, e.g. "1234_b", "5678_r"
     * @param int     $applicationId
     * @return array
     */
    public function reviseOffer(Request $request, $applicationId)
    {
        // 2. Application must be in an editable state
        $appState = DB::table('consumer_application')
            ->where('id', $applicationId)
            ->value('state');

        $validStates = ['waiting for decision', 'pre approved', 'cf approved', 'approved'];

        if (!in_array($appState, $validStates, true)) {
            return $this->errorResponse(
                '201004',
                'CF300',
                "Auto update cannot be done because status is {$appState} and offer cannot be updated."
            );
        }

        // 4. Snapshot current ("previous") offer state
        $appPrevState = DB::table('consumer_application')
            ->where('id', $applicationId)
            ->select('offer_id as prev_offer_id', 'loan_amount as prev_loan_amount', 'approved_amount as prev_approved_amount')
            ->first();

        $prevCalcState = DB::table('emi_calculation')
            ->where('application_id', $applicationId)
            ->select('table_of_offers_logic as prev_logic')
            ->first();

        $prevOfferDetails = DB::table('merchant_offers')
            ->where('id', $appPrevState->prev_offer_id ?? null)
            ->select('slug as prev_slug', 'interest_rate as prev_roi', 'advance_emi as prev_adv')
            ->first();

        // 5. Decode incoming offer_id suffix -> tof_type / logic_type
        $requestOfferId = (string) $request->offer_id;

        if ($this->endsWith($requestOfferId, '_b')) {
            $tofType   = 'bureau_surrogate_limit';
            $logicType = 'bureau_surrogate_limit';
        } elseif ($this->endsWith($requestOfferId, '_r')) {
            $tofType   = 'residual_emi';
            $logicType = 'residual_emi';
        } elseif ($this->endsWith($requestOfferId, '_a')) {
            $tofType   = 'BANKING-ABB';
            $logicType = 'residual_emi';
        } elseif ($this->endsWith($requestOfferId, '_p')) {
            $tofType   = 'POWER-SAVING';
            $logicType = 'residual_emi';
        } else {
            $tofType   = 'minimum_tenure';
            $logicType = 'minimum_tenure';
        }

        // 6. Strip suffix to get the real merchant_offers.id (mirrors
        //    SUBSTRING_INDEX(offer_id, '_', 1))
        $rawOfferId = (int) explode('_', $requestOfferId)[0];

        // 7. Resolve eligible amount/EMI for the new offer.
        //    priority 1: loan_decision_approved_amount (policy-type specific)
        //    priority 2: loan_decision_table (standard tof types only)
        $decisionRow = DB::table('loan_decision_approved_amount')
            ->where('application_id', $applicationId)
            ->where('offer_id', $rawOfferId)
            ->where('policy_type', $tofType)
            ->whereNull('deleted_at')
            ->select('calculated_approved_amount as final_loan_amount', 'emi_amount as final_emi')
            ->first();

        if (!$decisionRow && in_array($tofType, ['minimum_tenure', 'residual_emi', 'bureau_surrogate_limit'], true)) {
            $decisionTable = DB::table('loan_decision_table')
                ->where('application_id', $applicationId)
                ->where('offer_id', $rawOfferId)
                ->whereNull('deleted_at')
                ->first();

            if ($decisionTable) {
                $decisionRow = (object) [
                    'final_loan_amount' => $tofType === 'bureau_surrogate_limit'
                        ? $decisionTable->bureau_surrogate_limit_amount
                        : $decisionTable->eligible_amount,
                    'final_emi' => $tofType === 'bureau_surrogate_limit'
                        ? $decisionTable->bureau_surrogate_limit_emi
                        : $decisionTable->emi,
                ];
            }
        }

        if (!$decisionRow) {
            return $this->errorResponse(
                '201006',
                'CF300',
                'Selected offer is invalid or not applicable for this application'
            );
        }

        // 8. Reject duplicate hits (same offer id AND same approved amount)
        $isDuplicate = ((int) $rawOfferId === (int) $appPrevState->prev_offer_id)
            && ((float) $decisionRow->final_loan_amount === (float) $appPrevState->prev_approved_amount);

        if ($isDuplicate) {
            return $this->errorResponse(
                '201007',
                'CF300',
                'Duplicate hit. This offer is already applied to the application.'
            );
        }

        // 9. New offer details, needed for the audit comment
        $newOfferDetails = DB::table('merchant_offers')
            ->where('id', $rawOfferId)
            ->select('slug as new_slug', 'interest_rate as new_roi', 'advance_emi as new_adv')
            ->first();

        // 10. Build the audit-trail comment (mirrors the CONCAT step)
        $comment = $this->buildComment($prevCalcState, $tofType, $appPrevState, $prevOfferDetails, $decisionRow, $newOfferDetails);

        // Payload for the document-revision job (webhook notification omitted)
        $reviseJobPayload = json_encode([
            'list' => [
                'cam'            => '1',
                'proforma'       => '1',
                'sanction'       => '1',
                'loanagreement'  => '0',
                'emi_schedule'   => '1',
                'payment_links'  => '1',
            ],
            'auth_user_id' => 107,
            'app_id'       => (string) $applicationId,
            'source'       => 'update-table-of-offers',
        ]);

        // --- Below this line: logical completion, not in the source JSON ---

        // 11/12. Persist the new offer + log comment + queue the revise job
        DB::transaction(function () use ($applicationId, $rawOfferId, $decisionRow, $logicType, $comment, $reviseJobPayload) {
            DB::table('consumer_application')
                ->where('id', $applicationId)
                ->update([
                    'offer_id'         => $rawOfferId,
                    'approved_amount'  => $decisionRow->final_loan_amount,
                    'updated_at'       => Carbon::now(),
                ]);

            DB::table('emi_calculation')
                ->where('application_id', $applicationId)
                ->update([
                    'table_of_offers_logic' => $logicType,
                    'updated_at'             => Carbon::now(),
                ]);

            DB::table('comments')->insert([
                'application_id' => $applicationId,
                'comment'        => $comment,
                'created_at'     => Carbon::now(),
                'updated_at'     => Carbon::now(),
                'user_id'        => 107,
            ]);

            DB::table('jobs')->insert([
                'application_id' => $applicationId,
                'from'            => 'reviseDocsViaMconnect',
                'payload'         => $reviseJobPayload,
                'created_at'      => Carbon::now(),
                'updated_at'      => Carbon::now(),
                'available_at'      => Carbon::now(),
            ]);
        });

        return [
            'return_code' => '200000',
            'CF_code'     => 'CF100',
            'message'     => 'Offer updated successfully.',
        ];
    }

    /**
     * Builds the HTML audit comment, mirroring the source CONCAT logic.
     */
    private function buildComment($prevCalcState, $tofType, $appPrevState, $prevOfferDetails, $decisionRow, $newOfferDetails)
    {
        $comment = 'Offer changed/updated by system via mconnect flow.<br><br>';

        if (($prevCalcState->prev_logic ?? null) !== $tofType) {
            $comment .= "Offer type changed from {$prevCalcState->prev_logic} to {$tofType}.<br><br>";
        }

        $comment .= 'Applied Amount - Rs. ' . $appPrevState->prev_loan_amount
            . ' Applied Tenure - ' . $prevOfferDetails->prev_slug
            . ', ' . $prevOfferDetails->prev_roi . '% interest, '
            . $prevOfferDetails->prev_adv . ' Advance EMIs <br>'
            . 'Eligible Amount - Rs. ' . $decisionRow->final_loan_amount
            . ' Eligible Tenure - ' . $newOfferDetails->new_slug
            . ', ' . $newOfferDetails->new_roi . '% interest, '
            . $newOfferDetails->new_adv . ' Advance EMIs<br><br>'
            . 'Please find below the list of documents required to process your original loan request:<br>'
            . 'Please submit 12 months bank statement & Income proof to check for Eligibility on applied amount';

        return $comment;
    }

    private function endsWith($haystack, $needle)
    {
        return substr($haystack, -strlen($needle)) === $needle;
    }

    private function errorResponse($returnCode, $cfCode, $message)
    {
        return [
            'return_code' => $returnCode,
            'CF_code'     => $cfCode,
            'message'     => $message,
        ];
    }

    /**
     * Move all documents from the temp S3 bucket (folders: <unique_id>)
     * to the main S3 bucket (folder: <application_id>).
     *
     * For each unique ID in $uniqueIds, every file stored at
     *   s3_temp://<unique_id>/<file_name>
     * is fetched and written to:
     *   s3://<application_id>/<file_name>
     *
     * @param  int|string        $id        The application ID (destination folder name)
     * @param  array|string      $uniqueIds A single unique ID or an array of unique IDs
     *                                       (source folder names on s3_temp)
     * @return array             Summary of migrated / failed files keyed by unique ID
     */
    public function migrateS3Documents($id, $uniqueIds)
    {
        $cwFactory = new CloudWatchLoggerFactory();

        try {
            $s3Temp    = Storage::disk('s3_temp');
            $s3Main    = Storage::disk('s3');
            $destFolder = ltrim((string) $id, '/');

            // Normalise: accept both a single ID and an array of IDs
            $uniqueIdList = is_array($uniqueIds) ? $uniqueIds : [$uniqueIds];

            $results = [];

            foreach ($uniqueIdList as $uniqueId) {
                $sourceFolder = ltrim((string) $uniqueId, '/');
                $migrated     = [];
                $failed       = [];

                // List every file inside the unique_id folder on the temp bucket
                $allItems = $s3Temp->listContents($sourceFolder, true);

                foreach ($allItems as $item) {
                    // Skip directory entries
                    if (isset($item['type']) && $item['type'] === 'dir') {
                        continue;
                    }

                    $sourcePath = $item['path'] ?? ($item['key'] ?? null);

                    if (!$sourcePath) {
                        $cwFactory->logger(json_encode([
                            'title'          => 'whatsapp s3 source file empty',
                            'application_id' => $id ?? '',
                            'unique_id'      => $uniqueId,
                            'file_item'         => $item
                        ]));
                        continue;
                    }

                    try {
                        // Extract bare file name by stripping the unique_id prefix
                        // so the destination is: application_id/file_name
                        $fileName = ltrim(substr($sourcePath, strlen($sourceFolder)), '/');
                        $destPath = $destFolder . '/' . $fileName;

                        $fileContents = $s3Temp->get($sourcePath);
                        $s3Main->put($destPath, $fileContents);

                        // Insert a new record in consumer_application_files
                        // pointing to the migrated destination path
                        // ConsumerApplicationFiles::create([
                        //     'consumer_application_id' => $id,
                        //     'file_path'               => $destPath,
                        //     'file_type'               => pathinfo($fileName, PATHINFO_FILENAME),
                        // ]);


                        $file_id = $this->createConsumerApplicationFile($id, $destPath, pathinfo($fileName, PATHINFO_FILENAME));

                        if($file_id){
                            // Remove from temp bucket after a successful copy
                            $s3Temp->delete($sourcePath);
                        }else{
                            $cwFactory->logger(json_encode([
                                'title'          => 'whatsapp s3 file createion error',
                                'application_id' => $id ?? '',
                                'unique_id'      => $uniqueId,
                                'source'         => $sourcePath
                            ]));
                        }

                        $migrated[] = ['source' => $sourcePath, 'dest' => $destPath];
                    } catch (\Exception $fileEx) {
                        $failed[] = ['source' => $sourcePath, 'error' => $fileEx->getMessage()];
                        $cwFactory->logger(json_encode([
                            'title'          => 'whatsapp s3 migration file error',
                            'application_id' => $id ?? '',
                            'unique_id'      => $uniqueId,
                            'source'         => $sourcePath,
                            'message'        => $fileEx->getMessage(),
                            'line'           => $fileEx->getLine(),
                        ]));
                    }
                }

                $results[$uniqueId] = ['migrated' => $migrated, 'failed' => $failed];
            }

            return true;
        } catch (\Exception $e) {
            $cwFactory->logger(json_encode([
                'title'          => 'whatsapp s3 migration error',
                'application_id' => $id ?? '',
                'message'        => $e->getMessage(),
                'line'           => $e->getLine(),
                'trace'          => $e->getTraceAsString(),
            ]));
        }
    }

    public function createConsumerApplicationFile($id, $file_path, $file_type){
        $consumer_application_file = new ConsumerApplicationFiles();
        $consumer_application_file->consumer_application_id = $id;
        $consumer_application_file->file_type = $file_type;
        $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->discom_district = "";
        $consumer_application_file->discom_mobile = "";
        $consumer_application_file->doc_ref_id = "";

        $consumer_application_file->finbit_analysis_date = "";
        $consumer_application_file->bp_number = "";
        $consumer_application_file->accountUID = "";
        $consumer_application_file->file_path = $file_path;
        $consumer_application_file->created_by = 107;
        $consumer_application_file->image_score = "";
        $consumer_application_file->m_generated = 0;
        $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();

        return $consumer_application_file->id;
    }

    public function autoRejectJSONBRE(Request $request) {
        try {
            $id = $request->input('application_id');
            $applicationDetails = ConsumerApplication::with('merchant.grade_v1')->where('id', $id)->first();
            $grade = $applicationDetails->merchant->grade_v1;
            $source = $request->input('source', 'bot');
            $tracking_id = $request->input('tracking_id', '');
            $account_aggregator_id = $request->input('account_aggregator_id', '');

            $autoRejection = new \App\Http\Controllers\Grade\AutoRejection($request);
            $result = $autoRejection->autoRejectJSONBRE($grade, $id, $source, $tracking_id, $account_aggregator_id);

            if ($result) {
                $consumer_application = ConsumerApplication::find($id);
                $consumer_application->state = "rejected";
                $consumer_application->save();
                StatusLogs::insert([
                    'application_id' => $id,
                    'user_id' => 107,
                    'status' => 'rejected',
                    'created_at' => Carbon::now()->toDateTimeString()
                ]);
                return response()->json(['status' => 'success', 'message' => 'Application auto rejected successfully']);
            } else {
                return response()->json(['status' => 'failure', 'message' => 'Auto rejection failed or not applicable']);
            }
        } catch (\Exception $e) {
            $cwFactory->logger(json_encode(array('title' => 'auto rejection json bre', 'application_id' => $id ?? '', 'message' => $e->getMessage(), 'line' => $e->getLine(), 'trace' => $e->getTraceAsString())));
            return response()->json(['status' => 'success', 'message' => 'Application auto rejected successfully']);
        }
    }

    public function autoApprovalJSONBRE(Request $request) {
        try{
            $cwFactory = new CloudWatchLoggerFactory();
            $id = $request->input('application_id');
            $request->merge(['source' => 'whatsapp_agent']);

            $consumerUpdateData = [];
            if ($request->filled('loan_amount')) $consumerUpdateData['loan_amount'] = $request->input('loan_amount');
            if ($request->filled('tenure')) $consumerUpdateData['tenure'] = $request->input('tenure');
            if ($request->filled('profession')) $consumerUpdateData['employee_status'] = $request->input('profession');
            if ($request->filled('company_name')) $consumerUpdateData['company_name'] = $request->input('company_name');
            if ($request->filled('permanent_address')) $consumerUpdateData['permanent_address_line_1'] = $request->input('permanent_address');
            if ($request->filled('permanent_pincode')) $consumerUpdateData['permanent_address_pincode'] = $request->input('permanent_pincode');
            if ($request->filled('permanent_city')) $consumerUpdateData['permanent_address_city'] = $request->input('permanent_city');
            if ($request->filled('permanent_state')) $consumerUpdateData['permanent_address_state'] = $request->input('permanent_state');
            if ($request->filled('permanent_address_status')) $consumerUpdateData['permanent_address_status'] = $request->input('permanent_address_status');
            
            if (!empty($consumerUpdateData)) {
                ConsumerApplication::where('id', $id)->update($consumerUpdateData);
            }

            $elementUpdateData = [];
            if ($request->filled('gross_project_amount')) $elementUpdateData['inverter_gross_project_amount'] = $request->input('gross_project_amount');
            if ($request->filled('product_name')) $elementUpdateData['product_name'] = $request->input('product_name');
            if ($request->filled('solar_finance_for')) $elementUpdateData['type_of_customer'] = $request->input('solar_finance_for');
            if ($request->filled('solar_power_type')) $elementUpdateData['solar_power_type'] = $request->input('solar_power_type');
            if ($request->filled('kw_installation')) $elementUpdateData['inverter_kw_installation'] = $request->input('kw_installation');

            if (!empty($elementUpdateData)) {
                ConsumerApplicationElement::where('application_id', $id)->update($elementUpdateData);
            }
            $application = ConsumerApplication::with('category', 'merchant', 'emi', 'creditReport', 'approval','application_element', 'activeOffer')->where('id', $id)->first();
            $consumerApplicationController = new ConsumerApplicationController();
            $gradeController = new GradeController($request);
            $gradeController->generateEMIForDecision($consumerApplicationController, $application, $request, $id);
            $ltv = $gradeController->calculateLTV($application, $application->offer);
            $ltv_column_value = [
                'ltv_gross_project_amount' => $ltv['ltv_gross_project_amount'] ?? 0,
                'ltv_net_loan_amount' => $ltv['ltv_net_loan_amount'] ?? 0,
            ];
            ConsumerApplicationAdditional::where('application_id', $id)->update(['loan_to_value_limit' => json_encode($ltv_column_value)]);
            $testImport = new TestImport(new Request());
            $req_id = $testImport->random_strings(8);
            $tableOfOffers = true;

            $gradeController = new \App\Http\Controllers\Grade\GradeController($request);
            $result = $gradeController->MultiApprovalCheckJSONBRE($id, $req_id, $tableOfOffers);

            if ($result) {
                $consumerAppController = new \App\Http\Controllers\Admin\ConsumerApplicationController();
                $offers = $consumerAppController->getTableOfOffersMConnectPayload($id);
                if ($consumerAppController->isMultiLender($application->merchant_id) > 1) {
                    $status = 'cf approved';
                } else {
                    $status = 'approved';
                }
                \request()->merge(['state' => $status, 'source' => 'whatsapp_agent']);
                $consumerApplicationController->updateStatus($id, true);
                return response()->json(['status' => 'approved', 'offers' => $offers]);
            } else {
                return response()->json(['status' => 'manual_review', 'offers' => []]);
            }
        } catch (\Exception $e) {
            $cwFactory->logger(json_encode(array('title' => 'auto approval json bre', 'application_id' => $id ?? '', 'message' => $e->getMessage(), 'line' => $e->getLine(), 'trace' => $e->getTraceAsString())));
            return response()->json(['status' => 'manual_review', 'offers' => []]);
        }
    }

    public function saveOffer(Request $request) {
        try {
            $applicationId = $request->input('application_id');
            $result = $this->reviseOffer($request, $applicationId);
            
            return response()->json($result);
        } catch (\Exception $e) {
            $cwFactory->logger(json_encode(array('title' => 'save offer json bre', 'application_id' => $applicationId ?? '', 'message' => $e->getMessage(), 'line' => $e->getLine(), 'trace' => $e->getTraceAsString())));
        }
    }
}