#!/usr/bin/python


class UsersDB(object):
    def __init__(self, module):
        self.module = module
        self.users_db = self.module.params["usersdb"]
        self.source_user_db = self.module.params["source_userdb"]
        # If we have to userdb and source db lets merge them if not
        if self.users_db and self.source_user_db:
            self.users_db.update(self.source_user_db)
        if self.source_user_db and not self.users_db:
            self.users_db = self.source_user_db
        if not self.users_db and not self.source_user_db:
            self.module.fail_json(msg="Missing argument. You must defined either 'usersdb' or 'source_userdb'.")

        self.teams_db = self.module.params["teamsdb"]
        self.servers_db = self.module.params["serversdb"]
        # Databases
        self.lookup_key_db = {}  # used for quick lookup
        self.expanded_users_db = []  # Used in simple mode
        self.expanded_users_key_db = []  # Used in simple mode
        self.expanded_server_db = []  # Used in advanced mode for merged User + server
        self.expanded_server_key_db = []  # Used in advanced mode

    def _concat_keys(self, user_name, user_keys=None, server_keys=None, user_status=False):
        # Concat keys (if possible) and update username to keys
        new_user_keys = []
        if user_keys and server_keys:
            for user_key in user_keys:
                new_user_key = dict(user_key, **server_keys)
                new_user_key.pop("name", None)
                new_user_key.pop("user", None)
                if user_status:
                    new_user_key.update({"state": "absent"})
                new_user_keys.append(new_user_key)
        elif server_keys:
            # server key is dic
            server_keys.pop("name", None)
            new_user_keys = [server_keys]
        elif user_keys:
            for user_key in user_keys:
                new_user_key = user_key
                new_user_key.pop("user", None)
                new_user_key.pop("name", None)
                if user_status:
                    new_user_key.update({"state": "absent"})
                new_user_keys.append(new_user_key)
        else:
            # TODO: Should work without keys
            # self.module.fail_json(msg="user '{}' list has no keys defined.".format(user_name))
            pass
        return new_user_keys

    def _merge_key(self, user_keys, sever_keys, user_name, user_status=False):
        # Rules ( no real merge happens )
        # 1- Default use the user key
        # 2- if server has defined keys then use those instead no merge here
        if sever_keys:
            merged_keys = []
            for server_key in sever_keys:
                if "user" in server_key:
                    user = server_key.pop("user", False)
                    account_key = self.lookup_key_db.get(user)
                    user_definition = self.users_db.get(user, {})
                    if user_definition.get("state", "present") in ("absent", "delete", "deleted", "remove", "removed"):
                        user_status = "absent"
                    merged_keys += self._concat_keys(user_name, account_key, server_key, user_status=user_status)
                elif "team" in server_key:
                    self.module.fail_json(msg="Team key is not yet implemented")
                elif "key" in server_key:
                    merged_keys += self._concat_keys(user_name, server_keys=server_key, user_status=user_status)
                else:
                    # TODO: Should work without keys
                    # self.module.fail_json(msg="user '{}' list has no keys defined.".format(user_name))
                    pass
            return merged_keys
        else:
            return self._concat_keys(user_name, user_keys=user_keys, user_status=user_status)

    def _merge_user(self, user_name, user_server):
        user_definition = self.users_db.get(user_name, False)

        if not user_definition:
            self.module.fail_json(msg="'%s' user has no definition" % user_name)

        if user_definition.get("state", "present") in ("absent", "delete", "deleted", "remove", "removed"):
            user_status = "absent"
        else:
            user_status = False

        # Merge User and Server ( Server has precedence in this case )
        merged_user = dict(user_definition.items() + user_server.items())

        if user_status:
            merged_user.update({"state": "absent"})
        user_server = merged_user
        user_db_key = self.lookup_key_db.get(user_name, None)
        user_server_keys = self._merge_key(user_db_key, user_server.get("keys", None), user_name, user_status)
        # In case of team user dict will not be defined so lets just define anyway
        user_server.update({"user": user_name})

        # Populate DBs
        user_server.pop("keys", None)  # Get rid of keys
        user_server.pop("team", None)  # Get rid of team if exists
        self.expanded_server_db.append(user_server)
        if len(user_server_keys) > 0:
            self.expanded_server_key_db.append({"user": user_name, "keys": user_server_keys})

    def expand_servers(self):
        # Advanced mode Merges users and servers data
        # Expand server will overwrite same attributes defined in user db except for state = "absent"
        for user_server in self.servers_db:
            team_name = user_server.get("team", False)
            user_name = user_server.get("user", False) or user_server.get("name", False)

            if user_name:
                self._merge_user(user_name, user_server)
            elif team_name:
                team_definition = self.teams_db.get(team_name, False)
                if not team_definition:
                    self.module.fail_json(msg="'%s' team has no definition" % team_name)
                for user_in_team in team_definition:
                    self._merge_user(user_in_team, user_server)
            else:
                self.module.fail_json(msg="Your server definition has no user or team. Please check your data type. "
                                          "for '{}'".format(user_server))

    def expand_keys(self, keys, user):
        # if len(keys) == 0:
        #     # TODO: Should work without keys
        #     self.module.fail_json(msg="user '{}' has no keys defined.".format(user))

        user_keys = []
        # If key is not a list than its a raw key string
        if not isinstance(keys, list):
            user_keys.append({"key": keys})
        else:
            for key in keys:
                # Basic syntax check
                if isinstance(key, basestring) and "ssh-" in key:
                    user_keys.append({"key": key})
                elif "key" not in key and "name" not in key:
                    # TODO: Should work without keys
                    # self.module.fail_json(msg="user '{}' list has no keys defined.".format(key.keys()))
                    pass
                else:
                    # All is okay just add the dict
                    user_keys.append(key)
        return user_keys

    def expand_users(self):
        # Get User database which is a dic and create expendaded_user_db and key_db
        # Put keys in right dictionary format
        for username, user_options in self.users_db.iteritems():
            # 1- Convert dic to list (servers_db style)
            user = {"name": username}  # create the account name
            user.update(user_options)  # update all other option
            # 2- Compile key
            unformatted_keys = user_options.get("keys", [])
            keys = self.expand_keys(unformatted_keys, user)
            # 3- remove keys from userdb if exists
            user.pop("keys", None)
            # 4- Populate DBs
            self.expanded_users_db.append(user)  # Populate new list user db
            self.expanded_users_key_db.append({"user": username, "keys": keys})
            if len(keys) > 0:
                self.lookup_key_db.update({username: keys})  # Populate dict key db

    def main(self):
        self.expand_users()

        if self.servers_db and len(self.servers_db) > 0:
            # Advanced mode we have to do merges and stuff :D
            self.expand_servers()
            result = {"changed": False, "msg": "",
                      "users_db": self.expanded_server_db,
                      "key_db": self.expanded_server_key_db}
        else:
            # Simple mode no servers db
            result = {"changed": False, "msg": "",
                      "users_db": self.expanded_users_db,
                      "key_db": self.expanded_users_key_db}
        self.module.exit_json(**result)


def main():
    module = AnsibleModule(
        argument_spec=dict(
            usersdb=dict(default=None, required=False, type="dict"),
            source_userdb=dict(default=None, required=False, type="dict"),
            teamsdb=dict(default=None, required=False),  # Should be dict but would break if value is false/none
            serversdb=dict(default=None, required=False),
        ),
        supports_check_mode=False
    )
    UsersDB(module).main()

# import module snippets
from ansible.module_utils.basic import *
main()