#    Copyright 2015 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.

"""Fuel 8.0

Revision ID: 43b2cb64dae6
Revises: 1e50a4903910
Create Date: 2015-10-15 17:20:11.132934

"""

# revision identifiers, used by Alembic.
revision = '43b2cb64dae6'
down_revision = '1e50a4903910'

from alembic import op
from oslo_serialization import jsonutils
import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

from nailgun import consts
from nailgun.db.sqlalchemy.models import fields
from nailgun.utils.migration import drop_enum
from nailgun.utils.migration import upgrade_enum


release_states_old = (
    'available',
    'unavailable',
)
release_states_new = (
    'available',
    'unavailable',
    'manageonly',
)

task_statuses_old = (
    'ready',
    'running',
    'error'
)

task_statuses_new = task_statuses_old + (
    'pending',
)


task_names_old = (
    'super',
    'deploy',
    'deployment',
    'provision',
    'stop_deployment',
    'reset_environment',
    'update',
    'spawn_vms',
    'node_deletion',
    'cluster_deletion',
    'remove_images',
    'check_before_deployment',
    'check_networks',
    'verify_networks',
    'check_dhcp',
    'verify_network_connectivity',
    'multicast_verification',
    'check_repo_availability',
    'check_repo_availability_with_setup',
    'dump',
    'capacity_log',
    'create_stats_user',
    'remove_stats_user',
)

task_names_new = task_names_old + (
    'update_dnsmasq',
)


node_errors_old = (
    'deploy',
    'provision',
    'deletion',
)

node_errors_new = (
    'deploy',
    'provision',
    'deletion',
    'discover',
)

openstack_config_types = (
    'cluster',
    'role',
    'node',
)


def upgrade():
    upgrade_nodegroups_name_cluster_constraint()
    upgrade_release_state()
    task_statuses_upgrade()
    task_names_upgrade()
    add_node_discover_error_upgrade()
    upgrade_neutron_parameters()
    upgrade_cluster_plugins()
    upgrade_add_baremetal_net()
    upgrade_with_components()
    plugin_links_upgrade()
    cluster_plugin_links_upgrade()
    upgrade_master_node_bootstrap_settings()
    upgrade_all_network_data_from_string_to_appropriate_data_type()
    create_openstack_configs_table()
    upgrade_master_node_ui_settings()
    upgrade_plugins_parameters()
    upgrade_oswl_stats_version_info()
    upgrade_nodegroups_parameters()


def downgrade():
    downgrade_nodegroups_parameters()
    downgrade_oswl_stats_version_info()
    downgrade_plugins_parameters()
    downgrade_master_node_ui_settings()
    downgrade_openstack_configs()
    downgrade_all_network_data_to_string()
    downgrade_master_node_bootstrap_settings()
    cluster_plugin_links_downgrade()
    plugin_links_downgrade()
    downgrade_with_components()
    downgrade_add_baremetal_net()
    downgrade_cluster_plugins()
    downgrade_neutron_parameters()
    add_node_discover_error_downgrade()
    task_names_downgrade()
    task_statuses_downgrade()
    downgrade_release_state()
    downgrade_nodegroups_name_cluster_constraint()


def upgrade_release_state():
    connection = op.get_bind()
    op.drop_column('releases', 'is_deployable')

    upgrade_enum(
        'releases',
        'state',
        'release_state',
        release_states_old,
        release_states_new,
    )

    connection.execute(sa.sql.text(
        "UPDATE releases SET state='manageonly' WHERE state!='unavailable'"))


def upgrade_nodegroups_name_cluster_constraint():
    connection = op.get_bind()
    select_query = sa.sql.text(
        "SELECT name FROM nodegroups GROUP BY name HAVING COUNT(*) >= 2")
    update_query = sa.sql.text(
        "UPDATE nodegroups SET name = :name WHERE id = :ng_id")
    ng_query = sa.sql.text(
        "SELECT id, name FROM nodegroups WHERE name = :name")
    for name in connection.execute(select_query):
        for i, data in enumerate(connection.execute(ng_query, name=name[0])):
            connection.execute(
                update_query,
                ng_id=data[0], name="{0}_{1}".format(data[1], i))

    op.create_unique_constraint(
        '_name_cluster_uc',
        'nodegroups',
        [
            'cluster_id',
            'name'
        ]
    )


def downgrade_nodegroups_name_cluster_constraint():
    op.drop_constraint('_name_cluster_uc', 'nodegroups',)


def upgrade_with_components():
    op.add_column(
        'plugins',
        sa.Column(
            'components_metadata',
            fields.JSON(),
            server_default='[]'
        )
    )
    op.add_column(
        'releases',
        sa.Column(
            'components_metadata',
            fields.JSON(),
            server_default='[]'
        )
    )
    op.add_column(
        'clusters',
        sa.Column(
            'components',
            fields.JSON(),
            nullable=False,
            server_default='[]')
    )


def create_openstack_configs_table():
    op.create_table(
        'openstack_configs',
        sa.Column('id', sa.Integer, nullable=False),
        sa.Column('is_active', sa.Boolean, nullable=False),
        sa.Column(
            'config_type',
            sa.Enum(*openstack_config_types, name='openstack_config_types'),
            nullable=False),
        sa.Column('cluster_id', sa.Integer, nullable=False),
        sa.Column('node_id', sa.Integer, nullable=True),
        sa.Column('node_role', sa.String(length=64), nullable=True),
        sa.Column('created_at', sa.DateTime, nullable=False),
        sa.Column(
            'configuration', fields.JSON,