import {createServer, InsufficientAccessRightsError, parseDN, parseFilter, SearchRequest} from "ldapjs";
import {getRootDN} from "../utils/ldap";
import {LdapService} from "./ldap.service";

export function equalsDN(a, b) {
    a = parseDN((a instanceof String ? a : a.toString()).toLowerCase());
    b = parseDN((b instanceof String ? b : b.toString()).toLowerCase());
    return a.equals(b);
}

export function getPersonMatchedDN(reqDN) {
    const personFilter = parseFilter('(objectclass=inetOrgPerson)');
    return {};
}

function authorize(req, res, next) {
    console.debug('===> authorize info');
    console.debug('req type', req.constructor.name);
    console.debug('reqDN', (req.baseObject || '').toString());
    console.debug('bindDN', req.connection.ldap.bindDN.toString());
    console.debug('====');
    const bindDN = req.connection.ldap.bindDN;
    const rootDN = parseDN(getRootDN());

    // person can search itself
    if (req instanceof SearchRequest) {
        if (equalsDN(bindDN, req.baseObject)) {
            return next();
        }
    }

    // root admin can do everything
    if (equalsDN(bindDN.parent(), rootDN)) {
        return next();
    }

    return next(new InsufficientAccessRightsError());
}

export const ldapServer = createServer();
const registerMap = [];

export function ldapRegister(t: LdapService) {
    for (const item of registerMap) {
        if (item.type === 'search') {
            ldapServer.search(item.hook, authorize, function (req, res, next) {
                item.descriptor.value.apply(t, [req, res, next]);
            })
        }
        if (item.type === 'bind') {
            ldapServer.bind(item.hook, function (req, res, next) {
                item.descriptor.value.apply(t, [req, res, next]);
            });
        }

    }
}

export function Search(ditHook: string) {
    console.log("search hook " + ditHook);
    return function (target: LdapService, propertyKey: string, descriptor: PropertyDescriptor) {
        registerMap.push({
            type: 'search', hook: ditHook, descriptor: descriptor
        });
        return descriptor;
        // ldapServer.search(ditHook, authorize, async (req, res, next) => {
        //    await descriptor.value(req, res, next);
        // });
    }
}

export function Bind(ditHook: string) {
    return function (target: LdapService, propertyKey: string, descriptor: PropertyDescriptor) {
        registerMap.push({
            type: 'bind', hook: ditHook, descriptor: descriptor
        });
        return descriptor
        // ldapServer.bind(ditHook, async (req, res, next) => {
        //     await descriptor.value(req, res, next);
        // });
    };
}
