<?php

namespace App\Http\Controllers;

use Helper;
use Gemini;
use Gemini\Data\Blob;
use Gemini\Enums\MimeType;
use App\Helpers\Inspiring;
use App\Models\Capture;
use App\Models\CaptureItem;
use App\Models\Device;
use App\Models\Subscriber;
Use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\File;

class WebhookController extends Controller
{
  public function index(Request $request)
  {
    // file_put_contents('webhookdata.json', json_encode($request->all(), JSON_PRETTY_PRINT));
    $rawData = $request->rawdata ?? null;
    $key_id = $request->key_id ?? null; // $key_id is message_key_id
    $receiver = $request->receiver ?? null;
    $mentioned = $request->mentioned ?? null;
    $text = $request->text ?? null;
    $sender = $request->sender ?? null;
    $token = $request->token ?? null;
    $appurl = $request->appurl ?? null;
    $nodeurl = $request->nodeurl ?? null;
    $recname = $request->recname ?? null;
    $recnumber = $request->recnumber ?? null;
    $devtype = $request->devtype ?? null;
    $mediaurl = $request->mediaurl ?? null;

    // catch new incoming data:
    // $invoiceId  = !empty($request->invoiceId) ? $request->invoiceId : null;

    $phone = str_ends_with($receiver, '@s.whatsapp.net') ? preg_replace('/\D/', '', $receiver) : $receiver;
    $keyword = strtolower($text);

    $device = Device::where('sender', '=', $sender)->first();

    if (empty($phone)) {
      return;
    }

    $isReplied = false;
    $extWebhook = is_array($device->ext_webhook) ? $device->ext_webhook : json_decode($device->ext_webhook, true);
    $extWebhookUrl = $extWebhook['url'] ?? '';
    $extWebhookData = $extWebhook['data'] ?? '';
    if (!$isReplied && isset($extWebhookUrl)) {
      $response = $this->externalWebhook($extWebhookUrl, $extWebhookData, $sender, $receiver, $text, $device->token, $request);
    }

    if ($keyword === 'incall' && !empty($device->incall_msg)) {
      $result = ['data' => ['text' => $device->incall_msg]];
      echo json_encode($result);
      $isReplied = true;
    }

    if (!$isReplied && str_ends_with($keyword, '(warming up)')) {
      $warmingupMessage = ['text' => Inspiring::quote()];
      $result = ['data' => $warmingupMessage];
      echo json_encode($result);
      $isReplied = true;
    }

    if (!$isReplied && isset($device->welcome_msg)) {
      if (empty($device->welcome_cust)) {
        $device->welcome_cust = '[]';
      }
      $custPhones = json_decode($device->welcome_cust, true);
      if (!in_array($phone, $custPhones)) {
        array_push($custPhones, $phone);
        $device->welcome_cust = json_encode($custPhones);
        $device->save();
        $welcomeMessage = ['text' => $device->welcome_msg];
        $result = ['data' => $welcomeMessage];
        echo json_encode($result);
        $isReplied = true;
      }
    }

    // If enabled here, do not need to enable user's webhook to use Subscribe/Unsubscribe feature
    if (!$isReplied) $isReplied = $this->subUnsub($keyword, $phone, $device, $recname); // If want custom for each device, then enable it on each device webhook file (/public/users/{id}/assets/wh_123123123123_abcdefghijklmns.php) and disable this one.

    // If enabled here, do not need to enable user's webhook to use Capture feature
    if (!$isReplied) $isReplied = $this->captureWebhook($keyword, $phone, $device, $recname, $mediaurl); // If want custom for each device, then enable it on each device webhook file (/public/users/{id}/assets/wh_123123123123_abcdefghijklmns.php) and disable this one.

    // If enabled here, do not need to enable user's webhook to use OpenAI feature
    if (!$isReplied) $isReplied = $this->openAi($keyword, $phone, $device, $recname, $recnumber, $mentioned);
    // } // If want custom for each device, then enable it on each device webhook file (/public/users/{id}/assets/wh_123123123123_abcdefghijklmns.php) and disable this one.

    // If enabled here, do not need to enable user's webhook to use GeminiAi feature
    if (!$isReplied) $isReplied = $this->geminiAi($keyword, $phone, $device, $recname, $recnumber, $mentioned, $rawData);
    // } // If want custom for each device, then enable it on each device webhook file (/public/users/{id}/assets/wh_123123123123_abcdefghijklmns.php) and disable this one.

    if (!$isReplied) $isReplied = $this->globalWebhook($keyword);

    if (!$isReplied && ($device->user->role === 'admin' || $device->user->package->webhook)) {
      $fileWebhook = Helper::fileWebhook($device->user_id, $device->sender);
      include $fileWebhook;
    }
  }

  private function subUnsub($keyword, $phone, $device, $recname)
  {
    if (!empty($device->unsubscribe_keyword) && $keyword === strtolower($device->unsubscribe_keyword)) {
      Subscriber::updateOrInsert(['phone' => $phone], ['user_id' => $device->user_id, 'sender' => $device->sender, 'name' => $recname, 'status' => 0]);
      $result = ['data' => ['text' => str_replace('{name}', $recname, $device->unsubscribe_message)]];
      echo json_encode($result);
      return true;
    } else {
      if (!empty($device->subscribe_keyword) && $keyword === strtolower($device->subscribe_keyword)) {
        Subscriber::updateOrInsert(['phone' => $phone], ['user_id' => $device->user_id, 'sender' => $device->sender, 'name' => $recname, 'status' => 1]);
        $result = ['data' => ['text' => str_replace('{name}', $recname, $device->subscribe_message)]];
        echo json_encode($result);
        return true;
      }
      Subscriber::updateOrInsert(['phone' => $phone], ['user_id' => $device->user_id, 'sender' => $device->sender, 'name' => $recname]);
      return false;
    }
  }

  private function captureWebhook($keyword, $phone, $device, $recname, $mediaurl=null)
  {
    $capture = Capture::where('phone', '=', $phone)->first();
    $question = __('finish');
    
    if (str_starts_with($keyword, __('capture') . __('>>'))) {
      $arrs = explode(__('>>'), $keyword);
      if ($arrs[1] === __('start')) {
        $filePath = Helper::filePath($device->user_id, 'subjects.json');
        $subjects = json_decode(file_get_contents($filePath), true);
        if (empty($arrs[2])) {
          return false;
        }
        $data = $subjects[$arrs[2]];
        $data['wa_name'] = $recname;
        $data['wa_phone'] = $phone;
        Capture::updateOrInsert(['phone' => $phone], ['device_id' => $device->id, 'user_id' => $device->user_id, 'name' => $recname, 'subject' => $arrs[2], 'status' => __('start'), 'data' => json_encode($data)]);
        foreach ($data as $key => $value) {
          if (empty($value)) {
            $question = $key;
            break;
          }
        }
        $result = [
          'data' => [
            'text' => $question . ':',
          ],
        ];
      } elseif ($arrs[1] === __('finish')) {
        $msg = json_decode(Capture::where('phone', '=', $phone)->value('data'));
        Capture::where('phone', '=', $phone)->update(['status' => __('finish')]);
        $captureItem = new CaptureItem();
        $captureItem->capture_id = $capture->id;
        $captureItem->user_id = $capture->user_id;
        $captureItem->subject = $capture->subject;
        $captureItem->data = $capture->data;
        $captureItem->save();
        $result = [
          'data' => ['text' => !empty($msg->{__('finish')}) ? $msg->{__('finish')} : __('_MsgCaptureSave')],
        ];
      } elseif ($arrs[1] === __('stop')) {
        $msg = json_decode(Capture::where('phone', '=', $phone)->value('data'));
        Capture::updateOrInsert(['phone' => $phone], ['device_id' => $device->id, 'user_id' => $device->user_id, 'name' => $recname, 'status' => __('finish')]);
        $result = [
          'data' => ['text' => !empty($msg->{__('stop')}) ? $msg->{__('stop')} : __('_MsgCaptureStop')],
        ];
      }
    } elseif (!empty($capture) && $capture->device_id == $device->id && strtolower($capture->status) !== __('finish')) {
      $data = empty(json_decode($capture->data, true)) ? [] : json_decode($capture->data, true);
      $question = __('finish');
      $breakLoop = false;
      foreach ($data as $key => $value) {
        if (empty($value)) {
          if ($breakLoop) {
            $question = $key;
            break;
          } else {
            $data[$key] = !empty($mediaurl) ? $mediaurl : $keyword;
            $capture->data = json_encode($data);
            $capture->save();
            $question = __('finish');
            $breakLoop = true;
          }
        }
      }
      if (strtolower($question) === __('finish')) {
        $completedData = '';
        foreach ($data as $key => $value) {
          if (strtolower($key) === __('finish')) {
            break;
          }
          $completedData = $completedData . $key . "\n>> *" . $value . "*\n\n";
        }
        $completedData = $completedData . __('capture') . __('>>') . __('finish') . ' (' . __('to save') . ")\n" . __('capture') . __('>>') . __('stop') . ' (' . __('to stop') . ')';
        $result = [
          'data' => [
            'text' => __('Please confirm information below') . ":\n\n" . $completedData,
          ],
        ];
      } else {
        $result = [
          'data' => [
            'text' => $question . ':',
          ],
        ];
      }
    }

    if (!empty($result)) {
      echo json_encode($result);
      return true;
    }
    return false;
  }

  private function openAi($keyword, $phone, $device, $recname, $recnumber, $mentioned)
  {
    $oai = json_decode($device->openai, true);
    if (empty($oai['apikey'])) return;
    if (str_starts_with($oai['apikey'], 'sk-') && (empty($oai['contain']) || (!empty($oai['contain']) && stripos($keyword, trim($oai['contain'])) > -1) || $mentioned)) {
      $dirPath = File::ensureDirectoryExists(public_path("users/{$device->user_id}/oai/{$device->sender}"));
      $filePath = public_path("users/{$device->user_id}/oai/{$device->sender}/{$phone}.json");
      if (!file_exists($filePath)) file_put_contents($filePath, '[{ "role": "system", "content": "' . str_replace('"', "'", $oai['persona']) . '" }]');
      $msgContent = trim(str_ireplace([$oai['contain'], '@' . $device->sender], [''], $keyword));
      if (strtolower($msgContent) === 'reset chat') {
        if (file_exists($filePath)) unlink($filePath);
        echo json_encode(['text' => str_ends_with($recnumber, '@g.us') ? "@$recname,\n\nOpenai chat history successfully deleted." : 'Openai chat history successfully deleted.']);
        return true;
      }
      $messages = json_decode(file_get_contents($filePath), true);
      array_push($messages, ['role' => 'user', 'content' => str_replace('"', "'", str_replace('"', "'", $msgContent))]);
      if ($oai['save_chats'] == 1) {
        file_put_contents($filePath, json_encode($messages));
      }

      if (stripos(trim($msgContent), trim($oai['image_contain'])) > -1) {
        $apiurl = 'https://api.openai.com/v1/images/generations';
        $data = [
          'prompt' => trim(str_replace(strtolower($oai['contain']), '', $keyword)),
          'model' => $oai['image_model'],
        ];
        $image = json_decode($this->openaiPost($apiurl, $oai['apikey'], $data));
        if (!empty($image?->data[0])) {
          $aiRes = $image->data[0];
          $result = [
            'data' => [
              'image' => ['url' => $aiRes?->url],
              'mimetype' => 'image/png',
              'caption' => $aiRes?->revised_prompt ?? '',
              'mentions' => [$recnumber],
            ],
          ];
          echo json_encode($result);
          return true;
        } else if (!empty($image?->error?->message)) {
          $result = [
            'data' => [
              'text' => str_ends_with($phone, '@g.us') ? "@$recname, {$image->error->message}" : $image->error->message,
              'mentions' => [$recnumber],
            ],
          ];
          echo json_encode($result);
          return true;
        }
      } else {
        $apiurl = 'https://api.openai.com/v1/chat/completions';
        $data = [
          'messages' => $messages,
          'model' => $oai['model'],
          'max_tokens' => intval($oai['max_tokens']),
        ];
        $completion = json_decode($this->openaiPost($apiurl, $oai['apikey'], $data));
        if (!empty($completion?->choices[0]?->message)) {
          $aiRes = $completion->choices[0]->message;
          if ($oai['save_chats'] == 1) {
            array_push($messages, $aiRes);
            file_put_contents($filePath, json_encode($messages));
          }
          $result = [
            'data' => [
              'text' => str_ends_with($phone, '@g.us') ? "@$recname, $aiRes->content" : $aiRes->content,
              'mentions' => [$recnumber],
            ],
          ];
          echo json_encode($result);
          return true;
        } else if (!empty($completion?->error?->message)) {
          $result = [
            'data' => [
              'text' => str_ends_with($phone, '@g.us') ? "@$recname, {$completion->error->message}" : $completion->error->message,
              'mentions' => [$recnumber],
            ],
          ];
          echo json_encode($result);
          return true;
        }
      }
    }
    return false;
  }

  private function geminiAi($keyword, $phone, $device, $recname, $recnumber, $mentioned, $rawData)
  {
    $gai = is_array($device->openai) ? $device->openai : json_decode($device->openai, true);
    if (empty($gai['geminiai_apikey'])) return;
    if (str_starts_with($gai['geminiai_apikey'], 'AI') && (empty($gai['geminiai_contain']) || (!empty($gai['geminiai_contain']) && stripos($keyword, trim($gai['geminiai_contain'])) > -1) || $mentioned)) {
      $dirPath = File::ensureDirectoryExists(public_path("users/{$device->user_id}/gai/{$device->sender}"));
      $filePath = public_path("users/{$device->user_id}/gai/{$device->sender}/{$phone}.json");
      if (!file_exists($filePath)) file_put_contents($filePath, $gai['geminiai_persona'] ? '["' . str_replace('"', "'", $gai['geminiai_persona']) . '"]' : '["start chat"]');
      $msgContent = trim(str_ireplace([$gai['geminiai_contain'], '@' . $device->sender], [''], $keyword));
      if (strtolower($msgContent) === 'reset chat') {
        if (file_exists($filePath)) unlink($filePath);
        echo json_encode(['text' => str_ends_with($recnumber, '@g.us') ? "@$recname,\n\nOpenai chat history successfully deleted." : 'Openai chat history successfully deleted.']);
        return true;
      }
      $messages = json_decode(file_get_contents($filePath), true);
      if (empty($messages)) $messages = ['start chat'];
      $client = Gemini::client($gai['geminiai_apikey']);

      if (stripos(trim($msgContent), trim($gai['geminiai_image_contain'])) > -1 && $rawData['message']['imageMessage']['jpegThumbnail']) {
        // file_put_contents(public_path('webhookresult.txt'), $rawData['message']['imageMessage']['jpegThumbnail']);
        $response = $client->geminiProVision()->generateContent([
          $gai['geminiai_image_contain'],
          new Blob(
            mimeType: MimeType::IMAGE_JPEG,
            data: $rawData['message']['imageMessage']['jpegThumbnail']
          )
        ]);
        $text = $response->text();
        $result = [
          'data' => [
            'text' => str_ends_with($phone, '@g.us') ? "@$recname, {$text}" : $text,
            'mentions' => [$recnumber],
          ],
        ];
        echo json_encode($result);
        return true;
      } else {
        $chat = $client->geminiPro15()->startChat(history: $messages);
        $response = $chat->sendMessage($msgContent);
        $text = $response->text();
        if ($gai['geminiai_save_chats'] == '1') {
          array_push($messages, str_replace('"', "'", $keyword));
          array_push($messages, str_replace('"', "'", $text));
          file_put_contents($filePath, json_encode($messages));
        }
        $result = [
          'data' => [
            'text' => str_ends_with($phone, '@g.us') ? "@$recname, {$text}" : $text,
            'mentions' => [$recnumber],
          ],
        ];
        echo json_encode($result);
        return true;
      }
    }
    return false;
  }

  private function openaiPost($apiurl, $apikey, $data)
  {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_HTTPHEADER, ['Content-Type: application/json', "Authorization: Bearer $apikey"]);
    curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
    curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($data));
    curl_setopt($ch, CURLOPT_URL, $apiurl);
    curl_setopt($ch, CURLOPT_TIMEOUT, 30);
    $response = curl_exec($ch);
    $err = curl_error($ch);
    curl_close($ch);
    return $response;
  }

  private function externalWebhook($extWebhookUrl, $extWebhookData, $sender, $receiver, $text, $token, $request)
  {
    $extWebhookUrl = str_replace(
        ['{sender}', '{receiver}', '{text}', '{token}', '{key_id}', '{recnumber}', '{recname}', '{devtype}', '{mentioned}', '{appurl}', '{nodeurl}'],
        [$sender, $receiver, $text, $token, $request['key_id'] ?? '', $request['recnumber'] ?? '', $request['recname'] ?? '', $request['devtype'] ?? '', $request['mentioned'] ?? '', $request['appurl'] ?? '', $request['nodeurl'] ?? ''],
        $extWebhookUrl
      );
    if (!filter_var($extWebhookUrl, FILTER_VALIDATE_URL)) return response(['error' => 'The external webhook url must be a valid URL string.']);
    if (empty($extWebhookData)) {
      $data = ['sender' => $sender, 'receiver' => $receiver, 'text' => $text, 'token' => $token];
      $params = ['key_id', 'recnumber', 'recname', 'devtype', 'mentioned', 'appurl', 'nodeurl'];
      foreach ($params as $param) {
        $data[$param] = $request[$param] ?? '';
      }
    } else {
      $jsonString = str_replace(
        ['{sender}', '{receiver}', '{text}', '{token}', '{key_id}', '{recnumber}', '{recname}', '{devtype}', '{mentioned}', '{appurl}', '{nodeurl}'],
        [$sender, $receiver, $text, $token, $request['key_id'] ?? '', $request['recnumber'] ?? '', $request['recname'] ?? '', $request['devtype'] ?? '', $request['mentioned'] ?? '', $request['appurl'] ?? '', $request['nodeurl'] ?? ''],
        $extWebhookData
      );
      $data = is_array($jsonString) ? $jsonString : json_decode($jsonString, true);
      if (json_last_error() !== JSON_ERROR_NONE) return response(['error' => 'The external webhook data must be a valid JSON string.']);
    }
    return Helper::curlPost($extWebhookUrl, $data);
  }

  private function globalWebhook($keyword)
  {
    switch ($keyword) {
      case 'list':
        $rows1 = [['title' => 'Row 1', 'description' => 'Hello its description 1', 'rowId' => 'rowid1'], ['title' => 'Row 2', 'description' => 'Hello its description 2', 'rowId' => 'rowid2']];
        $rows2 = [['title' => 'Row 3', 'description' => 'Hello its description 3', 'rowId' => 'rowid3'], ['title' => 'Row 4', 'description' => 'Hello its description 4', 'rowId' => 'rowid4']];
        $sections = [['title' => 'Section 1', 'rows' => $rows1], ['title' => 'Section 2', 'rows' => $rows2]];
        $listMessage = [
          'text' => 'This is a list',
          'description' => 'This is a description',
          'footer' => 'nice footer, link: https://google.com',
          'title' => 'Amazing boldfaced list title',
          'buttonText' => 'Required, text on the button to view the list',
          'sections' => $sections,
        ];
        $result = ['data' => $listMessage];
        break;
    
      case 'sample list':
        $rows = [
          [
            "header" => "Row Header 111",
            "title" => "Row Title 111",
            "description" => "Row description 111",
            "id" => "rowId111"
          ],
          [
            "header" => "Row Header 112",
            "title" => "Row Title 112",
            "description" => "Row description 112",
            "id" => "rowId112"
          ],
        ];
        $list = [
          [
            "title" => "Section 11",
            "rows" => $rows,
          ],
          [
            "title" => "Section 12",
            "rows" => $rows,
          ],
        ];
        $sections = [
          [
            "buttonText" => "Button text 1",
            "list" => $list,
          ],
        ];
        $listMessage = [
          "text" => "testing...",
          "title" => "List message title",
          "description" => "List message description",
          "footer" => "Footer text",
          "sections" => $sections,
        ];
        $result = ['data' => $listMessage];
        break;
    }
    if (!empty($result)) {
      echo json_encode($result);
      return true;
    }
  }

  public function show(Request $request)
  {
    $device = Device::where('id', '=', $request->device_id)->first();
    if (auth()->user()->id != $device->user_id && auth()->user()->role !== 'admin') {
      return redirect()
        ->back()
        ->with(['danger_alert' => __('You are not allowed to show Webhook')]);
    }
    $fileWebhook = Helper::fileWebhook($device->user_id, $device->sender);
    if ($device->webhook === url('/api/webhook')) {
      $content = file_get_contents($fileWebhook);
    } else {
      $content =
        __('Custom webhook url is not editable') .
        "\n" .
        __('If you want it to be editable:') .
        "\n" .
        '1. ' .
        __('Go to menu devices') .
        "\n" .
        '2. ' .
        __('Click edit button') .
        "\n" .
        '3. ' .
        __('Delete custom webhook url. Use the default webhook url (EMPTY). Save') .
        "\n" .
        '4. ' .
        __('Copy custom webhook code. Paste the code inside this window') .
        "\n" .
        '5. ' .
        __('Save');
    }
    return view('/webhook-show', compact('device', 'content'));
  }

  public function save(Request $request)
  {
    $device = Device::where('id', '=', $request->input('device_id'))->first();
    if (auth()->user()->id != $device->user_id && auth()->user()->role !== 'admin') {
      return redirect()
        ->back()
        ->with(['danger_alert' => __('You are not allowed to update Webhook')]);
    }
    $fileWebhook = Helper::fileWebhook($device->user_id, $device->sender);
    file_put_contents($fileWebhook, $request->input('content'));
    return redirect()
      ->back()
      ->with(['success_alert' => __('Successfully update Webhook') . ': ' . $device->sender]);
  }

  public function reset(Request $request)
  {
    $device = Device::where('id', '=', $request->input('device_id'))->first();
    if (auth()->user()->id != $device->user_id && auth()->user()->role !== 'admin') {
      return redirect()
        ->back()
        ->with(['danger_alert' => __('You are not allowed to reset Webhook')]);
    }
    $awg_orderId = Helper::setting('awg_orderId');
    if (file_exists(public_path("users/{$device->user_id}/assets/wh_{$device->sender}_{$awg_orderId}.php"))) {
      unlink(public_path("users/{$device->user_id}/assets/wh_{$device->sender}_{$awg_orderId}.php"));
    }
    return redirect()
      ->back()
      ->with(['success_alert' => __('Successfully reset Webhook') . ': ' . $device->sender]);
  }
}
