<?php

namespace App\Services\SoftwareLicense;

use App\Models\SoftwareLicense\SoftwareLicense;
use App\Models\SoftwareLicense\SoftwareLicenseKey;
use App\Repositories\SoftwareLicense\LicenseRepository;
use Exception;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Auth;
use App\Services\SoftwareLicense\LicenseHistoryService;
use Carbon\Carbon;
use Illuminate\Support\Facades\DB;

/**
 * Software License Key Service
 */
class LicenseKeyService
{

    protected $licenseRepo;
    protected $licenseHistoryService;

    /**
     * Constructor for Software License Service
     *
     * @param object $licenseRepo
     */
    public function __construct(LicenseRepository $licenseRepo, LicenseHistoryService $licenseHistoryService)
    {
        $this->licenseRepo = $licenseRepo;
        $this->licenseHistoryService = $licenseHistoryService;
    }

    /**
     * remove key from the license
     */
    public function removeKeyFromLicense($id)
    {
        DB::beginTransaction();
        try {
            $licenseKey = $this->licenseRepo->getLicenseKey($id);
            $this->licenseRepo->deleteKey($id);
            $historyData = $this->licenseHistoryService->getHistoryDataForKeyRemoved('license_key_removed', $licenseKey);
            $this->licenseRepo->addLicenseHistory($historyData);
            if ($licenseKey->license->license_key_type == 'user_license') {
                $licenseKey->license->decrement('license_keys_added');
            }
            DB::commit();
            return $licenseKey->license->license_key_type == 'user_license' ? $licenseKey->license->license_keys_added : 1;
        } catch (Exception $e) {
            DB::rollBack();
            Log::error($e->getMessage());
            return false;
        }
    }

    /**
     * List Software License Keys
     *
     * @param int $licenseId
     */
    public function getAllLicenseKeys(int $licenseId)
    {
        $licenseKeys = $this->licenseRepo->getAllLicenseKeys($licenseId);
        $start = request('start');
        $limit = request('length');
        $count = $licenseKeys->count();

        if ($limit && $limit != -1) {
            $licenseKeys = $licenseKeys->offset($start)->limit($limit);
        }
        $licenseKeys->orderBy('created_at', 'desc');
        $licenseKeys = $licenseKeys->get();

        return compact('licenseKeys', 'count');
    }


    /**
     * List Available Software License Keys
     *
     * @param int $licenseId
     */
    public function getAvailableLicenseKeys(int $licenseId)
    {
        $licenseKeys = $this->licenseRepo->getAllLicenseKeys($licenseId);
        $licenseKeys = $licenseKeys->orderBy('created_at', 'desc')->get();

        $licenseKeys = $licenseKeys->filter(function ($key) {
            if ($key->users_allowed_for_key != 'U') {
                return $key->users_allowed_for_key > $key->no_of_users;
            }
            return $key;
        });

        return $licenseKeys;
    }

    /**
     * Set up Software License keys for listing page
     *
     * @param object $licenseKeys
     * @param mixed $start
     * @param array $data
     *
     * @return array
     */
    public function getLicensesKeyData(object $licenseKeys, $start, array $data)
    {
        $parentIndex = $start;

        foreach ($licenseKeys as $key => $licenseKey) {
            $parentIndex++;
            $nestedData = $this->getLicensesKeyNestedData($licenseKey, $parentIndex);

            $data[] = $nestedData;
        }

        return $data;
    }

    /**
     * Formatting Software License keys for listing page
     *
     * @param object $licenseKey
     * @param int $index
     *
     * @return array
     */
    public function getLicensesKeyNestedData(object $licenseKey, int $index)
    {
        $nestedData['id']         = $index;
        $nestedData['license_key']   = $licenseKey->license_key;
        $nestedData['key_usage_type']   = config('software-license.key_usage_types.' . $licenseKey->key_usage_type);
        $nestedData['users_allowed_for_key']   = $licenseKey->users_allowed_for_key == 'U' ? 'Unlimited users' : $licenseKey->users_allowed_for_key;
        $nestedData['keys_used']   = $licenseKey->no_of_users == 'U' ? ' - ' : $licenseKey->no_of_users;
        $usersAllowed = $licenseKey->users_allowed_for_key ? $licenseKey->users_allowed_for_key : 0;
        $remaining = $licenseKey->no_of_users ? $licenseKey->no_of_users : 0;
        $nestedData['keys_remaining']   = ($licenseKey->key_usage_type != 'unlimited_usage') ? ($usersAllowed - $remaining) : '-';
        $nestedData['date_added'] = parse_date_from_db_datetime($licenseKey->created_at);
        $removeAction = "<a style='margin-left:15px;' class='edit-key edit-key-inline' data-id='" . $licenseKey->id . "' data-key='" . $licenseKey->license_key . "'><i class='icon icon-n-edit'></i></a>";
        if (!$licenseKey->licenseUsers->count()) {
            $removeAction .= "<a style='margin-left:15px' class='delete-key delete-key-inline' data-id='" . $licenseKey->id . "' ><i class='icon icon-delete-forever'></i></a>";
        }
        $nestedData['action']  = $removeAction;

        return $nestedData;
    }

    /**
     * get License Key with id
     *
     * @param int $id
     *
     * @return object
     */
    public function getLicenseKey(int $id)
    {
        return $this->licenseRepo->getLicenseKey($id);
    }

    /**
     * Stores the license keys
     * @param array $licenseKeys
     * @param integer $assetId
     *
     * @return [type]
     */
    public function storeLicenseKey($licenseKeys, $assetId, $usageType = null, $usersAllowed = null)
    {
        $license = SoftwareLicense::findOrFail($assetId);
        $licensesUsed = 0;
        // dd($licenseKeys);
        foreach ($licenseKeys as $key) {
            $refinedKey = str_replace([PHP_EOL, ' ', ',', "\n", "\r"], '', $key); //trimming whitespace, comma and newline
            if ($refinedKey != '') {
                $this->createLicenseKeys($assetId, $usageType, $usersAllowed, $refinedKey, 'license_key_added');

                if ($license->license_key_type == 'user_license') {
                    $license->increment('license_keys_added');
                    if ($license->license_keys_added >= $license->licenses_purchased) {
                        return ++$licensesUsed;
                    }
                }
                $licensesUsed++;
            }
        }

        return $licensesUsed;
    }

    /**
     * Save documenst of the license
     */
    public function saveLicenseKeyDocument($fileName, $licenseId, $usageType, $usersAllowed)
    {
        $excelData = $this->getItems($fileName);
        $items = $excelData['csvData'];
        $license = SoftwareLicense::findOrFail($licenseId);
        $licensesUsed = 0;
        $rejected = 0;

        if (!$this->validateCSV($items, $fileName)) {
            return ['licensesUsed' => false, 'rejected' => 'All'];
        }


        foreach ($items as $key => $item) {
            if (!count($item)) {
                $rejected++;
                continue; //skip empty columns
            }

            $refinedKey = str_replace([PHP_EOL, ' ', ',', "\n", "\r"], '', $item['License Key']); //trimming whitespace, comma and newline

            if ($refinedKey == '') {
                $rejected++;
                continue; //skip empty columns
            }

            if (!preg_match('/^[a-zA-Z0-9,_\r\n -]+$/i', $refinedKey)) {  // clear keys with invalid characters
                $rejected++;
                continue; //skip if key is not in correct format
            }

            $this->createLicenseKeys($licenseId, $usageType, $usersAllowed, $refinedKey, 'license_key_upload');

            if ($license->license_key_type == 'user_license') {
                $license->increment('license_keys_added');
                $licensesUsed++;
                if ($license->license_keys_added >= $license->licenses_purchased) {
                    return ['licensesUsed' => $licensesUsed, 'rejected' => 'Remaining'];
                }
            }
        }
        return ['licensesUsed' => $licensesUsed, 'rejected' => $rejected];
    }


    /**
     * stores license keys to db
     * @param mixed $licenseId
     * @param mixed $usageType
     * @param mixed $usersAllowed
     * @param mixed $refinedKey
     * @param string $action
     *
     * @return [type]
     */
    public function createLicenseKeys($licenseId, $usageType, $usersAllowed, $refinedKey, $action = 'license_key_added')
    {
        $keyArray = [];
        $keyArray = [
            'software_license_id' => $licenseId,
            'license_key' => $refinedKey, //trimming whitespace, comma and newline
            'key_usage_type' => $usageType,
            'users_allowed_for_key' => $usersAllowed  && in_array($usageType, ['limited_usage']) ? $usersAllowed : ($usageType == 'unlimited_usage' ? 'U' : 1),
            'no_of_users' => ($usageType == 'unlimited_usage') ? 'U' : 0,
        ];
        $licenseKey = SoftwareLicenseKey::create($keyArray);
        $historyData = $this->licenseHistoryService->getHistoryDataForKeyAdded($action, $licenseKey);

        $this->licenseRepo->addLicenseHistory($historyData);
        return true;
    }


    /**
     * Updates the license keys
     * @param array $licenseKeys
     * @param integer $assetId
     *
     * @return [type]
     */
    public function updateLicenseKey($assetId, $usageType, $usersAllowed = null)
    {
        $keyArray = [];
        $licenseKeys = SoftwareLicenseKey::where('software_license_id', $assetId)->get();
        if (!empty($licenseKeys)) {
            foreach ($licenseKeys as $key) {
                $licenseKey = str_replace([PHP_EOL, ' ', ',', "\n", "\r"], '', $key); //trimming whitespace, comma and newline;
                $keyArray = [
                    'key_usage_type' => $usageType,
                    'users_allowed_for_key' => $usersAllowed && !in_array($usageType, ['unlimited_usage', 'unique_usage']) ? $usersAllowed : ($usageType == 'unlimited_usage' ? 'U' : 1),
                    'no_of_users' => $usageType != 'unlimited_usage' ? $key->no_of_users : 0,
                ];
                $key->update($keyArray);
                unset($keyArray['no_of_users']);
                $historyData = $this->licenseHistoryService->getHistoryDataForKeyActions('license_key_updated', $licenseKey, $keyArray);
                $this->licenseRepo->addLicenseHistory($historyData);
            }
        }
        return true;
    }


    public function validateCSV($items, $fileName)
    {
        $csv = array_map("str_getcsv", file($fileName, FILE_SKIP_EMPTY_LINES));
        $csvKeys = array_shift($csv);
        if (empty($csvKeys)) {
            return false;
        }
        $result = array_diff($csvKeys, ['License Key']);

        if (!empty($result)) {
            // return 'Uploaded file is not match with template file';
            return false;
        }
        if (empty($items)) {
            // return 'Please make sure the file contains data';
            return false;
        }
        return true;
    }

    /**
     * Convert CSV to array.
     *
     * @param string $path
     *
     * @return mixed
     */
    public function getItems($path)
    {
        if (!file_exists($path)) {
            return false;
        }

        $extension = getFileExtension($path);
        if ($extension == 'csv') {
            $items = csv_to_array($path);
        } elseif ($extension == 'xlsx') {
            $items = excel_to_array($path);
        }

        if (empty($items)) {
            return false;
        }

        return $items;
    }


    /**
     * Update individual licenses
     * @return [type]
     */
    public function updateIndividual()
    {

        $licenseKey = SoftwareLicenseKey::find(request('license_key_id'));
        $update['license_key'] = str_replace([PHP_EOL, ' ', ',', "\n", "\r"], '', request('license_key')); //trimming whitespace, comma and newline;
        if (request()->has('users_allowed_for_key')) {
            $update['users_allowed_for_key'] = request('users_allowed_for_key');
        }

        $historyData = $this->licenseHistoryService->getHistoryDataForKeyActions('license_key_updated', $licenseKey, $update);
        $this->licenseRepo->addLicenseHistory($historyData);
        $licenseKey->update($update);

        return $licenseKey->fresh();
    }
}
