<?php

namespace App\Classes\MikrotikService;

use Exception;
use Illuminate\Support\Facades\Log;
use RouterOS\Query;


class Mikrotik extends MikrotikConfig
{
    public $profileMap;


    public function __construct(string $host, string $user, string $pass, int $port = 8728)
    {
        parent::__construct($host,  $user,  $pass,  $port);
        $this->profileMap = $this->setProfileMap();
    }


    public function createSecret(array $data)
    {
        if ($this->checkIfUserInIgnoreList($data["username"])) {
            return;
        }

        $query = new Query("/ppp/secret/add");
        $query->equal("name", $data["username"]);
        $query->equal("password", $data["password"]);
        $query->equal("service", $data["service"]);
        $query->equal("profile", $data["profile"]);
        $query->equal("disabled", $data["disabled"]);

        if ($data["disabled"] == 'yes') {
            Log::error("disabled from secret create - " . $data["username"] . " - " . $data["disabled"]);
        }

        if (array_key_exists("comment", $data)) {
            $query->equal("comment", $data["comment"]);
        }

        if (globalPermission("static-ip-address")) {
            if (array_key_exists("remote-address", $data) && $data["remote-address"] != null && filter_var($data["remote-address"], FILTER_VALIDATE_IP)) {
                $query->equal("remote-address", $data["remote-address"]);
            }
        }

        if (globalPermission("client-mac-binding")) {
            if (array_key_exists("caller-id", $data)) {
                $query->equal("caller-id", $data["caller-id"]);
            }
        }


        $response = $this->runQuery($query);
        if (!array_key_exists("ret", $response['after'])) {

            if ($response["after"]["message"] == "input does not match any value of profile") {

                throw new \Exception('ইউজার এর প্যাকেজ প্রোফাইল মাইক্রোটিক প্রোফাইলের সাথে মিলেনা।', 404);
            } else {

                throw new \Exception($response["after"]["message"], 404);
            }
        }
        return ["success" => true, "message" => "secret created successfully.", "response" => $response];
    }

    public function getSecret($username = null)
    {
        if ($this->checkIfUserInIgnoreList(null)) {
            return [];
        }

        $query = new Query("/ppp/secret/print");

        if ($username) {
            $query->where("name", $username);
        }

        $response = $this->runQuery($query);
        if ($username && sizeof($response) == 0) {
            throw new \Exception("Secret not found in mikrotik.", 404);
        }

        return $username ? $response[0] : $response;
    }

    public function setComment(string $username, string $comment)
    {
        $user = $this->getSecret($username);

        $query = new Query("/ppp/secret/set");
        $query->equal(".id", $user[".id"]);
        $query->equal("comment", $comment);
        $this->runQuery($query);
    }


    public function updateSecret(string $username, array $data)
    {

        if ($this->checkIfUserInIgnoreList($username)) {
            return;
        }

        $user = $this->getSecret($username);
        $query = new Query("/ppp/secret/set");
        $query->equal(".id", $user[".id"]);
        $query->equal("name", $data["username"]);
        $query->equal("password", $data["password"]);
        $query->equal("service", $data["service"]);
        $query->equal("profile", $data["profile"]);

        if (array_key_exists("disabled", $data)) {
            // if ($data["disabled"] == 'yes') {
            Log::error("disabled from secret update - " . $data["username"] . " - " . $data["disabled"]);
            // }

            $query->equal("disabled", $data["disabled"]);
        }

        if (array_key_exists("comment", $data)) {
            $query->equal("comment", $data["comment"]);
        }

        if (globalPermission("static-ip-address")) {
            if (array_key_exists("remote-address", $data) && $data["remote-address"] != null && filter_var($data["remote-address"], FILTER_VALIDATE_IP)) {
                $query->equal("remote-address", $data["remote-address"]);
            } else if ((isset($data["remote-address"]) && ($data["remote-address"] == null
                    || !filter_var($data["remote-address"], FILTER_VALIDATE_IP)))
                && array_key_exists("remote-address", $user)
            ) {
                $removeQuery = new Query("/ppp/secret/unset");
                $removeQuery->equal(".id", $user[".id"]);
                $removeQuery->equal("value-name", "remote-address");
                $this->runQuery($removeQuery);
            }
        }

        if (globalPermission("client-mac-binding")) {
            if (array_key_exists("caller-id", $data)) {
                $query->equal("caller-id", $data["caller-id"]);
            } else  $query->equal("caller-id", "");
        }

        $ip_check = array_key_exists("remote-address", $data)
            && (array_key_exists("remote-address", $user) && $data["remote-address"] != null &&
                globalPermission("static-ip-address") && $user["remote-address"] != $data["remote-address"]);

        $mac_check = (array_key_exists("caller-id", $data) &&
            globalPermission("client-mac-binding")  && $user["caller-id"] != $data["caller-id"]);

        $this->runQuery($query);
        if (
            $user["name"] != $data["username"] ||
            $user["password"] != $data["password"] ||
            $user["profile"] != $data["profile"] ||
            (array_key_exists("disabled", $data) && $data["disabled"] == "yes")
            || $mac_check || $ip_check
        ) {
            try {
                // Log::info("disabled from secret update - ".$data["username"]." - ");
                $this->disconnectSecret($data["username"]);
            } catch (\Exception  $err) {
            }
        }
    }

    private function changeSecretStatus(string $username, string $status)
    {
        if ($this->checkIfUserInIgnoreList($username)) {
            return;
        }

        $user = $this->getSecret($username);
        $query = new Query("/ppp/secret/$status");
        $query->equal("numbers", $user[".id"]);
        $response = $this->runQuery($query);
        if (sizeof($response) == 0) {
            return ["success" => true, 'message' => "Secret $status successfully"];
        } else {
            throw new \Exception("Something went wrong in process of $status secret. user name $username", 404);
        }
    }

    public function deleteSecret(string $username)
    {
        if ($this->checkIfUserInIgnoreList($username) || !$this->getUserOrNull($username)) {
            return;
        }

        return $this->changeSecretStatus($username, "remove");
    }

    public function disableSecret(string $username)
    {
        return $this->changeSecretStatus($username, "disable");
    }

    public function enableSecret(string $username)
    {
        return $this->changeSecretStatus($username, "enable");
    }

    public function getActiveConnection(string $username = null)
    {
        if ($this->checkIfUserInIgnoreList(null)) {
            return [];
        }
        $query = new Query("/ppp/active/print");
        if ($username) {
            $query->where("name", $username);
        }
        $response = $this->runQuery($query);

        if ($username && sizeof($response) == 0) {
            throw new \Exception("active connection not found.", 404);
        }

        return $username ? $response[0] : $response;
    }

    public function getBandwidthUsage(string $username)
    {

        $query = new Query("/interface/print");
        $query->where('type', 'pppoe-in');
        $query->where('name', "<pppoe-$username>");
        $response = $this->runQuery($query);
        if (sizeof($response) > 0) {
            return $response[0];
        } else {
            throw new \Exception('User offline in MikroTik', 404);
        }
    }

    public function getSpeed(string $username)
    {
        $query = new Query("/interface/monitor-traffic");
        $query->equal('interface', "<pppoe-$username>");
        $query->equal('duration', '1');
        $response = $this->runQuery($query);
        if (array_key_exists('after', $response)) {
            throw new \Exception("User Offline In MikroTik", 404);
        }

        return $response;
    }

    public function disconnectSecret($username)
    {
        $activeUser = $this->getActiveConnection($username);

        $query = new Query("/ppp/active/remove");
        $query->equal("numbers", $activeUser[".id"]);
        $response = $this->runQuery($query);
        if (count($response) > 0) {
            throw new \Exception("Something went wrong while disconnecting user.", 404);
        }
        return $response;
    }


    public function disconnectAndDisableSecret($username)
    {
        try {
            $activeUser = $this->getActiveConnection($username);
            $disconnectQuery = new Query("/ppp/active/remove");
            $disconnectQuery->equal("numbers", $activeUser[".id"]);
            $disconnectResult =  $this->runQuery($disconnectQuery);
            if (sizeof($disconnectResult) != 0) {
                throw new \Exception("something went wrong while deleting secret", 404);
            }
        } catch (Exception $err) {
        }

        $user = $this->getSecret($username);
        $deleteQuery = new Query("/ppp/secret/disable");
        $deleteQuery->equal("numbers", $user[".id"]);
        $disableResult = $this->runQuery($deleteQuery);
        if (sizeof($disableResult) != 0) {
            throw new \Exception("something went wrong while deleting secret", 404);
        }
    }

    public function getUserOrNull($username)
    {
        try {
            $user = $this->getSecret($username);
            return $user;
        } catch (Exception $err) {
            return null;
        }
    }
    public function getSecretLogs()
    {
        $logQuery = new Query("/log/print");
        $logQuery->where("topics", "pppoe,ppp,error");
        $logQuery->operations('|');
        $logQuery->where("topics", "pppoe,ppp,info");
        return  $this->runQuery($logQuery);
    }

    public function getIpRanges($packageName)
    {
        $profile = new Query('/ppp/profile/print');
        $profile->where("name", $packageName);

        $profileResponse = $this->runQuery($profile);
        if (sizeof($profileResponse) == 0) {
            throw new \Exception("Profile Not found.");
        }

        $pool = new Query('/ip/pool/print');
        $pool->where("name", $profileResponse[0]['remote-address']);
        $poolResponse = $this->runQuery($pool);

        if (sizeof($poolResponse) == 0) {
            throw new \Exception("Pool Not found.");
        }

        return $poolResponse[0]["ranges"];
    }

    public function getServices()
    {
        $query = new Query("/ip/service/print");
        return $this->runQuery($query);
    }

    public function getProfiles()
    {
        $query = new Query("/ppp/profile/print");
        return $this->runQuery($query);
    }

    public function setProfileMap()
    {
        $profiles = $this->getProfiles();
        $profileMap = [];
        foreach ($profiles as $profile) {
            $profileMap[$profile['name']] = $profile['.id'];
        }
        return $profileMap;
    }
}
