#!/usr/bin/python3
#
# Like what you see? Join us!
# https://www.univention.com/about-us/careers/vacancies/
#
# Copyright 2020-2024 Univention GmbH
#
# https://www.univention.de/
#
# All rights reserved.
#
# The source code of this program is made available
# under the terms of the GNU Affero General Public License version 3
# (GNU AGPL V3) as published by the Free Software Foundation.
#
# Binary versions of this program provided by Univention to you as
# well as other copyrighted, protected or trademarked materials like
# Logos, graphics, fonts, specific documentations and configurations,
# cryptographic keys etc. are subject to a license agreement between
# you and Univention and not subject to the GNU AGPL V3.
#
# In the case you use this program under the terms of the GNU AGPL V3,
# the program is provided in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public
# License with the Debian GNU/Linux or Univention distribution in file
# /usr/share/common-licenses/AGPL-3; if not, see

import click

from univention.config_registry import ConfigRegistry
from univention.udm import UDM, NoObject


def clear_objects(udm, module):
    click.echo("Removing all %s objects..." % module)
    i = 0
    for obj in udm.get(module).search():
        click.echo("The object %s will be removed." % obj.dn)
        obj.delete()
        i += 1
    click.echo(click.style("%d objects were removed." % i, fg="green"))


def migrate_entry(udm, old_obj):
    click.echo("The obj %s will be migrated." % old_obj.dn)
    try:
        existing_obj = udm.get("portals/entry").get_by_id(old_obj.props.name)
    except NoObject:
        pass
    else:
        click.echo(click.style("Already found: %s" % existing_obj.dn, fg="yellow"))
        return
    new_obj = udm.get("portals/entry").new()
    new_obj.props.name = old_obj.props.name
    new_obj.props.displayName = old_obj.props.displayName
    new_obj.props.description = old_obj.props.description
    new_obj.props.link = [{'locale': 'en_US', 'value': locale} for locale in old_obj.props.link]
    new_obj.props.linkTarget = old_obj.props.linkTarget
    new_obj.props.activated = old_obj.props.activated
    new_obj.props.allowedGroups = old_obj.props.allowedGroups
    new_obj.props.icon = old_obj.props.icon
    new_obj.save()
    click.echo(click.style("We created: %s" % new_obj.dn, fg="green"))


def migrate_portal(udm, old_obj):
    click.echo("The obj %s will be migrated." % old_obj.dn)
    try:
        existing_obj = udm.get("portals/portal").get_by_id(old_obj.props.name)
    except NoObject:
        pass
    else:
        click.echo(click.style("Already found: %s" % existing_obj.dn, fg="yellow"))
        migrate_content(udm, old_obj, new_obj=None)
        return
    new_obj = udm.get("portals/portal").new()
    new_obj.props.name = old_obj.props.name
    new_obj.props.displayName = old_obj.props.displayName
    new_obj.props.ensureLogin = old_obj.props.ensureLogin
    new_obj.props.background = old_obj.props.background
    new_obj.props.logo = old_obj.props.logo
    new_obj.props.defaultLinkTarget = "newwindow"  # old_obj.props.defaultLinkTarget
    new_obj.save()
    click.echo(click.style("We created: %s" % new_obj.dn, fg="green"))
    if old_obj.props.links:
        click.echo(click.style("ATTENTION: We did not migrate the footer links. They were mere values on the portal object, but are now references to portals/entry objects. Please migrate them manually. Here they are:", fg="red"), err=True)
        for link in old_obj.props.links:
            click.echo(link)
        click.echo("You do it like this:")
        click.echo(" udm portals/entry create --position \"cn=entry,cn=portals,cn=univention\" --set name=... --append displayName=\"\\\"en_US\\\" \\\"...\\\"\" --append displayName=\"\\\"de_DE\\\" \\\"...\\\"\" ...")
        click.echo(" udm portals/portal modify --dn \"%s\" --append menuLinks=\"$DN_OF_THE_NEWLY_CREATED_OBJECT\"" % new_obj.dn)
    migrate_content(udm, old_obj, new_obj)


def migrate_content(udm, old_obj, new_obj):
    click.echo("Okay. Now migrating the content...")
    for category_dn, entry_dns in old_obj.props.content:
        click.echo("Creating our very own version of %s" % category_dn)
        old_category = udm.get("settings/portal_category").get(category_dn)
        new_entry_dns = []
        for entry_dn in entry_dns:
            old_entry = udm.get("settings/portal_entry").get(entry_dn)
            new_entry = udm.get("portals/entry").get_by_id(old_entry.props.name)
            new_entry_dns.append(new_entry.dn)
        new_category_name = f"{old_obj.props.name}-{old_category.props.name}"
        try:
            existing_obj = udm.get("portals/category").get_by_id(new_category_name)
        except NoObject:
            pass
        else:
            click.echo(click.style("Already found: %s" % existing_obj.dn, fg="yellow"))
            for new_entry_dn in new_entry_dns:
                if new_entry_dn not in existing_obj.props.entries:
                    click.echo(click.style("But we add %s" % new_entry_dn, fg="green"))
                    existing_obj.props.entries.append(new_entry_dn)
            existing_obj.save()
            continue
        new_category = udm.get("portals/category").new()
        new_category.props.name = new_category_name
        new_category.props.displayName = old_category.props.displayName
        new_category.props.entries = new_entry_dns
        new_category.save()
        click.echo(click.style("We created: %s" % new_category.dn, fg="green"))
        click.echo(click.style("... with %d entries" % len(entry_dns), fg="green"))
        if new_obj:
            new_obj.props.categories.append(new_category.dn)
    if new_obj:
        new_obj.save()


@click.command(context_settings={"help_option_names": ['-h', '--help']})
@click.option('--binddn', help="DN of account to use to write to LDAP (e.g. uid=Administrator,cn=users,..).")
@click.option('--bindpwdfile', help="File containing password of user provided by --binddn.")
def main(binddn, bindpwdfile):
    """
    Migrates UDM objects from the (now) outdated settings/portal, settings/portal_category
    and settings/portal_entry modules to their new portals/* equivalents.

    The names of the old objects will be the names of the new objects. If those objects are
    found, no migration will happen.
    """
    ucr = ConfigRegistry()
    ucr.load()

    if ucr["server/role"] in ("domaincontroller_master", "domaincontroller_backup"):
        binddn = password = None

    if not binddn:
        udm = UDM.admin().version(2)
    else:
        server = ucr["ldap/master"]
        server_port = ucr["ldap/master/port"]
        password = open(bindpwdfile).read().strip()
        udm = UDM.credentials(binddn, password, server=server, port=server_port).version(2)

    # clear_objects(udm, "portals/portal")
    # clear_objects(udm, "portals/category")
    # clear_objects(udm, "portals/entry")

    click.echo("Alright, we will now migrate old portal data...")
    click.echo("First things first, let's start with settings/portal_entry")
    for obj in udm.get("settings/portal_entry").search():
        migrate_entry(udm, obj)

    click.echo("Great, now all settings/portal")
    for obj in udm.get("settings/portal").search():
        migrate_portal(udm, obj)


if __name__ == '__main__':
    main()
