<?php

namespace Services\Ldap\Implementations;

use LdapRecord\Connection;
use LdapRecord\Container;
use LdapRecord\Models\Entry;

class Ldap
{
    private Connection $connection;
    private array $config = [];

    /**
     * Ldap constructor.
     * @param array $config
     */
    public function __construct(array $config)
    {
        $this->config = $config;
        $this->connection = new Connection(self::prepareConnectionConfig($config));
        $this->connection->connect();
        Container::addConnection($this->connection);
    }

    /**
     * @param string $username
     * @param string $password
     * @return bool
     * @throws \Exception
     */
    public function authAttempt(string $username, string $password): bool
    {
        $userDn = $this->getUser($username);
        return $this->connection->auth()->attempt($userDn, $password);
    }

    public function getUserData($username)
    {
        return $this->provider
            ->search()
            ->select([$this->config['login_attribute'], $this->config['group_attribute']])
            ->rawFilter($this->config['ldap_filter'])
            ->whereEquals($this->config['login_attribute'], $username)
            ->firstOrFail();
    }

    public function getUsersList($searchArr = false)
    {
        $searchResult = Entry::query()
            ->select([$this->config['login_attribute'], $this->config['group_attribute'], 'mail'])
            ->whereHas($this->config['login_attribute'])
            ->orderBy($this->config['login_attribute'], 'asc');

        if (!empty($this->config['ldap_filter'])) {
            $searchResult = $searchResult->rawFilter($this->config['ldap_filter']);
        }

        if ($searchArr) {
            $searchResult = $searchResult->whereContains($searchArr['searchKey'], $searchArr['searchValue']);
        }

        return $searchResult->get();
    }

    public function getUsersByUsernames(array $usernames = [])
    {
        $searchResult = Entry::query()
            ->select([$this->config['login_attribute'], $this->config['group_attribute'], 'mail'])
            ->whereHas($this->config['login_attribute']);

        if (!empty($this->config['ldap_filter'])) {
            $searchResult = $searchResult->rawFilter($this->config['ldap_filter']);
        }

        return $searchResult
            ->andFilter(function ($q) use ($usernames) {
                foreach ($usernames as $username) {
                    $q->orWhere($this->config['login_attribute'], '=', $username);
                }
            })
            ->get();
    }

    public function getUser($name)
    {
        $query = Entry::query()
            ->addSelect([$this->config['login_attribute'], $this->config['group_attribute'], 'mail'])
            ->where($this->config['login_attribute'], '=', $name);

        if (!empty($this->config['ldap_filter'])) {
            $query = $query->rawFilter($this->config['ldap_filter']);
        }

        return $query->firstOrFail();
    }

    public static function prepareConnectionConfig($config): array
    {
        return [
            'hosts' => $config['domain_controllers'],
            'base_dn' => $config['base_dn'],
            'username' => $config['admin_username'],
            'password' => $config['admin_password'],
            'port' => $config['port'],
            'use_ssl' => $config['use_ssl'],
            'use_tls' => $config['use_tls'],
            'version' => $config['version'],
            'timeout' => $config['timeout'],
            'options' => $config['custom_options'],
        ];
    }
}
