<?php

namespace App\Lib\WhatsApp;

use App\Constants\Status;
use App\Events\ReceiveMessage;
use App\Lib\AiAssistantLib\Gemini;
use App\Lib\AiAssistantLib\OpenAi;
use App\Lib\CurlRequest;
use App\Models\AiAssistant;
use App\Models\Message;
use Carbon\Carbon;
use Exception;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Http;

class WhatsAppLib
{
    public function sendCtaUrlMessage($toNumber, $whatsappAccount, $ctaUrl)
    {

        $phoneNumberId    = $whatsappAccount->phone_number_id;
        $accessToken      = $whatsappAccount->access_token;

        $url       = $this->getWhatsAppBaseUrl() . "{$phoneNumberId}/messages";

        $data = [
            'messaging_product' => 'whatsapp',
            'recipient_type'    => 'individual',
            'to'                => $toNumber,
            'type'              => 'interactive'
        ];

        $interactive = [
            'type'  => 'cta_url',
            'header' => $ctaUrl->header,
            'body'  => $ctaUrl->body,
            'action' => $ctaUrl->action
        ];

        if (!empty($ctaUrl->footer) && count($ctaUrl->footer) > 0) {
            $interactive['footer'] = $ctaUrl->footer;
        }

        $data['interactive'] = $interactive;

        try {

            $response = Http::withHeaders([
                'Authorization' => "Bearer {$accessToken}"
            ])->post($url, $data);
            $responseData = $response->json();

            if (!is_array($responseData) || !count($responseData)) {
                throw new Exception("Something went wrong");
            }

            if (isset($responseData['error']) || !isset($responseData['messages'])) {
                throw new Exception(@$responseData['error']['error_data']['details'] ?? @$responseData['error']['message'] ?? "Something went wrong");
            }

            if ($response->failed()) {
                throw new Exception("Message sending failed");
            }

            return [
                'whatsAppMessage' => $responseData['messages'],
                'ctaUrlId'        => $ctaUrl->id,
                'mediaId'         => null,
                'mediaUrl'        => null,
                'mediaPath'       => null,
                'mediaCaption'    => null,
                'mediaFileName'   => null,
                'messageType'     => 'url',
                'mimeType'        => null,
                'mediaType'       => null
            ];
        } catch (Exception $ex) {
            throw new Exception($ex->getMessage());
        }
    }

    public function messageSend($request, $toNumber, $whatsappAccount)
    {
        $phoneNumberId    = $whatsappAccount->phone_number_id;
        $accessToken      = $whatsappAccount->access_token;

        $url       = $this->getWhatsAppBaseUrl() . "{$phoneNumberId}/messages";
        $mediaLink = $this->getWhatsAppBaseUrl() . "{$phoneNumberId}/media";

        $mediaId       = null;
        $mediaUrl      = null;
        $mediaPath     = null;
        $mediaCaption  = null;
        $mediaFileName = null;
        $mimeType      = null;
        $mediaType     = null;

        $data = [
            'messaging_product' => 'whatsapp',
            'recipient_type'    => 'individual',
            'to'                => $toNumber,
        ];

        if ($request->hasFile('image')) {
            $file          = $request->file('image');
            $mediaUpload   = $this->uploadMedia($mediaLink, $file, $accessToken);

            $mediaId       = $mediaUpload['id'];
            $mediaCaption  = $request->message;
            $data['type']  = 'image';
            $data['image'] = [
                'id'      => $mediaId,
                'caption' => $mediaCaption
            ];
            $mediaType     = 'image';
            $mimeType      = mime_content_type($file->getPathname());
        } else if ($request->hasFile('document')) {
            $file             = $request->file('document');
            $mediaUpload      = $this->uploadMedia($mediaLink, $file, $accessToken);
            $mediaId          = $mediaUpload['id'];
            $mediaCaption     = $request->message;
            $mediaFileName    = $request->file('document')->getClientOriginalName();
            $data['type']     = 'document';
            $data['document'] = [
                'id'       => $mediaId,
                'caption'  => $mediaCaption,
                'filename' => $mediaFileName
            ];
            $mediaType        = 'document';
            $mimeType         = mime_content_type($file->getPathname());
        } else if ($request->hasFile('video')) {
            $file          = $request->file('video');
            $mediaUpload   = $this->uploadMedia($mediaLink, $file, $accessToken);
            $mediaId       = $mediaUpload['id'];
            $mediaCaption  = $request->message;
            $data['type']  = 'video';
            $data['video'] = [
                'id'      => $mediaId,
                'caption' => $mediaCaption
            ];
            $mediaType     = 'video';
            $mimeType      = mime_content_type($file->getPathname());
        } else if ($request->hasFile('audio')) {
            $file          = $request->file('audio');
            $mediaUpload   = $this->uploadMedia($mediaLink, $file, $accessToken);
            $mediaId       = $mediaUpload['id'];
            $mediaCaption  = $request->message;
            $data['type']  = 'audio';
            $data['audio'] = [
                'id'      => $mediaId
            ];
            $mediaType     = 'audio';
            $mimeType      = mime_content_type($file->getPathname());
        } else {
            $data['type'] = 'text';
            $data['text'] = [
                'body' => $request->message
            ];
        }

        try {

            if ($mediaId) {
                $mediaUrl = $this->getMediaUrl($mediaId, $accessToken)['url'];
            }

            if ($mediaId && $mediaUrl && ($request->hasFile('image') || $request->hasFile('audio'))) {
                $mediaPath = $this->storedMediaToLocal($mediaUrl, $mediaId, $accessToken, $whatsappAccount->user_id);
            }
            $response = Http::withHeaders([
                'Authorization' => "Bearer {$accessToken}"
            ])->post($url, $data);
            
            $responseData = $response->json();

            if (!is_array($responseData) || !count($responseData)) {
                throw new Exception("Something went wrong");
            }

            if (isset($responseData['error']) || !isset($responseData['messages'])) {
                throw new Exception(@$responseData['error']['message'] ?? "Something went wrong");
            }

            if ($response->failed()) {
                throw new Exception("Message sending failed");
            }

            return [
                'whatsAppMessage' => $responseData['messages'],
                'ctaUrlId'        => 0,
                'mediaId'         => $mediaId,
                'mediaUrl'        => $mediaUrl,
                'mediaPath'       => $mediaPath,
                'mediaCaption'    => $mediaCaption,
                'mediaFileName'   => $mediaFileName,
                'messageType'     => $data['type'],
                'mimeType'        => $mimeType ?? null,
                'mediaType'       => $mediaType ?? null
            ];
        } catch (Exception $ex) {
            throw new Exception($ex->getMessage());
        }
    }

    public function messageResend(object $message, $toNumber, $whatsappAccount)
    {
        $phoneNumberId    = $whatsappAccount->phone_number_id;
        $accessToken      = $whatsappAccount->access_token;

        $url       = $this->getWhatsAppBaseUrl() . "{$phoneNumberId}/messages";

        $mediaId = $message->media_id ?? null;
        $mediaCaption = $message->media_caption ?? null;
        $mediaFileName = $message->media_filename ?? null;

        $data = [
            'messaging_product' => 'whatsapp',
            'to'                => $toNumber,
            'type'              => 'text'
        ];

        if ($message->media_id && $message->message_type == Status::IMAGE_TYPE_MESSAGE) {
            $data['type']  = 'image';
            $data['image'] = [
                'id'      => $mediaId,
                'caption' => $mediaCaption
            ];
        } else if ($message->media_id && $message->message_type == Status::DOCUMENT_TYPE_MESSAGE) {
            $data['type']     = 'document';
            $data['document'] = [
                'id'       => $mediaId,
                'caption'  => $mediaCaption,
                'filename' => $mediaFileName
            ];
        } else if ($message->media_id && $message->message_type == Status::VIDEO_TYPE_MESSAGE) {
            $data['type']  = 'video';
            $data['video'] = [
                'id'      => $mediaId,
                'caption' => $mediaCaption
            ];
        } else {
            $data['type'] = 'text';
            $data['text'] = [
                'body' => $message->message
            ];
        }

        try {

            $response = Http::withHeaders([
                'Authorization' => "Bearer {$accessToken}"
            ])->post($url, $data);

            $responseData = $response->json();

            if (!is_array($responseData) || !count($responseData)) {
                throw new Exception("Something went wrong");
            }

            if (isset($responseData['error']) || !isset($responseData['messages'])) {
                throw new Exception(@$responseData['error']['message'] ?? "Something went wrong");
            }

            if ($response->failed()) {
                throw new Exception("Message sending failed");
            }

            return [
                'whatsAppMessage' => $responseData['messages']
            ];
        } catch (Exception $ex) {
            throw new Exception($ex->getMessage());
        }
    }

    public function uploadMedia($mediaUrl, $file, $accessToken)
    {
        $filePath = $file->getRealPath();
        $fileName = $file->getClientOriginalName();

        $postData = [
            'messaging_product' => 'whatsapp',
        ];

        $headers = [
            "Authorization: Bearer {$accessToken}",
        ];

        $response = CurlRequest::curlFileUpload(
            $mediaUrl,
            $postData,
            'file',
            $filePath,
            $fileName,
            $headers
        );

        $data = json_decode($response, true);

        if (!is_array($data) || isset($data['error']) || !isset($data['id'])) {
            $errorMessage = "Failed to upload media";
            if (isset($data['error']['error_user_msg'])) {
                $errorMessage = $data['error']['error_user_msg'];
            }
            if ($data['error']['message']) {
                $errorMessage = $data['error']['message'];
            }
            throw new Exception($errorMessage);
        }

        return $data;
    }

    function getSessionId($appId, array $fileData, $accessToken)
    {
        try {

            $url      = "https://graph.facebook.com/v23.0/{$appId}/uploads";
            $response = Http::post($url, [
                'file_name'    => $fileData['name'],
                'file_length'  => $fileData['size'],
                'file_type'    => $fileData['type'],
                'access_token' => $accessToken
            ]);
            $data = $response->json();
            if ($response->failed() || !is_array($data) || !isset($data['id'])) {
                throw new Exception(@$data['error']['message'] ?? "Couldn\'t upload your header image! Please try again later.");
            }
            return $data;
        } catch (Exception $ex) {
            throw new Exception($ex->getMessage() ?? "Couldn\'t upload your header image! Please try again later.");
        }
    }


    function getMediaHandle($sessionId, $accessToken, $filePath, $mimeType)
    {
        try {

            $cleanSessionId = str_replace('upload:', '', $sessionId);
            $url            = "https://graph.facebook.com/v23.0/upload:$cleanSessionId";
            $fileContents   = file_get_contents($filePath);

            $response = Http::withHeaders([
                'Authorization' => "OAuth $accessToken",
                'file_offset'   => '0',
            ])->withBody($fileContents, $mimeType)
                ->post($url);

            $data = $response->json();
            if ($response->failed() || !is_array($data) || !isset($data['h'])) {
                throw new Exception(@$data['error']['error_user_msg'] ?? @$data['error']['message']);
            }
            return $data['h'];
        } catch (Exception $ex) {
            throw new Exception($ex->getMessage() ?? "Failed to get the media handle! Please try again later.");
        }
    }

    public function getMediaUrl($mediaId, $accessToken)
    {
        $url = $this->getWhatsAppBaseUrl() . "{$mediaId}";

        $response = CurlRequest::curlContent($url, [
            "Authorization: Bearer {$accessToken}"
        ]);

        $data = json_decode($response, true);

        if (!is_array($data) || isset($data['error']) || !isset($data['url'])) {
            throw new Exception(@$data['error']['error_user_msg'] ?? @$data['error']['message'] ?? "Failed to load the media URL. Please try again later.");
        }

        return $data;
    }

    public function getWhatsAppBaseUrl()
    {
        return "https://graph.facebook.com/v22.0/";
    }

    public function storedMediaToLocal($mediaUrl, $mediaId, $accessToken, $userId)
    {
        try {
            $response = Http::withHeaders([
                'Authorization' => "Bearer {$accessToken}",
            ])->get($mediaUrl);

            if ($response->failed()) {
                throw new Exception("Message sending fail for the download media");
            }

            $fileContent = $response->body();
            $mimeType    = $response->header('Content-Type');

            $fileExtension = explode('/', $mimeType)[1];
            $fileName      = "{$mediaId}.{$fileExtension}";

            $parentFolder = getFilePath('conversation');
            $subFolder    = "{$userId}/" . date('Y/m/d');
            $folderPath   = $parentFolder . "/" . $subFolder;
            $filePath     = $folderPath . "/" . $fileName;

            if (!file_exists($folderPath)) {
                mkdir($folderPath, 0755, true);
            }

            file_put_contents($filePath, $fileContent);

            return $subFolder . "/" . $fileName;
        } catch (Exception $ex) {
            throw new Exception($ex->getMessage());
        }
    }

    public function  submitTemplate($businessAccountId, $accessToken, $templateData = [])
    {
        $header = [
            'Content-Type: application/json',
            'Authorization: Bearer ' . $accessToken
        ];

        try {

            $response = CurlRequest::curlPostContent($this->getWhatsAppBaseUrl() . "{$businessAccountId}/message_templates", $templateData, $header);
            $data     = json_decode($response, true);

            if (!is_array($data) || isset($data['error'])) {
                throw new Exception(@$data['error']['error_user_msg'] ?? @$data['error']['message'] ?? "Something went wrong");
            }
            return $data;
        } catch (Exception $ex) {
            throw new Exception($ex->getMessage() ?? "Something went wrong");
        }
    }

    public function sendAutoReply($user, $conversation,$message)
    {
        $this->checkConversationLastMessage($conversation);

        $contact  = $conversation->contact;
        $userAiSetting = $user->aiSetting;

        if (!$userAiSetting) return;

        if($userAiSetting->status == Status::DISABLE || !$contact || $conversation->needs_human_reply == Status::YES) return;

        $provider = [
            'openai' => OpenAi::class,
            'gemini' => Gemini::class
        ];

        $activeProvider   = AiAssistant::active()->first();
        
        if(!$activeProvider) return;

        if($user->ai_assistance == 0) return;

        $aiAssistantClass = $provider[$activeProvider->provider];

        $aiAssistant = new $aiAssistantClass();

        if($userAiSetting->status == Status::ENABLE){
            $systemPrompt    = $userAiSetting->system_prompt;
            $aiResponse      = $aiAssistant->getAiReply($systemPrompt, $message);
            $whatsappAccount = $user->currentWhatsapp();
            
            if($aiResponse['success'] == true)
            {
                if($aiResponse['response'] == null && $userAiSetting->fallback_response != null) {
                    $request = new Request([
                        'message' => $userAiSetting->fallback_response,
                    ]);
                    $conversation->needs_human_reply = Status::YES;
                    $conversation->save();    
                }else {
                    $request = new Request([
                        'message' => $aiResponse['response'],
                    ]);
                }

                $messageSend = $this->messageSend($request,$contact->mobileNumber, $whatsappAccount);
                extract($messageSend);

                $message                      = new Message();
                $message->user_id             = $user->id;
                $message->whatsapp_account_id = $whatsappAccount->id;
                $message->whatsapp_message_id = $whatsAppMessage[0]['id'];
                $message->conversation_id     = $conversation->id;
                $message->type                = Status::MESSAGE_SENT;
                $message->message             = $request->message;
                $message->media_id            = $mediaId;
                $message->message_type        = getIntMessageType($messageType);;
                $message->media_caption       = $mediaCaption;
                $message->media_filename      = $mediaFileName;
                $message->media_url           = $mediaUrl;
                $message->media_path          = $mediaPath;
                $message->mime_type           = $mimeType;
                $message->media_type          = $mediaType;
                $message->status              = Status::MESSAGE_SENT;
                $message->ordering            = Carbon::now();
                $message->ai_reply            = Status::YES;
                $message->save();

                $conversation->last_message_at = Carbon::now();
                $conversation->save();

                $html                        = view('Template::user.inbox.single_message', compact('message'))->render();
                $lastConversationMessageHtml = view("Template::user.inbox.conversation_last_message", compact('message'))->render();

                event(new ReceiveMessage($whatsappAccount->id, [
                    'html'            => $html,
                    'message'         => $message,
                    'newMessage'      => true,
                    'newContact'      => false,
                    'lastMessageHtml' => $lastConversationMessageHtml,
                    'unseenMessage'   => $conversation->unseenMessages()->count() < 10 ? $conversation->unseenMessages()->count() : '9+',
                    'lastMessageAt'   => showDateTime(Carbon::now()),
                    'conversationId'  => $conversation->id,
                    'mediaPath'       => getFilePath('conversation')
                ]));
            }
        }
    }

    private function checkConversationLastMessage($conversation)
    {
        $lastMessage = Message::latest('id')->where('conversation_id', $conversation->id)->skip(1)->first();
        if($lastMessage && $lastMessage->created_at->diffInHours(Carbon::now()) > 24) {
            $conversation->needs_human_reply = Status::NO;
            $conversation->save();
        }
    }
}
