import {ldapConfig} from "../ldap.config";
import {createHash, randomUUID} from "crypto";
import {randomStringGenerator} from "@nestjs/common/utils/random-string-generator.util";

export function getRootDN() {
    return ldapConfig.rootDN;
}

export function makeDN(...parts) {
    return parts.filter((value) => {
        return value != ""
    }).join(',');
}

export function getOrganizationBaseDN() {
    return makeDN('o=' + ldapConfig.organization, getRootDN());
}

export function getPeopleBaseDN() {
    return makeDN('ou=People', getOrganizationBaseDN());
}

export function getGroupsBaseDN() {
    return makeDN('ou=Groups', getOrganizationBaseDN());
}

export function makeGroupEntryDN(...prefix) {
    return makeDN(...prefix, getGroupsBaseDN());
}

export function makeOrganizationUnitEntryDN(...prefix) {
    return makeDN(...prefix, getPeopleBaseDN());
}

export function makeOrganizationUnitEntry(dn, name, attrs = {}) {
    // dn: d.dn,
    // attributes: {
    //   objectclass: ['organizationalUnit', 'top'],
    //   ou: d.name,
    // }
    const generatedDN = makeOrganizationUnitEntryDN(dn);
    return {
        dn: generatedDN,
        attributes: Object.assign({
            objectclass: ['organizationalUnit', 'top'],
            ou: name,
            entryDN: dn,
        }, attrs),
    };
}

export function makePersonEntry(dn, attrs) {
    // u.ldap = {
    //   dn: `mail=${mail}, ${depDN}`,
    //   attributes: {
    //     objectclass: ['inetOrgPerson', 'organizationalPerson', 'person', 'top'],
    //     uid: u.userid,
    //     title: u.position,
    //     mobileTelephoneNumber: u.mobile,
    //     cn: u.name,
    //     givenName: u.name.substr(1),
    //     sn: u.name.substr(0, 1),
    //     mail,
    //     userPassword: '123456',
    //   }
    // };
    // const generatedDN = makeOrganizationUnitEntryDN(dn);
    const generatedDN = makeDN(dn, getRootDN());
    if (attrs.userPassword===''){
        attrs.userPassword = randomUUID();
    }
    const shaPassword = createHash('sha1').update(attrs.userPassword);
    const base64ShaPassword = shaPassword.digest('base64');
    attrs.userPassword =  '{SHA}' + base64ShaPassword;
    return {
        dn: generatedDN,
        attributes: Object.assign({
            objectclass: ['inetOrgPerson', 'organizationalPerson', 'person', 'top'],
            userPassword: '********',
            // otpSecret: 'abcd',
            memberOf: [],
            entryDN: dn,
        }, attrs)
    };
}

export function makeGroupEntry(dn, name, members, attrs = {}) {
    // dn: d.dn,
    // attributes: {
    //   objectclass: ['groupOfNames', 'top'],
    //   ou: d.name,
    // }
    const generatedDN = makeGroupEntryDN(dn);
    return {
        dn: generatedDN,
        attributes: Object.assign({
            objectclass: ['groupOfNames', 'top'],
            cn: name,
            ou: name,
            member: members,
            memberOf: [],
            entryDN: dn,
        }, attrs),
    };
}

export function getOrganizationEntry() {
    return {
        dn: getOrganizationBaseDN(),
        attributes: {
            objectclass: ['organization', 'top'],
            ou: ldapConfig.organization,
        },
    };
}

export function getPeopleBaseEntry() {
    return {
        dn: getPeopleBaseDN(),
        attributes: {
            objectclass: ['organizationalUnit', 'top'],
            ou: 'People',
        },
    };
}

export function getGroupsBaseEntry() {
    return {
        dn: getGroupsBaseDN(),
        attributes: {
            objectclass: ['organizationalUnit', 'top'],
            ou: 'Groups',
        },
    };
}

export function makeAdminEntry(attrs) {
    // {dn: 'cn=admin,'+rootDN, attributes: { objectclass: ['simpleSecurityObject', 'organizationalRole'], hasSubordinates: ['FALSE'] } },
    return {
        dn: [`cn=${attrs.commonName}`, getRootDN()].join(','),
        attributes: {
            objectclass: ['simpleSecurityObject', 'organizationalRole'],
            hasSubordinates: ['FALSE']
        },
    };
}

export function getAdminEntries() {
    return ldapConfig.admins.map(cfg => {
        return makeAdminEntry(cfg);
    });
}

export function getBaseEntries() {

    const rootDN = getRootDN();
    const rootEntry = {
        dn: rootDN,
        attributes: {
            objectclass: ['dcObject', 'organization', 'top'],
            dc: rootDN.split(',')[0].split('=')[1].trim(),
            o: ldapConfig.organization,
            hasSubordinates: ['TRUE'],
        }
    };

    return [
        rootEntry,
        getOrganizationEntry(),
        getPeopleBaseEntry(),
        getGroupsBaseEntry(),
        ...getAdminEntries(),
    ];
}

export function validateAdminPassword(username, password) {
    const [user] = ldapConfig.admins.filter(u => {
        return u.commonName == username;
    });
    return user && user.password === password;


}


export function validateAdminPermission(username, permission) {
    const [user] = ldapConfig.admins.filter(u => {
        return u.commonName == username;
    });
    return !!(user && user[permission]);


}

export function addMemberToGroup(memberEntry, groupEntry) {
    if (groupEntry.attributes.member.indexOf(memberEntry.dn) < 0) {
        groupEntry.attributes.member.push(memberEntry.dn);
    }
    if (memberEntry.attributes.memberOf.indexOf(groupEntry.dn) < 0) {
        memberEntry.attributes.memberOf.push(groupEntry.dn);
    }
}

