<?php

namespace App\Console\Commands\Report;

use App\Helpers\Utility\SymEncrypt;
use App\Models\Organisation;
use App\Models\Webhook;
use App\Models\WebhookHookType;
use App\Models\WebhookMessage;
use App\Models\WebhookMessageStatus;
use App\Models\WebhookUsageType;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;

class HookReportSend extends \App\Console\Commands\BaseCommand
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'hook:report-send';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Send report links via webhook...';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        $sym_encrypt = new SymEncrypt(config('creatia.reporting.encryption_key'));
        
        // We need to segregate things based upon ASN to keep things memory friendly and parameter size friendly
        Organisation::select(
            'id',
        )->each(function($organisation) use($sym_encrypt) {

            $hooks = Webhook::where('organisation_id', '=', $organisation->id)->where('usage_type', '=', WebhookUsageType::Reporting->value)->select('id', 'hook', 'hook_type')->get()->toArray();

            if(sizeof($hooks) !== 0 ) {

                foreach($hooks as $hook) {

                    // Messages
                    $messages = WebhookMessage::where([
                        [ 'webhook_id', '=', $hook['id'] ],
                        [ 'status', '=', WebhookMessageStatus::Pending->value ],
                        [ 'message_type', '=', WebhookUsageType::Reporting->value ],
                        [ 'organisation_id', '=', $organisation->id ],
                        [ 'created_at', '>', Carbon::now()->subMinutes(60) ]
                    ])
                    ->select(
                        'id',
                        'payload'
                    )
                    ->orderBy('id', 'asc')
                    ->get()
                    ->toArray();

                    // Decrypt the webhook
                    $decrypted_hook = $sym_encrypt->decrypt($hook['hook']);

                    foreach($messages as $message) {

                        // Check if it has already been sent - We don't ever want duplicates

                        try {
                            $outcome = false;

                            if(WebhookMessage::where([['webhook_id', '=', $hook['id']], [DB::raw('payload::text'), '=', json_encode($message['payload'])], ['id', '!=', $message['id']]])->count()) {
                                $outcome = true;
                            }
                            else {

                                $this->log_event("Webhook notification is being sent.", [
                                    'organisation' => $organisation->id,
                                    'hook' => $hook['id'],
                                    'message' => $message['id']
                                ]);

                                switch($hook['hook_type']) {
                                    case WebhookHookType::Slack->value:
                                        $outcome = Http::withHeaders([ 'Content-Type' => 'application/json' ])->timeout(6)->post($decrypted_hook, [
                                            'text' => implode("\n", $message['payload']),
                                        ])->successful();

                                        break;
                                    case WebhookHookType::Discord->value:
                                        $outcome = Http::withHeaders([ 'Content-Type' => 'application/json' ])->timeout(6)->post($decrypted_hook, [
                                            'content' => implode("\n", $message['payload']),
                                        ])->successful();
                                        break;
                                    case WebhookHookType::Raw->value:
                                        $outcome = Http::withHeaders([ 'Content-Type' => 'application/json' ])->timeout(6)->post($decrypted_hook, [
                                            'text' => implode("\n", $message['payload']),
                                        ])->successful();
                                        break;
                                }

                                sleep(2.5); // We sleep for a small period to ensure we do not end up triggering any rate limits
                            }
    
                            WebhookMessage::where('id', '=', $message['id'])->update([
                                'sent_at' => Carbon::now(),
                                'status' => $outcome ? WebhookMessageStatus::Sent->value : WebhookMessageStatus::Error
                            ]);
                        }
                        catch(\Throwable $ex) {

                            $this->log_error("Webhook notification is failed to send.", [
                                'organisation' => $organisation->id,
                                'hook' => $hook['id'],
                                'message' => $message['id'],
                                'exception' => $ex
                            ]);

                            WebhookMessage::where('id', '=', $message['id'])->update([
                                'sent_at' => Carbon::now(),
                                'status' => WebhookMessageStatus::Error->value
                            ]);
                        }
                    }

                    // Unset it as soon as we're done with. We should keep it out of memory once it's done.
                    unset($decrypted_hook);
                }
            }
        });
        return 0;
    }
}