# Copyright (c) 2013 Mirantis Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.


from oslo_utils import uuidutils
import sqlalchemy as sa
from sqlalchemy.orm import relationship

from sahara.db.sqlalchemy import model_base as mb
from sahara.db.sqlalchemy import types as st


# Helpers

def _generate_unicode_uuid():
    return uuidutils.generate_uuid()


def _id_column():
    return sa.Column(sa.String(36),
                     primary_key=True,
                     default=_generate_unicode_uuid)


# Main objects: Cluster, NodeGroup, Instance

class Cluster(mb.SaharaBase):
    """Contains all info about cluster."""

    __tablename__ = 'clusters'

    __table_args__ = (
        sa.UniqueConstraint('name', 'tenant_id'),
    )

    id = _id_column()
    name = sa.Column(sa.String(80), nullable=False)
    description = sa.Column(sa.Text)
    tenant_id = sa.Column(sa.String(36))
    trust_id = sa.Column(sa.String(36))
    is_transient = sa.Column(sa.Boolean, default=False)
    plugin_name = sa.Column(sa.String(80), nullable=False)
    hadoop_version = sa.Column(sa.String(80), nullable=False)
    cluster_configs = sa.Column(st.JsonDictType())
    default_image_id = sa.Column(sa.String(36))
    neutron_management_network = sa.Column(sa.String(36))
    anti_affinity = sa.Column(st.JsonListType())
    management_private_key = sa.Column(sa.Text, nullable=False)
    management_public_key = sa.Column(sa.Text, nullable=False)
    user_keypair_id = sa.Column(sa.String(80))
    status = sa.Column(sa.String(80))
    status_description = sa.Column(st.LongText())
    info = sa.Column(st.JsonDictType())
    extra = sa.Column(st.JsonDictType())
    rollback_info = sa.Column(st.JsonDictType())
    sahara_info = sa.Column(st.JsonDictType())
    use_autoconfig = sa.Column(sa.Boolean(), default=True)
    provision_progress = relationship('ClusterProvisionStep',
                                      cascade="all,delete",
                                      backref='cluster',
                                      lazy='joined')
    verification = relationship('ClusterVerification', cascade="all,delete",
                                backref="cluster", lazy='joined')
    node_groups = relationship('NodeGroup', cascade="all,delete",
                               backref='cluster', lazy='joined')
    cluster_template_id = sa.Column(sa.String(36),
                                    sa.ForeignKey('cluster_templates.id'))
    cluster_template = relationship('ClusterTemplate',
                                    backref="clusters", lazy='joined')
    shares = sa.Column(st.JsonListType())
    is_public = sa.Column(sa.Boolean())
    is_protected = sa.Column(sa.Boolean())
    domain_name = sa.Column(sa.String(255))

    def to_dict(self, show_progress=False):
        d = super(Cluster, self).to_dict()
        d['node_groups'] = [ng.to_dict() for ng in self.node_groups]
        d['provision_progress'] = [pp.to_dict(show_progress) for pp in
                                   self.provision_progress]
        if self.v
erification:
            d['verification'] = self.verification[0].to_dict()
        return d


class NodeGroup(mb.SaharaBase):
    """Specifies group of nodes within a cluster."""

    __tablename__ = 'node_groups'

    __table_args__ = (
        sa.UniqueConstraint('name', 'cluster_id'),
    )

    id = _id_column()
    name = sa.Column(sa.String(80), nullable=False)
    tenant_id = sa.Column(sa.String(36))
    flavor_id = sa.Column(sa.String(36), nullable=False)
    image_id = sa.Column(sa.String(36))

    image_username = sa.Column(sa.String(36))
    node_processes = sa.Column(st.JsonListType())
    node_configs = sa.Column(st.JsonDictType())
    volumes_per_node = sa.Column(sa.Integer)
    volumes_size = sa.Column(sa.Integer)
    volumes_availability_zone = sa.Column(sa.String(255))
    volume_mount_prefix = sa.Column(sa.String(80))
    volume_type = sa.Column(sa.String(255))
    count = sa.Column(sa.Integer, nullable=False)
    use_autoconfig = sa.Column(sa.Boolean(), default=True)

    instances = relationship('Instance', cascade="all,delete",
                             backref='node_group',
                             order_by="Instance.instance_name", lazy='joined')
    cluster_id = sa.Column(sa.String(36), sa.ForeignKey('clusters.id'))
    node_group_template_id = sa.Column(sa.String(36),
                                       sa.ForeignKey(
                                           'node_group_templates.id'))
    node_group_template = relationship('NodeGroupTemplate',
                                       backref="node_groups", lazy='joined')
    floating_ip_pool = sa.Column(sa.String(36))
    security_groups = sa.Column(st.JsonListType())
    auto_security_group = sa.Column(sa.Boolean())
    availability_zone = sa.Column(sa.String(255))
    open_ports = sa.Column(st.JsonListType())
    is_proxy_gateway = sa.Column(sa.Boolean())
    volume_local_to_instance = sa.Column(sa.Boolean())
    shares = sa.Column(st.JsonListType())

    def to_dict(self):
        d = super(NodeGroup, self).to_dict()
        d['instances'] = [i.to_dict() for i in self.instances]

        return d


class Instance(mb.SaharaBase):
    """An OpenStack instance created for the cluster."""

    __tablename__ = 'instances'

    __table_args__ = (
        sa.UniqueConstraint('instance_id', 'node_group_id'),
    )

    id = _id_column()
    tenant_id = sa.Column(sa.String(36))
    node_group_id = sa.Column(sa.String(36), sa.ForeignKey('node_groups.id'))
    instance_id = sa.Column(sa.String(36))
    instance_name = sa.Column(sa.String(80), nullable=False)
    internal_ip = sa.Column(sa.String(45))
    management_ip = sa.Column(sa.String(45))
    volumes = sa.Column(st.JsonListType())
    storage_devices_number = sa.Column(sa.Integer)
    dns_hostname = sa.Column(sa.String(255))


# Template objects: ClusterTemplate, NodeGroupTemplate, TemplatesRelation

class ClusterTemplate(mb.SaharaBase):
    """Template for Cluster."""

    __tablename__ = 'cluster_templates'

    __table_args__ = (
        sa.UniqueConstraint('name', 'tenant_id'),
    )

    id = _id_column()
    name = sa.Column(sa.String(80), nullable=False)
    description = sa.Column(sa.Text)
    cluster_configs = sa.Column(st.JsonDictType())
    default_image_id = sa.Column(sa.String(36))
    anti_affinity = sa.Column(st.JsonListType())
    tenant_id = sa.Column(sa.String(36))
    neutron_management_network = sa.Column(sa.String(36))
    plugin_name = sa.Column(sa.String(80), nullable=False)
    hadoop_version = sa.Column(sa.String(80), nullable=False)
    node_groups = relationship('TemplatesRelation', cascade="all,delete",
                               backref='cluster_template', lazy='joined')
    is_default = sa.Column(sa.Boolean(), default=False)
    use_autoconfig = sa.Column(sa.Boolean(), default=True)
    shares = sa.Column(st.JsonListType())
    is_public = sa.Column(sa.Boolean())
    is_protected = sa.Column(sa.Boolean())
    domain_name = sa.Column(sa.String(255))

    def to_dict(self):
        d = super(ClusterTemplate, self).to_dict()
        d['node_groups'] = [tr.to_dict() for tr in
                            self.node_groups]
        return d


class NodeGroupTemplate(mb.SaharaBase):
    """Template for NodeGroup."""

    __tablename__ = 'node_group_templates'

    __table_args__ = (
        sa.UniqueConstraint('name', 'tenant_id'),
    )

    id = _id_column()
    name = sa.Column(sa.String(80), nullable=False)
    description = sa.Column(sa.Text)
    tenant_id = sa.Column(sa.String(36))
    flavor_id = sa.Column(sa.String(36), nullable=False)
    image_id = sa.Column(sa.String(36))
    plug