<?php

namespace App\Services\DiscoveryTools\JumpCloud;

use App\Http\Traits\Asn\AsnAssetTrait;
use App\Http\Traits\AutoCreateAssignTrait;
use App\Models\AssetStatus;
use App\Models\AssetType;
use App\Models\JumpCloudDevices;
use App\Repositories\DiscoveryTools\JumpCloudRepository;
use App\Services\Integrations\JumpCloudIntegration;
use Carbon\Carbon;
use Exception;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;

class JumpCloudSyncService
{
    use AutoCreateAssignTrait;

    public function __construct(protected JumpCloudIntegration $jumpCloudIntegration, protected JumpCloudRepository $repository) {}

    /**
     * Upload the JumpCloudAPI data to Database
     */
    public function import()
    {
        $page = 0;

        try {
            while (1) {
                $response = $this->jumpCloudIntegration->getAllDevicesData($page);
                if ($response === false) {
                    break;
                }

                if (!isset($data['results']) || empty($data['results'])) {
                    break;
                }

                $this->saveJumpCloudData($data['results']);
                $page++;
            }

            return true;
        } catch (Exception $e) {
            Log::channel('daily')->error('JumpCloud API sync error: ' . $e->getMessage());
            return false;
        }
    }

    /**
     * Save the Jumpcloud data to DB.
     *
     * @param Array $results  APi response
     *
     * Return boolean
     */

    public function saveJumpCloudData($results)
    {
        foreach ($results as $key => $data) {
            if ($data) {
                // Log::channel('daily')->info('JumpCloud Data: ' . json_encode($data));
                $dataToUpdate = [];
                $dataToUpdate = $this->parseJumpCloudApiResponse($data);
                if ($dataToUpdate['serial_no'] == '') {
                    continue;
                }

                $this->updateOrCreateJumpcloudData($dataToUpdate);
            }
        }

        return true;
    }

    /**
     * Parse the API response and create an array for insert into DB
     *
     * @param $response  JumpCloud API response
     *
     * @return Array
     */
    public function parseJumpCloudApiResponse($response)
    {
        $data = [];
        $data['user_name']      = isset($response['userMetrics'][0]['userName']) ? $response['userMetrics'][0]['userName'] : '';
        $data['serial_no']      = $response['serialNumber'] ?? '';
        $data['asset_id']       = $this->getAssetId($data['serial_no']);
        $data['device_id']      = $response['id'] ?? '';
        $data['os']             = $response['os'] ?? null;
        $data['os_version']     = $response['version'] ?? null;
        $data['platform']       = $response['osFamily'] ?? null;
        $data['ip_address']     = $response['remoteIP'] ?? null;
        $data['manufacturer']   = $response['hwVendor'] ?? '';
        $data['last_seen']      = format_kandji_check_in_date($response['lastContact'] ?? '');
        $data['disk_capacity']  = $this->getDiskCapacity($response['id']);
        $data['user_id'] = 0;
        $data['processor_speed'] = '';

        $data = $this->getHardwareDetails($data);
        $data = $this->getUserDetails($data);

        return $data;
    }

    /**
     * Retrieves the hardware details for a given device.
     *
     * @param array $data The data containing the device ID.
     * @return array The updated data array with hardware details
     */
    public function getHardwareDetails($data)
    {
        $deviceId = $data['device_id'] ?? '';
        $response = $this->jumpCloudIntegration->getHardwareDetails($deviceId);
        if ($response) {
            $hardwareDetails = $response[0] ?? [];
            $data['model'] = $hardwareDetails['hardware_model'] ?? '';
            $data['device_name'] = $hardwareDetails['computer_name'] ?? '';
            $data['host_name'] = $hardwareDetails['hostname'] ?? '';
            $data['ram'] = convertSpecsToReadableValueFromBytes($hardwareDetails['physical_memory'] ?? '', 'ram');
        }

        return $data;
    }

    /**
     * Retrieves the user details based on the given data.
     *
     * @param array $data The data containing the device ID.
     * @return array The updated data with user details.
     */
    public function getUserDetails($data)
    {
        $deviceId = $data['device_id'] ?? '';
        $userData = $this->jumpCloudIntegration->getSystemUsers($deviceId);
        if ($userData) {
            $user = isset($userData[0]) ? $this->jumpCloudIntegration->getUserById($userData[0]['id'] ?? null) : null;
            if ($user) {
                $data['email'] = $user['email'] ?? '';
                $data['user_name'] =  $user['firstname'] . " " . $user['lastname'];
                $data['user_id']   = $this->getUserId($data['email']);
            }
        }

        return $data;
    }

    /**
     * GET Techspec string for finding tech specs
     * @param mixed $data
     *
     * @return [type]
     */
    public function getTechSpecString($data)
    {
        if (!$data['processor_speed'] && !$data['ram'] && !$data['disk_capacity']) {
            return 'Ghz/GB/GB';
        }

        return (!empty($data['processor_speed']) ? $data['processor_speed'] : 'Ghz') . '/' . ($data['ram'] ?? 'GB') . '/' . ($data['disk_capacity'] ?? 'GB');
    }

    /**
     *  Update Dicovery tool table asset ID
     * @param mixed $data
     * @param mixed $assetId
     *
     * @return [type]
     */
    public function updateDiscoveryAssetId($data, $assetId)
    {
        return JumpCloudDevices::find($data['jumpcloud_id'])->update(['asset_id' => $assetId]);
    }

    /**
     * Retrieves the disk capacity for a given device.
     *
     * @param int $deviceId The ID of the device.
     * @return string The disk capacity in a standardized format.
     */
    public function getDiskCapacity($deviceId)
    {
        $diskCapacity = '';
        $diskData = $this->jumpCloudIntegration->getDiskDetails($deviceId);
        if ($diskData) {
            $diskCapacity = isset($diskData[0]['disk_size']) ? convertSpecsToReadableValueFromBytes($diskData[0]['disk_size'], 'disk_capacity') : '';
        }

        return $diskCapacity;
    }

    /**
     * Updates or creates Jumpcloud data based on the provided data.
     * If the data already exists, it will be updated based on recent last seen date.
     *
     * @param array $data The data used to update or create Jumpcloud data.
     * @return \App\Models\JumpCloudDevices The updated or newly created Jumpcloud device data.
     */
    public function updateOrCreateJumpcloudData($data)
    {
        $jumpCloudDeviceData = JumpCloudDevices::where('serial_no', $data['serial_no'])->first();
        if ($jumpCloudDeviceData) {
            if (Carbon::parse($data['last_seen'])->greaterThan(Carbon::parse($jumpCloudDeviceData->last_seen))) {
                $jumpCloudDeviceData->update($data);
            }

            return $jumpCloudDeviceData;
        }

        $jumpCloudDeviceData = JumpCloudDevices::create($data);

        return $jumpCloudDeviceData;
    }

    /**
     * Creates assets by retrieving data from the JumpCloudDevices table
     *
     * @return void
     */
    public function createAndAssignAssets()
    {

        $assetTypeId = Cache::remember('asset_type_computer_id', 60 * 60, function () {
            return AssetType::firstOrCreate(['name' => 'Computer', 'slug' => 'computer'])->id;
        });

        $assetStatusId = Cache::remember('asset_status_assigned_id', 60 * 60, function () {
            return AssetStatus::firstOrCreate(['name' => 'Assigned', 'slug' => 'assigned'])->id;
        });

        JumpCloudDevices::where('asset_id', 0)->where('user_id', '!=', 0)->whereNotNull('model')->chunk(100, function ($devices) use ($assetTypeId, $assetStatusId) {
            foreach ($devices as $device) {
                $dataToUpdate = [];
                $dataToUpdate = $device->toArray();
                $dataToUpdate['asset_type_id'] = $assetTypeId;
                $dataToUpdate['asset_status_id'] = $assetStatusId;
                $dataToUpdate['jumpcloud_id'] = $device->id;

                $techSpec = $this->getTechSpec($dataToUpdate, 'jumpcloud_model');

                // Auto create and assign asset if asset is not exist.
                $this->autoCreateAndAssign($dataToUpdate, $techSpec, "JumpCloud");
            }
        });
    }

    /**
     * Auto re assigns the discreapancy assets.
     * @return [type]
     */
    public function autoResassignAssets()
    {
        $discrepancyDevices = $this->repository->getUserMismatchDevices()->has('user')->get();
        $this->reAssignDiscrepancyAssets($discrepancyDevices, 'JumpCloud');

        return true;
    }
}
