<?php
/**
 * Created by PhpStorm.
 * User: sunil
 * Date: 28/11/18
 * Time: 12:39 PM
 */

namespace App;


use App\Http\Controllers\Admin\ConsumerDocument;
use App\Http\Middleware\MongoMiddleware;
use App\MongoDB\Mongo;
use Carbon\Carbon;
use Dompdf\Exception;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Storage;
use App\Logging\CloudWatchLoggerFactory;

class OCRRecognition
{
    private $request;
    private $endPointDevelopment = '';
    private $fileURL = '';
    private $karza_key = '';
    private $production = 'SFXdlWrNYduYwxk';
    private $curl;
    private $link;
    private $s3 = null;
    private $filePath;
    const PUBLIC = 'public';
    const PRIVATE = 'private';
    private $response;
    private $payload;
    private $auto_mask = false;
    private $provider = 'python'; // default

    public function __construct(Request $request){
        $this->request = $request;
        $this->link = ($this->request->has('link')) ? $this->request->get('link') : false;
        
        $pythonBaseUrl = rtrim(_env('CF_PYTHON_OCR_URL', ''), '/');
        $docType = $this->request->get('type', 'aadhaar_card');
        
        $shouldUseOwn = false;
        $pythonUrl = '';
        if ($pythonBaseUrl) {
            if (strpos($docType, 'aadhaar_card') !== false) {
                $pythonUrl = $pythonBaseUrl . '/aadhaar_ocr';
                $shouldUseOwn = _env('OCR_AADHAAR_USE_OWN', false) == 'true';
            } else if (strpos($docType, 'pan_card') !== false) {
                $pythonUrl = $pythonBaseUrl . '/pan_ocr';
                $shouldUseOwn = _env('OCR_PAN_USE_OWN', false) == 'true';
            }
        }

        if ($pythonUrl && $shouldUseOwn) {
            $this->endPointDevelopment = $pythonUrl;
            $this->provider = 'python';
            $this->setup();
        } else {
            $this->setupKarza();
        }
    }

    private function setupKarza() {
        $this->provider = 'karza';
        $this->endPointDevelopment = (_env('KARZAURLV3', ''));
        $this->karza_key = (_env('KARZAKEY', ''));
        if($this->endPointDevelopment && $this->karza_key) {
            $this->setup();
        }
    }

    private function setup(){
        $this->OCRRecognition();
        (new Slack(new Request(['title' => 'OCR Recognition Setup', 'fields' => ['file_url' => $this->fileURL, 'auto_mask' => $this->auto_mask], 'group' => Slack::$STAGE_TESTING])))->send();
        if ($this->provider == 'python') {
            $payloadData = [
                'id' => (int)$this->request->get('id'),
                'user_id' => (int)(Auth::check() ? Auth::user()->id : 107),
                'file_type' => $this->request->get('file_type'),
                'doc_type' => $this->request->has('type') ? $this->request->get('type') : 'aadhar_card'
            ];
            
            if ($this->request->has('file_base64')) {
                $payloadData['fileB64'] = $this->request->get('file_base64');
            } else {
                $payloadData['url'] = $this->fileURL;
            }
            
            $this->payload = json_encode($payloadData);
            $header = [
                'Content-Type: application/json',
                'cache-control: no-cache'
            ];
        } else if($this->request->has('file_object') && $this->request->get('file_object') == true){
            $this->payload = array(
                'file' => curl_file_create( $_FILES['document']['tmp_name'],  $_FILES['document']['type'], $_FILES['document']['name'])
            );
            $this->endPointDevelopment = rtrim($this->endPointDevelopment, '/') . '/ocr/cheque';
            $header = array(
                'cache-control: no-cache',
                "content-type: multipart/form-data",
                'x-karza-key: '.$this->karza_key,
            );
        }else{
            if ($this->request->has('file_base64')) {
                $this->payload = json_encode(['fileB64' => $this->request->get('file_base64'), 'maskAadhaar' => $this->auto_mask]);
            } else {
                $this->payload = json_encode(['url' => $this->fileURL, 'maskAadhaar' => $this->auto_mask]);
            }
            $this->endPointDevelopment = rtrim($this->endPointDevelopment, '/') . '/ocr/kyc';
            $header = array(
                'Content-Type: application/json',
                'cache-control: no-cache',
                'x-karza-key: '.$this->karza_key
            );
        }

        $curl = curl_init();
        curl_setopt_array($curl, array(
            CURLOPT_URL => $this->endPointDevelopment,
            CURLOPT_RETURNTRANSFER => true,
            CURLOPT_ENCODING => "",
            CURLOPT_MAXREDIRS => 10,
            CURLOPT_TIMEOUT => 120,
            CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
            CURLOPT_CUSTOMREQUEST => "POST",
            CURLOPT_POSTFIELDS => $this->payload,
            CURLOPT_HTTPHEADER => $header,
        ));

        $this->curl = $curl;
    }

    private function OCRRecognition(){
        if(!$this->request->has('file_object') && $this->request->get('file_object') == false) {
            if (!$this->link) {
                $applicationFile = ConsumerApplicationFiles::where('consumer_application_id', $this->request->get('id'))->where('file_type', $this->request->get('file_type'))->first(['file_path']);
                if (isset($applicationFile->file_path)) {
                    $s3 = Storage::disk('s3');
                    $this->s3 = $s3;
                    $this->filePath = $applicationFile->file_path;
                    $s3->setVisibility($applicationFile->file_path, static::PUBLIC);
                    $this->fileURL = _env('AWS_ORIGIN') . _env('AWS_BUCKET') . str_replace('//', '/', '/' . $applicationFile->file_path);
                }
            } else {
                $this->fileURL = $this->request->get('file_type');
                $this->auto_mask = $this->request->has('auto_mask') ? $this->request->get('auto_mask') : false;
                if (strpos($this->fileURL, 'X-Amz-') === false && strpos($this->fileURL, _env('AWS_ORIGIN')) !== false) {
                    $file = explode(_env('AWS_ORIGIN'), $this->fileURL);
                    if (count($file) > 1) {
                        $this->filePath = (isset($file[1])) ? $file[1] : '';
                        if(strpos($this->filePath, 'aadhar_card') !== false){
                            $this->s3 = Storage::disk('s3_temp');
                            if(!$this->s3->exists($this->filePath)){
                                $this->s3 = Storage::disk('s3');
                            }
                        } else{
                            $this->s3 = Storage::disk('s3');
                        }
                        $this->s3->setVisibility($this->filePath, static::PUBLIC);

                    }
                }
            }
        }
    }

    public function get(){
        $cwFactory = new CloudWatchLoggerFactory();
        // Log the start of the OCR process
        $cwFactory->logger(json_encode(['msg' => 'OCR Request Started', 'provider' => $this->provider, 'app_id' => $this->request->get('id')]));
        (new Slack(new Request(['title' => 'OCR Request Started', 'fields' => ['provider' => $this->provider, 'app_id' => $this->request->get('id'), 'url' => $this->endPointDevelopment], 'group' => Slack::$STAGE_TESTING])))->send();

        $response = curl_exec($this->curl);

        if (curl_error($this->curl)) {
            $error = curl_error($this->curl);
        }
        $data = json_decode($response, 1);
        $cwFactory = new CloudWatchLoggerFactory();
        $logData = [
            'application_id' => $this->request->get('id'),
            'user_id' => isset(Auth::user()->id) ? Auth::user()->id : '',
            'provider' => $this->provider,
            'api_url' => $this->endPointDevelopment,
            'Request' => is_string($this->payload) ? json_decode($this->payload, 1) : $this->payload,
            'Response' => $data ?? 'Raw: ' . substr($response, 0, 100),
            'error_msg' => $error ?? ''
        ];
        $cwFactory->logger(json_encode(['msg' => 'OCR Attempt Result', 'data' => $logData]));

        // Smart Fallback Trigger
        if ($this->provider == 'python' && (!isset($data['status']) || $data['status'] != 'success' || isset($error))) {
            $errorMsg = $error ?? (isset($data['error_msg']) ? $data['error_msg'] : 'Status Fail');
            $cwFactory->logger(json_encode(['msg' => 'Python OCR Failed - Falling back to Karza', 'app_id' => $this->request->get('id'), 'error' => $errorMsg]));
            (new Slack(new Request(['title' => 'Python OCR Failed - Falling back to Karza', 'fields' => ['error' => $errorMsg, 'app_id' => $this->request->get('id')], 'group' => Slack::$STAGE_TESTING])))->send();
            $this->setupKarza();
            return $this->get();
        }

        if(isset($error)){
            return $error;
        }
        if($response){
            $fileName = 'ocr_'.time();
            if($this->request->has('type')){
                $fileName .= '_'.$this->request->get('type');
            }
            if($this->request->has('id')){
                $fileName .= '_'.$this->request->get('id');
            }
            Storage::disk('s3_temp')->put('/partner/ocr/'.$fileName.'.json', $response);
        }
        if(isset($data['result'])) {
            if(isset($data['result'][0]['type'])) {
                foreach ($data['result'] as $details) {
                    $this->setDocument($details);
                    (new Slack(new Request(['title' => 'OCR response check', 'fields' => ['application_id'=>$this->request->get('id'), 'auto mask' => $this->auto_mask], 'group' => Slack::$STAGE_TESTING])))->send();
                    if (isset($details['details']) && isset($details['details']['imageUrl'])){
                        if(strpos($this->filePath, 'aadhar_card') !== false){
                            if(isset($details['details']['imageUrl']['value'])){
                                $consumer_files = ConsumerApplicationFiles::where('file_path', ltrim($this->filePath,'/'))->first(['file_type', 'file_path', 'applicant_type']);
                                $masked = true;
                                if(!empty($consumer_files)) {
                                    $name = $consumer_files->file_type;
                                }else{
                                    $name = 'aadhaar_card';
                                }
                                $extension = explode('.',$this->filePath);
                                $ext = '.jpg';
                                if(isset($extention)){
                                    $extension = count($extension) > 1 ? $extension[count($extension) - 1] : '.jpeg';
                                    $ext = '.'.$extension;
                                }

                                if($this->request->get('id') !== 0){
                                    $imageContent = null;
                                    // Python Base64 Priority
                                    if (!empty($data['masked_b64'])) {
                                        $imageContent = base64_decode($data['masked_b64']);
                                    } 
                                    // Karza URL Fallback
                                    else if (!empty($details['details']['imageUrl']['value'])) {
                                        $imageContent = @file_get_contents($details['details']['imageUrl']['value']);
                                    }

                                    if ($imageContent) {
                                        $cwFactory->logger(json_encode(['msg' => 'Masked image saved to S3', 'app_id' => $this->request->get('id'), 'source' => (!empty($data['masked_b64']) ? 'Python Base64' : 'Karza URL')]));
                                        $document = new ConsumerDocument(new Request(['type_of_applicant' => isset($consumer_files->applicant_type) ? $consumer_files->applicant_type : 1]));
                                        $document->uploadToS3($name, $this->request->get('id'), $imageContent, $ext, $masked);
                                    } else {
                                        $cwFactory->logger(json_encode(['msg' => 'Failed to resolve masked image content', 'app_id' => $this->request->get('id')]));
                                    }
                                }
                                if(!empty($consumer_files)) {
                                    $s3_temp = Storage::disk('s3_temp');
                                    $s3_temp->delete($this->filePath);
                                    ConsumerApplicationFiles::where('file_path', ltrim($this->filePath,'/'))->update(['deleted_at' => Carbon::now()->toDateTimeString()]);
                                }
                            }
                        }
                    }else{
                        (new Slack(new Request(['title' => 'Masking Failed', 'fields' => ['info' => json_encode($details['details']), 'application_id'=>$this->request->get('id')], 'group' => Slack::$STAGE_TESTING])))->send();
                    }
                }
                if(!empty(array_keys($this->response)) && isset(array_keys($this->response)[0])) {
                    $x=[];
                    $y=[];
                    try{
                        foreach($this->response['details'] as $details=>$value){
                            if($details != 'addressSplit'){
                                $x[$details] = $value['value'];
                            }else{
                                $y=$value;
                            }

                            if (isset($this->response['type']) && strtolower($this->response['type']) == 'pan_card' && isset($x['name']) && $x['name']){
                                $consumerApplication = new ConsumerApplication();
                                $nameSplitDetails = $consumerApplication->getNameSplitAttribute(1, $x['name']);
                            }
                        }

                        $responsedata=array_merge($x,$y);
                        $responsedata = array_merge($responsedata,['document' => $this->response[array_keys($this->response)[ 0]]]);
                        if (isset($nameSplitDetails)){
                            $responsedata = array_merge($responsedata,$nameSplitDetails);
                        }
                        if (isset($responsedata['pin']) && !empty($responsedata['pin']) && $responsedata['pin'] !== '') {
                            try{
                                $statecity = ''; //GooglePlaces::getCityFromPincodeV1($responsedata['pin']);
                                $datacitydata = ''; //$statecity->getData();
//                                if ($datacitydata->status == "OK") {
                                    $responsedata['city'] = ''; //$datacitydata->district ?? '';
                                    $responsedata['state'] = ''; //$datacitydata->state ?? '';
//                                }
                            } catch (Exception $e){
                                (new Slack(new Request(['title'=>'Error 2', 'fields'=>['Message' => $e->getMessage(), 'Line' => $e->getLine(), 'File' =>$e->getFile(), 'Trace' => $e->getTraceAsString()]])))->send();
                            }
                        }
                        return array_merge($responsedata);
                    } catch (Exception $e){
                        (new Slack(new Request(['title'=>'Error 2', 'fields'=>['Message' => $e->getMessage(), 'Line' => $e->getLine(), 'File' =>$e->getFile(), 'Trace' => $e->getTraceAsString()]])))->send();
                    }
                }
                return false;
            }else{
                return ($data['result']);
            }
        }else{
            return false;
        }
    }

    public function setDocument($data){
        if(isset($data['type'])) {
            switch ($data['type']) {
                case 'pan_card':
                    $this->response = $data;
                    $this->response[$data['type']] = $data['details'];
                    break;
                case 'aadhaar_front_top':
                    if (!empty($this->response['aadhar_card'])) {
                        $this->response['aadhar_card'] = array_merge($this->response['aadhar_card'], $data['details']);
                    } else {
                        $this->response['aadhar_card'] = $data['details'];
                    }
                    break;
                case 'aadhaar_front_bottom':
                    if (!empty($this->response['aadhar_card'])) {
                        $this->response['aadhar_card'] = array_merge($this->response['aadhar_card'], $data['details']);
                    } else {
                        $this->response['aadhar_card'] = $data['details'];
                    }
                    break;
                case 'aadhaar_back':
                    if (!empty($this->response['aadhar_card'])) {
                        $this->response['aadhar_card'] = array_merge($this->response['aadhar_card'], $data['details']);
                    } else {
                        $this->response['aadhar_card'] = $data['details'];
                    }
                    break;
                case 'passport_front':
                    if (!empty($this->response['passport'])) {
                        $this->response['passport'] = array_merge($this->response['passport'], $data['details']);
                    } else {
                        $this->response['passport'] = $data['details'];
                    }
                    break;
                case 'passport_back':
                    if (!empty($this->response['passport'])) {
                        $this->response['passport'] = array_merge($this->response['passport'], $data['details']);
                    } else {
                        $this->response['passport'] = $data['details'];
                    }
                    break;
                default :
                    $this->response = $data;
            }
        }else{
            $this->response = $data;
        }
    }
}
