<?php

namespace App\Services\Integrations;

use App\Http\Traits\AutoCreateAssignTrait;
use Illuminate\Support\Facades\Http;
use App\Models\ApiCredential;
use App\Models\Asset;
use App\Models\Carrier;
use App\Models\AirWatch;
use App\Models\AssetStatus;
use App\Models\AssetType;
use App\Repositories\DiscoveryTools\WorkspaceOneRepository;
use App\User;
use Exception;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Client\RequestException;
use Illuminate\Support\Carbon;
use Cache;

class WorkspaceOneApi
{
    use AutoCreateAssignTrait;
    public function __construct(protected WorkspaceOneRepository $repository) {}

    /**
     * Upload the Workspace ONE data to the Report table
     *
     * @param String $slug   API slug
     */
    public function upload($slug = 'airwatch_laptop')
    {
        $page = 0;
        while (1 == 1) {
            $data = $this->getData($slug, $page);
            if ($data) {
                $response   = $this->saveWorkspaceOneData($data, $slug);
            } else {
                break;
            }
            Log::channel('single')->info("WorkspaceOne Importing Page :- " . $page . '-');
            ++$page;
        }

        return true;
    }

    /**
     * Calls the API and returns response
     * @param mixed $slug
     * @param mixed $url
     *
     * @return api response data
     */
    public function getApiResponse($slug, $url)
    {
        $apiCredential = ApiCredential::where('slug', $slug)->first();
        $baseUrl = optional($apiCredential)->url;
        $userName = optional($apiCredential)->user_name;
        $password = optional($apiCredential)->password;
        $apiToken = optional($apiCredential)->key;

        $response = Http::withBasicAuth($userName, $password)
            ->withHeaders([
                'Content-Type'  => 'application/json',
                'aw-tenant-code' => $apiToken,
            ])
            ->retry(3, 5000)
            ->get($baseUrl . $url)
            ->throw()
            ->json();
        return $response;
    }

    /**
     * Check the API connection status.
     *
     * @param string $slug API credentials sug
     *
     * @return Boolean
     */
    public function apiConnect($slug = 'airwatch_laptop')
    {
        try {
            $response = $this->getApiResponse($slug, 'api/mdm/devices/bulksettings');
            return $response->successful();
        } catch (Exception $e) {
            // dd($e->getMessage());
            return false;
        }
    }

    /**
     * Call the API and return the reponse.
     *
     * @param string $slug API credentials sug
     * @param int    $page Page number
     *
     * @return Array
     */
    public function getData($slug = 'airwatch_laptop', $page = 1)
    {
        try {
            $response = $this->getApiResponse($slug, 'api/mdm/devices/search?page=' . $page);

            if ($response && $response['Devices']) {
                return $response['Devices'];
            }
        } catch (RequestException $e) {
            Log::channel('single')->info('WorkspaceOne Import Error :- ' . $e->getMessage());

            return false;
        }
    }

    /**
     * Save the data to the DB.
     *
     * @param Array $data     API response data
     * @param string $slug   API credentials sug
     */
    public function saveWorkspaceOneData($data, $slug = 'airwatch_laptop')
    {
        $user = User::where('email', 'support@teqube.com')->first();
        $assetTypeId   = AssetType::firstOrCreate(['name' => 'Computer', 'slug' => 'computer'])->id;
        $assetStatusId = AssetStatus::firstOrCreate(['name' => 'Assigned', 'slug' => 'assigned'])->id;
        $dataToUpdateArray = [];
        foreach ($data as $key => $item) {

            //Windows devices in WorkspaceOne should get ignored
            if ($item['Platform'] === 'Windows') {
                continue;
            }

            $dataToUpdate = $this->parseWorkspaceOneData($item, $slug);

            if ($slug == 'airwatch_mobile') {
                $imeiData = $this->getImeiData($item['Id']['Value'] ?? '');
            }


            $workspaceOne = AirWatch::updateOrCreate(
                ['serial_no' => $dataToUpdate['serial_no']],
                $dataToUpdate
            );

            $workspaceOneId = $workspaceOne->id;
            $this->updateLockedDate($workspaceOne, $slug);

            $this->updateHostName($workspaceOne, $slug);

            //Auto Create and Assign Asset
            $dataToUpdate['asset_type_id'] = $assetTypeId;
            if (($dataToUpdate['asset_id'] == 0) && ($dataToUpdate['user_id'] != 0)) {
                $techSpec = $this->getTechSpec($dataToUpdate, 'workspace_one_model');
                $dataToUpdate['asset_status_id'] = $assetStatusId;
                $dataToUpdate['workspace_one_id']   = $workspaceOneId;
                $this->autoCreateAndAssign($dataToUpdate, $techSpec, "WorkspaceOne");
            }

            if ($key == 500) {
                sleep(2);
            }
        }
        return true;
    }

    /**
     * Making array with needed data for saving to DB.
     *
     * @param $item    WorkspaceOne response single data
     * @param string $slug   API credentials sug
     *
     * @return array
     */
    public function parseWorkspaceOneData($item, $slug)
    {
        if ($item) {
            $userId     = $this->getUserId($item['UserEmailAddress']);
            $asset      = $this->getAsset($item['SerialNumber']);
            $phoneNumber = $this->getCellularNetworkInfo($item, 'PhoneNumber');
            $carrier    = $this->getCellularNetworkInfo($item, 'CarrierName');
            $carrierId  = $this->getCarrierId($carrier);

            $nameArray  = $item['UserId'] ? explode(" ", $item['UserId']['Name']) : [];
            $firstName  = $nameArray[0];
            $lastName   = isset($nameArray[2]) ? $nameArray[2] : '';

            $data['user_id']        = $userId;
            $data['asset_id']       = $asset ? $asset->id : 0;
            $data['last_seen']      = $item['LastSeen'] ?? '';
            $data['user_name']      = $item['UserName'] ?? '';
            $data['first_name']     = $firstName;
            $data['last_name']      = $lastName;
            $data['email']          = $item['UserEmailAddress'] ?? '';
            $data['friendly_name']  = $item['DeviceFriendlyName'] ?? '';
            $data['serial_no']      = $item['SerialNumber'] ?? '';
            $data['model']          = $item['Model'] ?? '';
            $data['manufacturer'] = "Apple";
            $data['ownership']      = $item['Ownership'] ?? '';
            $data['imei']           = $item['Imei'] ?? '';
            $data['phone_number']   = preg_replace('/[^0-9]/', '', $phoneNumber);
            $data['carrier']        = $carrier;
            $data['carrier_id']     = $carrierId;
            $data['device_id']      = $item['Id']['Value'] ?? '';
            $data['mac_address']    = $item['MacAddress'] ? formatMacAddress($item['MacAddress']) : '';
            $data['last_enrolled_on'] = $item['LastEnrolledOn'] ?? '';
            $data['platform']        = $item['Platform'] ?? '';
            $data['os_version']      = $item['OperatingSystem'] ?? '';
            $data['apple_enrollment_status'] = ($item['EnrollmentStatus'] && $item['EnrollmentStatus'] == 'Enrolled') ? 'Enrolled' : 'Not Enrolled';

            return $data;
        }

        return false;
    }

    /**
     * Taking the cellular network details from the WorkspaceOne data.
     *
     * @param $item WorkspaceOne Data array
     * @param string $field Cellular data field
     */
    public function getCellularNetworkInfo($item, $field, $index = 0)
    {
        return isset($item['DeviceCellularNetworkInfo']) ?
            ($item['DeviceCellularNetworkInfo'][$index] ?
                trim($item['DeviceCellularNetworkInfo'][$index][$field])
                : null)
            : null;
    }

    /**
     * Taking the imei from the WorkspaceOne data.
     *
     * @param $item WorkspaceOne Data array
     * @param string $field Cellular data field
     */
    public function getImeiData($deviceId)
    {
        if (!$deviceId) {
            return null;;
        }
        $slug = 'airwatch_mobile';

        try {
            $response = $this->getApiResponse($slug, "api/mdm/devices/" . $deviceId . "/network/");
            if ($response) {
                $imeiData = [];
                foreach ($response['ServiceSubscription']  as $key => $value) {
                    $imeiData[] = $value['Imei'];
                }
                return $imeiData;
            }
        } catch (RequestException $e) {
            //return false;
        } catch (Exception $e) {
        }

        return null;
    }


    /**
     * get the user id from email address.
     *
     * @param string $email
     *
     * @return int
     */
    public function getUserId($email)
    {
        if ($email) {
            $user = User::select('id')->where('email', $email)->first();

            return  $user ? $user->id : 0;
        }

        return 0;
    }

    /**
     * get the Asset from serial #.
     *
     * @param string $serial
     *
     * @return int
     */
    public function getAsset($serial)
    {
        if ($serial) {
            $asset = Asset::select('id')->where('serial_no', $serial)->first();

            //return $asset ? $asset->id : 0;

            return $asset;
        }

        return 0;
    }

    /**
     * get the carrier id from carrier.
     *
     * @param string $carrier
     *
     * @return int
     */
    public function getCarrierId($carrier)
    {
        if ($carrier) {
            $carrier = Carrier::where('name', $carrier)->first();

            return $carrier ? $carrier->id : null;
        }

        return null;
    }

    /**
     * Updates the lock date
     * @param Airwatch $workspaceOne
     * @param string $slug
     *
     * @return [type]
     */
    public function updateLockedDate(Airwatch $workspaceOne, string $slug)
    {
        return $workspaceOne->update([
            'locked_date' => $workspaceOne->device_id ? $this->getLockedDate($workspaceOne->device_id, $slug) : ''
        ]);

        return false;
    }

    /**
     * Taking the locked date from the WorkspaceOne Notes API
     *
     * @param int $deviceId
     * @param string $slug
     */
    public function getLockedDate(int $deviceId, string $slug)
    {
        try {
            $response = $this->getApiResponse($slug, "api/mdm/devices/" . $deviceId . "/notes");
            if (isset($response['DeviceNotes'])) {
                $notes = $response['DeviceNotes'][0] ?? '';

                if (!empty($notes)) {
                    if (substr($notes['Note'], 0, 10) == "Unlock Pin") {
                        return $notes['CreatedOn'] ?? '';
                    }
                }
            }
        } catch (RequestException $e) {
            return false;
        }
    }

    /**
     * Get the Device details from device id API
     *
     * @param int $id  WorkspaceOne Device ID
     * @param string $slug   API credentials sug
     */
    public function getDeviceDetails($deviceId = null, $slug)
    {
        if (!$deviceId) {
            return null;
        }

        try {

            $response = $this->getApiResponse($slug, "api/mdm/devices/" . $deviceId);
            if ($response) {
                return $response;
            }
        } catch (RequestException $e) {
            //return false;
        } catch (Exception $e) {
        }

        return null;
    }

    /**
     * Call the host name details api and update the host details in WorkspaceOne and assets table
     *
     * @param Airwatch $workspaceOne
     * @param string $slug
     */
    public function updateHostName(Airwatch $workspaceOne, string $slug)
    {
        $deviceDetails = $this->getDeviceDetails($workspaceOne->device_id, $slug);
        $workspaceOne->host_name = $deviceDetails['HostName'] ?? null;
        $workspaceOne->local_host_name = $deviceDetails['LocalHostName'] ?? null;
        $workspaceOne->save();
        // $asset = Asset::find($workspaceOne->asset_id);
    }


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

    /**
     * Update Dicovery tool table asset ID
     * @param mixed $data
     * @param mixed $assetId
     *
     * @return [type]
     */
    public function updateDiscoveryAssetId($data, $assetId)
    {
        AIrwatch::find($data['workspace_one_id'])->update(['asset_id' => $assetId]);
    }
    /**
     * Auto re assigns the discreapancy assets.
     * @return [type]
     */
    public function reassignAssets()
    {
        $discrepancyDevices = $this->repository->getUserMismatchDevices()->has('user')->get();
        $this->reAssignDiscrepancyAssets($discrepancyDevices, 'WorkspaceOne');
        return true;
    }
}
