#!/usr/bin/python
# -*- coding: utf-8 -*-

# Copyright (c) 2015 IBM Corporation
# GNU General Public License v3.0+ (see COPYING or https://www.gnu.org/licenses/gpl-3.0.txt)

DOCUMENTATION = r'''
---
module: project
short_description: Manage OpenStack Identity (Keystone) projects
author: OpenStack Ansible SIG
description:
  - Create, update or delete a OpenStack Identity (Keystone) project.
options:
  name:
    description:
      - Name for the project.
      - This attribute cannot be updated.
    required: true
    type: str
  description:
    description:
      - Description for the project.
    type: str
  domain:
    description:
      - Domain name or id to create the project in if the cloud supports
        domains.
    aliases: ['domain_id']
    type: str
  extra_specs:
    description:
      - Additional properties to be associated with this project.
    type: dict
    aliases: ['properties']
  is_enabled:
    description:
      - Whether this project is enabled or not.
    aliases: ['enabled']
    type: bool
  state:
    description:
      - Should the resource be present or absent.
    choices: [present, absent]
    default: present
    type: str
extends_documentation_fragment:
  - openstack.cloud.openstack
'''

EXAMPLES = r'''
- name: Create a project
  openstack.cloud.project:
    cloud: mycloud
    description: demodescription
    domain: demoid
    is_enabled: True
    name: demoproject
    extra_specs:
      internal_alias: demo_project
    state: present

- name: Delete a project
  openstack.cloud.project:
    cloud: mycloud
    endpoint_type: admin
    name: demoproject
    state: absent
'''

RETURN = r'''
project:
  description: Dictionary describing the project.
  returned: On success when I(state) is C(present).
  type: dict
  contains:
    description:
      description: Project description
      type: str
      sample: "demodescription"
    domain_id:
      description: Domain ID to which the project belongs
      type: str
      sample: "default"
    id:
      description: Project ID
      type: str
      sample: "f59382db809c43139982ca4189404650"
    is_domain:
      description: Indicates whether the project also acts as a domain.
      type: bool
    is_enabled:
      description: Indicates whether the project is enabled
      type: bool
    name:
      description: Project name
      type: str
      sample: "demoproject"
    options:
      description: The resource options for the project
      type: dict
    parent_id:
      description: The ID of the parent of the project
      type: str
    tags:
      description: A list of associated tags
      type: list
      elements: str
'''

from ansible_collections.openstack.cloud.plugins.module_utils.openstack import OpenStackModule


class IdentityProjectModule(OpenStackModule):
    argument_spec = dict(
        description=dict(),
        domain=dict(aliases=['domain_id']),
        extra_specs=dict(type='dict', aliases=['properties']),
        is_enabled=dict(type='bool', aliases=['enabled']),
        name=dict(required=True),
        state=dict(default='present', choices=['absent', 'present'])
    )
    module_kwargs = dict(
        supports_check_mode=True
    )

    def run(self):
        state = self.params['state']

        project = self._find()

        if self.ansible.check_mode:
            self.exit_json(changed=self._will_change(state, project))

        if state == 'present' and not project:
            # Create project
            project = self._create()
            self.exit_json(changed=True,
                           project=project.to_dict(computed=False))

        elif state == 'present' and project:
            # Update project
            update = self._build_update(project)
            if update:
                project = self._update(project, update)

            self.exit_json(changed=bool(update),
                           project=project.to_dict(computed=False))

        elif state == 'absent' and project:
            # Delete project
            self._delete(project)
            self.exit_json(changed=True)

        elif state == 'absent' and not project:
            # Do nothing
            self.exit_json(changed=False)

    def _build_update(self, project):
        update = {}

        # Params name and domain are being used to find this project.

        non_updateable_keys = [k for k in []
                               if self.params[k] is not None
                               and self.params[k] != project[k]]

        if non_updateable_keys:
            self.fail_json(msg='Cannot update parameters {0}'
                               .format(non_updateable_keys))

        attributes = dict((k, self.params[k])
                          for k in ['description', 'is_enabled']
                          if self.params[k] is not None
                          and self.params[k] != project[k])

        extra_specs = self.params['extra_specs']
        if extra_specs:
            duplicate_keys = set(attributes.keys()) & set(extra_specs.keys())
            if duplicate_keys:
                raise ValueError('Duplicate key(s) in extra_specs: {0}'
                                 .format(', '.join(list(duplicate_keys))))
            for k, v in extra_specs.items():
                if v != project[k]:
                    attributes[k] = v

        if attributes:
            update['attributes'] = attributes

        return update

    def _create(self):
        kwargs = dict((k, self.params[k])
                      for k in ['description', 'is_enabled', 'name']
                      if self.params[k] is not None)

        domain_name_or_id = self.params['domain']
        if domain_name_or_id is not None:
            domain = self.conn.identity.find_domain(domain_name_or_id,
                                                    ignore_missing=False)
            kwargs['domain_id'] = domain.id

        extra_specs = self.params['extra_specs']
        if extra_specs:
            duplicate_keys = set(kwargs.keys()) & set(extra_specs.keys())
            if duplicate_keys:
                raise ValueError('Duplicate key(s) in extra_specs: {0}'
                                 .format(', '.join(list(duplicate_keys))))
            kwargs = dict(kwargs, **extra_specs)

        return self.conn.identity.create_project(**kwargs)

    def _delete(self, project):
        self.conn.identity.delete_project(project.id)

    def _find(self):
        name = self.params['name']
        kwargs = {}

        domain_name_or_id = self.params['domain']
        if domain_name_or_id is not None:
            domain = self.conn.identity.find_domain(domain_name_or_id,
                                                    ignore_missing=False)
            kwargs['domain_id'] = domain.id

        return self.conn.identity.find_project(name_or_id=name,
                                               **kwargs)

    def _update(self, project, update):
        attributes = update.get('attributes')
        if attributes:
            project = self.conn.identity.update_project(project.id,
                                                        **attributes)

        return project

    def _will_change(self, state, project):
        if state == 'present' and not project:
            return True
        elif state == 'present' and project:
            return bool(self._build_update(project))
        elif state == 'absent' and project:
            return True
        else:
            # state == 'absent' and not project:
            return False


def main():
    module = IdentityProjectModule()
    module()


if __name__ == '__main__':
    main()