<?php

namespace App\Console\Commands;

use App\Models\ActiveVulnerabilitiesPerWeek;
use App\Models\Asset;
use App\Models\AssetVulnerability;
use Illuminate\Console\Command;
use App\Services\Security\Crowdstrike\CrowdstrikeSyncDataService;
use App\Services\Security\Crowdstrike\CrowdstrikeApiService;
use Exception;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;
use App\Notifications\Security\CrowdstrikeSyncNotification;

class CrowdstrikeSync extends Command
{

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'Crowdstrike:sync';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Syncing assets with crowdstrike service and fetching details from it';


    /**
     * Create a new command instance.
     *
     * @param CrowdstrikeApiService      $apiService      The service responsible for interacting with the Crowdstrike API.
     * @param CrowdstrikeSyncDataService $syncDataService The service responsible for syncing data with Crowdstrike.
     *
     * @return void
     */
    public function __construct(protected CrowdstrikeApiService $apiService, protected CrowdstrikeSyncDataService $syncDataService)
    {
        parent::__construct();
    }


    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        try {
            $connectionCheckResponse = $this->apiService->checkConnection();

            if (!$connectionCheckResponse) {
                $msg = 'FAILED! Invalid CrowdStrike credentials';
                $this->sendNotification($msg);

                return $this->info(json_encode([
                    'status'  => 'error',
                    'message' => $msg
                ]));
            }

            // We can use this if we found any cases that serial # edited on crowdstrike.
            // Asset::whereNotNull('crowdstrike_id')->update(['crowdstrike_id' => null]);

            // Empty vulnerbilities table and related data before each sync.
            AssetVulnerability::truncate();
            ActiveVulnerabilitiesPerWeek::truncate();

            $updatedAssetsCount = $this->apiService->syncCrowdstrikeIdToAssets();

            Asset::whereNotNull('crowdstrike_id')->select('id', 'crowdstrike_id')->chunk(1000, function ($crowdStrikeConnectedAssets) {
                // Sync vulnerability data to the assets and save in db.
                $this->syncVulnerabilityData($crowdStrikeConnectedAssets);
            });

            $vulnerabilityCount = AssetVulnerability::count();

            $message = "Crowdstrike ID added for " . $updatedAssetsCount . " assets and total " . $vulnerabilityCount . " vulnerabilities found.";
            $this->sendNotification($message);

            return $this->info($message);
        } catch (Exception $e) {
            Log::error("Error occured in crowdstrike sync : " . $e->getMessage());
        }
    }


    /**
     * Synchronizes vulnerability data.
     *
     * @param Collection $crowdStrikeConnectedAssets The CrowdStrike connected assets for which vulnerability data needs to be synced.
     *
     * @return void
     */
    public function syncVulnerabilityData($crowdStrikeConnectedAssets)
    {
        foreach ($crowdStrikeConnectedAssets as $asset) {
            // Fetching vulnerabilities for the device.
            $vulnerabilities = $this->apiService->getVulnerbilitiesWithLimit($asset->crowdstrike_id);

            // Save vulnerabilities to the asset.
            $this->syncDataService->saveAssetVulnerability($vulnerabilities, $asset->id);
        }
    }


    /**
     * Send a Slack notification with the given message.
     *
     * @param string $message The message content to be sent as a notification.
     *
     * @return void
     */
    public function sendNotification($message)
    {
        if (config('teqtivity-notification-mails.slack')) {
            Notification::route('slack', config('teqtivity-notification-mails.slack'))->notify(new CrowdstrikeSyncNotification($message));
        }
    }
}
