"""Initialize database

Revision ID: aa42863f0b44
Revises:
Create Date: 2024-09-19 08:18:04.298905

"""

import sqlalchemy as sa
from alembic import op
from geoalchemy2 import Geometry
from sqlalchemy.dialects import postgresql

# revision identifiers, used by Alembic.
revision = "aa42863f0b44"
down_revision = None
branch_labels = None
depends_on = None


def upgrade():
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_geospatial_table(
        "base_geo_data",
        sa.Column(
            "type",
            sa.Enum(
                "geo_data",
                "generated_geo_data",
                "proposition_geo_data",
                name="geodatatype",
            ),
            nullable=True,
        ),
        sa.Column("name", sa.String(), nullable=False),
        sa.Column(
            "extent",
            Geometry(
                spatial_index=False,
                from_text="ST_GeomFromEWKT",
                name="geometry",
            ),
            nullable=True,
        ),
        sa.Column("properties_modified_at", sa.DateTime(), nullable=True),
        sa.Column("created_at", sa.DateTime(), nullable=True),
        sa.Column("modified_at", sa.DateTime(), nullable=True),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_base_geo_data")),
    )
    with op.batch_alter_table("base_geo_data", schema=None) as batch_op:
        batch_op.create_geospatial_index(
            "idx_base_geo_data_extent",
            ["extent"],
            unique=False,
            postgresql_using="gist",
            postgresql_ops={},
        )

    op.create_table(
        "preference_model",
        sa.Column(
            "pref_type",
            sa.Enum(
                "categories_rule",
                "continuous_rule",
                "geo_buffer",
                "mrsort",
                "weighted_sum",
                name="preftype",
            ),
            nullable=True,
        ),
        sa.Column("name", sa.String(), nullable=True),
        sa.Column("data_generator_id", sa.Integer(), nullable=False),
        sa.Column(
            "keep_overlap",
            sa.Enum("all", "min", "max", "sum", "average", name="keepoverlap"),
            nullable=False,
        ),
        sa.Column("cut_to_extent", sa.Boolean(), nullable=True),
        sa.Column("dissolve_adjacent", sa.Boolean(), nullable=True),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column("radius", sa.BigInteger(), nullable=True),
        sa.Column("majority_threshold", sa.Float(), nullable=True),
        sa.Column("rule", sa.String(), nullable=True),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_preference_model")),
    )
    op.create_table(
        "project_data",
        sa.Column(
            "data_type",
            sa.Enum("geo_data", "generator", name="datatype"),
            nullable=True,
        ),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column("data_id", sa.Integer(), nullable=True),
        sa.Column("last_update", sa.DateTime(), nullable=True),
        sa.Column("project_id", sa.Integer(), nullable=False),
        sa.Column("description", sa.String(), nullable=True),
        sa.Column("name", sa.String(), nullable=True),
        sa.Column("active_model_id", sa.Integer(), nullable=True),
        sa.ForeignKeyConstraint(
            ["active_model_id"],
            ["preference_model.id"],
            name=op.f("fk_project_data_active_model_id_preference_model"),
        ),
        sa.ForeignKeyConstraint(
            ["data_id"],
            ["base_geo_data.id"],
            name=op.f("fk_project_data_data_id_base_geo_data"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_project_data")),
    )
    op.create_foreign_key(
        op.f("fk_preference_model_data_generator_id_project_data"),
        "preference_model",
        "project_data",
        ["data_generator_id"],
        ["id"],
    )
    op.create_table(
        "user",
        sa.Column("username", sa.String(), nullable=True),
        sa.Column("email", sa.String(), nullable=True),
        sa.Column("password_hash", sa.String(), nullable=True),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_user")),
        sa.UniqueConstraint("email", name=op.f("uq_user_email")),
        sa.UniqueConstraint("username", name=op.f("uq_user_username")),
    )
    op.create_table(
        "data_attribute",
        sa.Column(
            "type",
            sa.Enum(
                "quantitative", "nominal", "ordinal", name="attributetype"
            ),
            nullable=True,
        ),
        sa.Column("name", sa.String(), nullable=False),
        sa.Column(
            "statistics",
            postgresql.JSONB(astext_type=sa.Text()),
            nullable=True,
        ),
        sa.Column("geo_data_id", sa.Integer(), nullable=False),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column(
            "order", postgresql.JSONB(astext_type=sa.Text()), nullable=True
        ),
        sa.ForeignKeyConstraint(
            ["geo_data_id"],
            ["base_geo_data.id"],
            name=op.f("fk_data_attribute_geo_data_id_base_geo_data"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_data_attribute")),
    )
    with op.batch_alter_table("data_attribute", schema=None) as batch_op:
        batch_op.create_index(
            batch_op.f("ix_data_attribute_geo_data_id"),
            ["geo_data_id"],
            unique=False,
        )

    op.create_table(
        "data_input_association",
        sa.Column("project_data_id", sa.Integer(), nullable=False),
        sa.Column("input_data_id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["input_data_id"],
            ["project_data.id"],
            name=op.f("fk_data_input_association_input_data_id_project_data"),
        ),
        sa.ForeignKeyConstraint(
            ["project_data_id"],
            ["project_data.id"],
            name=op.f(
                "fk_data_input_association_project_data_id_project_data"
            ),
        ),
        sa.PrimaryKeyConstraint(
            "project_data_id",
            "input_data_id",
            name=op.f("pk_data_input_association"),
        ),
    )
    op.create_table(
        "data_share",
        sa.Column("data_id", sa.Integer(), nullable=False),
        sa.Column("uid", sa.UUID(), nullable=False),
        sa.Column("expiration", sa.DateTime(), nullable=True),
        sa.Column("expired", sa.Boolean(), nullable=True),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["data_id"],
            ["project_data.id"],
            name=op.f("fk_data_share_data_id_project_data"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_data_share")),
        sa.UniqueConstraint("uid", name=op.f("uq_data_share_uid")),
    )
    op.create_table(
        "discrete_category",
        sa.Column("type", sa.String(), nullable=True),
        sa.Column("name", sa.String(), nullable=True),
        sa.Column("position", sa.Integer(), nullable=True),
        sa.Column("preference_model_id", sa.Integer(), nullable=True),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column(
            "rules", postgresql.ARRAY(sa.String(), dimensions=1), nullable=True
        ),
        sa.ForeignKeyConstraint(
            ["preference_model_id"],
            ["preference_model.id"],
            name=op.f(
                "fk_discrete_category_preference_model_id_preference_model"
            ),
            ondelete="CASCADE",
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_discrete_category")),
    )
    op.create_geospatial_table(
        "feature",
        sa.Column(
            "geom",
            Geometry(
                spatial_index=False,
                from_text="ST_GeomFromEWKT",
                name="geometry",
            ),
            nullable=True,
        ),
        sa.Column("geo_data_id", sa.Integer(), nullable=True),
        sa.Column(
            "explainability",
            postgresql.JSONB(astext_type=sa.Text()),
            nullable=True,
        ),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["geo_data_id"],
            ["base_geo_data.id"],
            name=op.f("fk_feature_geo_data_id_base_geo_data"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_feature")),
    )
    with op.batch_alter_table("feature", schema=None) as batch_op:
        batch_op.create_geospatial_index(
            "idx_feature_geom",
            ["geom"],
            unique=False,
            postgresql_using="gist",
            postgresql_ops={},
        )
        batch_op.create_index(
            batch_op.f("ix_feature_geo_data_id"), ["geo_data_id"], unique=False
        )

    op.create_geospatial_table(
        "project",
        sa.Column("is_template", sa.Boolean(), nullable=False),
        sa.Column("name", sa.String(), nullable=False),
        sa.Column("manager_id", sa.Integer(), nullable=True),
        sa.Column("description", sa.String(), nullable=True),
        sa.Column(
            "extent",
            Geometry(
                spatial_index=False,
                from_text="ST_GeomFromEWKT",
                name="geometry",
                nullable=False,
            ),
            nullable=False,
        ),
        sa.Column("created_at", sa.DateTime(), nullable=True),
        sa.Column("modified_at", sa.DateTime(), nullable=True),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column("is_public", sa.Boolean(), nullable=False),
        sa.ForeignKeyConstraint(
            ["manager_id"],
            ["user.id"],
            name=op.f("fk_project_manager_id_user"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_project")),
    )
    with op.batch_alter_table("project", schema=None) as batch_op:
        batch_op.create_geospatial_index(
            "idx_project_extent",
            ["extent"],
            unique=False,
            postgresql_using="gist",
            postgresql_ops={},
        )
    op.create_foreign_key(
        op.f("fk_project_data_project_id_project"),
        "project_data",
        "project",
        ["project_id"],
        ["id"],
    )

    op.create_table(
        "stream_geo_data",
        sa.Column(
            "type", sa.Enum("wfs", "wms", name="streamdatatype"), nullable=True
        ),
        sa.Column("name", sa.String(), nullable=False),
        sa.Column("title", sa.String(), nullable=True),
        sa.Column("original_name", sa.String(), nullable=True),
        sa.Column("url", sa.String(), nullable=False),
        sa.Column("upload_user_id", sa.Integer(), nullable=True),
        sa.Column("description", sa.String(), nullable=True),
        sa.Column(
            "keywords",
            postgresql.ARRAY(sa.String(), dimensions=1),
            nullable=True,
        ),
        sa.Column("is_public", sa.Boolean(), nullable=False),
        sa.Column("created_at", sa.DateTime(), nullable=True),
        sa.Column("modified_at", sa.DateTime(), nullable=True),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["upload_user_id"],
            ["user.id"],
            name=op.f("fk_stream_geo_data_upload_user_id_user"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_stream_geo_data")),
    )
    op.create_table(
        "user_permission",
        sa.Column("user_id", sa.Integer(), nullable=False),
        sa.Column(
            "ability",
            sa.Enum(
                "create_project", "create_geo_data", name="permissionability"
            ),
            nullable=False,
        ),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["user_id"],
            ["user.id"],
            name=op.f("fk_user_permission_user_id_user"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_user_permission")),
    )
    op.create_table(
        "user_refresh_token",
        sa.Column("user_id", sa.Integer(), nullable=True),
        sa.Column("refresh_token", sa.LargeBinary(), nullable=True),
        sa.Column("expiration", sa.DateTime(), nullable=True),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["user_id"],
            ["user.id"],
            name=op.f("fk_user_refresh_token_user_id_user"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_user_refresh_token")),
    )
    op.create_table(
        "data_value",
        sa.Column(
            "type",
            sa.Enum(
                "quantitative", "nominal", "ordinal", name="attributetype"
            ),
            nullable=True,
        ),
        sa.Column("attribute_id", sa.Integer(), nullable=True),
        sa.Column("feature_id", sa.Integer(), nullable=True),
        sa.Column(
            "value", postgresql.JSONB(astext_type=sa.Text()), nullable=True
        ),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["attribute_id"],
            ["data_attribute.id"],
            name=op.f("fk_data_value_attribute_id_data_attribute"),
        ),
        sa.ForeignKeyConstraint(
            ["feature_id"],
            ["feature.id"],
            name=op.f("fk_data_value_feature_id_feature"),
            ondelete="CASCADE",
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_data_value")),
        sa.UniqueConstraint(
            "attribute_id",
            "feature_id",
            name=op.f("uq_data_value_attribute_id"),
        ),
    )
    with op.batch_alter_table("data_value", schema=None) as batch_op:
        batch_op.create_index(
            batch_op.f("ix_data_value_attribute_id"),
            ["attribute_id"],
            unique=False,
        )
        batch_op.create_index(
            batch_op.f("ix_data_value_feature_id"),
            ["feature_id"],
            unique=False,
        )

    op.create_table(
        "default_value",
        sa.Column("attribute_id", sa.Integer(), nullable=False),
        sa.Column("model_id", sa.Integer(), nullable=False),
        sa.Column(
            "value", postgresql.JSONB(astext_type=sa.Text()), nullable=True
        ),
        sa.ForeignKeyConstraint(
            ["attribute_id"],
            ["data_attribute.id"],
            name=op.f("fk_default_value_attribute_id_data_attribute"),
            ondelete="CASCADE",
        ),
        sa.ForeignKeyConstraint(
            ["model_id"],
            ["preference_model.id"],
            name=op.f("fk_default_value_model_id_preference_model"),
            ondelete="CASCADE",
        ),
        sa.PrimaryKeyConstraint(
            "attribute_id", "model_id", name=op.f("pk_default_value")
        ),
    )
    op.create_table(
        "geo_data",
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column("original_name", sa.String(), nullable=True),
        sa.Column("source_driver", sa.String(), nullable=True),
        sa.Column("upload_user_id", sa.Integer(), nullable=True),
        sa.Column("description", sa.String(), nullable=True),
        sa.Column("stream_id", sa.Integer(), nullable=True),
        sa.Column("is_public", sa.Boolean(), nullable=False),
        sa.ForeignKeyConstraint(
            ["id"],
            ["base_geo_data.id"],
            name=op.f("fk_geo_data_id_base_geo_data"),
        ),
        sa.ForeignKeyConstraint(
            ["stream_id"],
            ["stream_geo_data.id"],
            name=op.f("fk_geo_data_stream_id_stream_geo_data"),
        ),
        sa.ForeignKeyConstraint(
            ["upload_user_id"],
            ["user.id"],
            name=op.f("fk_geo_data_upload_user_id_user"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_geo_data")),
    )
    op.create_table(
        "mrsort_criterion",
        sa.Column(
            "profiles",
            postgresql.ARRAY(sa.Float(), dimensions=1),
            nullable=True,
        ),
        sa.Column("weight", sa.Float(), nullable=True),
        sa.Column("maximize", sa.Boolean(), nullable=False),
        sa.Column("mrsort_id", sa.Integer(), nullable=False),
        sa.Column("attribute_id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["attribute_id"],
            ["data_attribute.id"],
            name=op.f("fk_mrsort_criterion_attribute_id_data_attribute"),
            ondelete="CASCADE",
        ),
        sa.ForeignKeyConstraint(
            ["mrsort_id"],
            ["preference_model.id"],
            name=op.f("fk_mrsort_criterion_mrsort_id_preference_model"),
            ondelete="CASCADE",
        ),
        sa.PrimaryKeyConstraint(
            "mrsort_id", "attribute_id", name=op.f("pk_mrsort_criterion")
        ),
    )
    op.create_table(
        "mrsort_inference_alternative",
        sa.Column("category_id", sa.Integer(), nullable=True),
        sa.Column("mrsort_id", sa.Integer(), nullable=False),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["category_id"],
            ["discrete_category.id"],
            name=op.f(
                "fk_mrsort_inference_alternative_category_id_discrete_category"
            ),
            ondelete="SET NULL",
        ),
        sa.ForeignKeyConstraint(
            ["mrsort_id"],
            ["preference_model.id"],
            name=op.f(
                "fk_mrsort_inference_alternative_mrsort_id_preference_model"
            ),
            ondelete="CASCADE",
        ),
        sa.PrimaryKeyConstraint(
            "id", name=op.f("pk_mrsort_inference_alternative")
        ),
    )
    op.create_table(
        "project_permission",
        sa.Column("object_id", sa.Integer(), nullable=True),
        sa.Column("user_id", sa.Integer(), nullable=False),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["object_id"],
            ["project.id"],
            name=op.f("fk_project_permission_object_id_project"),
        ),
        sa.ForeignKeyConstraint(
            ["user_id"],
            ["user.id"],
            name=op.f("fk_project_permission_user_id_user"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_project_permission")),
    )
    op.create_table(
        "project_root",
        sa.Column("project_id", sa.Integer(), nullable=False),
        sa.Column("project_root_data_id", sa.Integer(), nullable=True),
        sa.ForeignKeyConstraint(
            ["project_id"],
            ["project.id"],
            name=op.f("fk_project_root_project_id_project"),
        ),
        sa.ForeignKeyConstraint(
            ["project_root_data_id"],
            ["project_data.id"],
            name=op.f("fk_project_root_project_root_data_id_project_data"),
        ),
        sa.PrimaryKeyConstraint("project_id", name=op.f("pk_project_root")),
    )
    op.create_table(
        "project_shared_data",
        sa.Column("project_id", sa.Integer(), nullable=False),
        sa.Column("project_data_id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["project_data_id"],
            ["project_data.id"],
            name=op.f("fk_project_shared_data_project_data_id_project_data"),
        ),
        sa.ForeignKeyConstraint(
            ["project_id"],
            ["project.id"],
            name=op.f("fk_project_shared_data_project_id_project"),
        ),
        sa.PrimaryKeyConstraint(
            "project_id",
            "project_data_id",
            name=op.f("pk_project_shared_data"),
        ),
    )
    op.create_table(
        "project_task",
        sa.Column("task_id", sa.String(), nullable=True),
        sa.Column("started_at", sa.DateTime(), nullable=True),
        sa.Column("finished_at", sa.DateTime(), nullable=True),
        sa.Column("state", sa.String(), nullable=True),
        sa.Column("error_message", sa.String(), nullable=True),
        sa.Column(
            "type",
            sa.Enum(
                "process_project_data",
                "process_zone_proposition",
                name="projecttasktype",
            ),
            nullable=True,
        ),
        sa.Column(
            "params", postgresql.JSONB(astext_type=sa.Text()), nullable=True
        ),
        sa.Column("project_id", sa.Integer(), nullable=False),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["project_id"],
            ["project.id"],
            name=op.f("fk_project_task_project_id_project"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_project_task")),
    )
    op.create_table(
        "stream_geo_data_permission",
        sa.Column("object_id", sa.Integer(), nullable=True),
        sa.Column("user_id", sa.Integer(), nullable=False),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["object_id"],
            ["stream_geo_data.id"],
            name=op.f(
                "fk_stream_geo_data_permission_object_id_stream_geo_data"
            ),
        ),
        sa.ForeignKeyConstraint(
            ["user_id"],
            ["user.id"],
            name=op.f("fk_stream_geo_data_permission_user_id_user"),
        ),
        sa.PrimaryKeyConstraint(
            "id", name=op.f("pk_stream_geo_data_permission")
        ),
    )
    op.create_table(
        "weighted_sum_operand",
        sa.Column("attribute_id", sa.Integer(), nullable=True),
        sa.Column("weight", sa.Float(), nullable=True),
        sa.Column("model_id", sa.Integer(), nullable=False),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["attribute_id"],
            ["data_attribute.id"],
            name=op.f("fk_weighted_sum_operand_attribute_id_data_attribute"),
            ondelete="SET NULL",
        ),
        sa.ForeignKeyConstraint(
            ["model_id"],
            ["preference_model.id"],
            name=op.f("fk_weighted_sum_operand_model_id_preference_model"),
            ondelete="CASCADE",
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_weighted_sum_operand")),
    )
    op.create_table(
        "wfs_geo_data",
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column("feature_type", sa.String(), nullable=False),
        sa.Column("version", sa.String(), nullable=True),
        sa.ForeignKeyConstraint(
            ["id"],
            ["stream_geo_data.id"],
            name=op.f("fk_wfs_geo_data_id_stream_geo_data"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_wfs_geo_data")),
    )
    op.create_table(
        "wms_geo_data",
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column(
            "classes",
            postgresql.ARRAY(sa.Float(), dimensions=2),
            nullable=True,
        ),
        sa.Column("layer", sa.String(), nullable=False),
        sa.Column("resolution", sa.Float(), nullable=True),
        sa.Column("version", sa.String(), nullable=True),
        sa.ForeignKeyConstraint(
            ["id"],
            ["stream_geo_data.id"],
            name=op.f("fk_wms_geo_data_id_stream_geo_data"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_wms_geo_data")),
    )
    op.create_table(
        "zone_proposition",
        sa.Column("data_id", sa.Integer(), nullable=True),
        sa.Column("project_id", sa.Integer(), nullable=True),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["data_id"],
            ["base_geo_data.id"],
            name=op.f("fk_zone_proposition_data_id_base_geo_data"),
        ),
        sa.ForeignKeyConstraint(
            ["project_id"],
            ["project.id"],
            name=op.f("fk_zone_proposition_project_id_project"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_zone_proposition")),
    )
    op.create_table(
        "geo_data_permission",
        sa.Column("object_id", sa.Integer(), nullable=True),
        sa.Column("user_id", sa.Integer(), nullable=False),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.ForeignKeyConstraint(
            ["object_id"],
            ["geo_data.id"],
            name=op.f("fk_geo_data_permission_object_id_geo_data"),
        ),
        sa.ForeignKeyConstraint(
            ["user_id"],
            ["user.id"],
            name=op.f("fk_geo_data_permission_user_id_user"),
        ),
        sa.PrimaryKeyConstraint("id", name=op.f("pk_geo_data_permission")),
    )
    op.create_table(
        "mrsort_inference_value",
        sa.Column("mrsort_id", sa.Integer(), nullable=False),
        sa.Column("attribute_id", sa.Integer(), nullable=False),
        sa.Column("alternative_id", sa.Integer(), nullable=False),
        sa.Column(
            "value", postgresql.JSONB(astext_type=sa.Text()), nullable=True
        ),
        sa.ForeignKeyConstraint(
            ["alternative_id"],
            ["mrsort_inference_alternative.id"],
            name=op.f(
                "fk_mrsort_inference_value_alternative_id_mrsort_inference_"
                "alternative"
            ),
            ondelete="CASCADE",
        ),
        sa.ForeignKeyConstraint(
            ["mrsort_id", "attribute_id"],
            ["mrsort_criterion.mrsort_id", "mrsort_criterion.attribute_id"],
            name=op.f("fk_mrsort_inference_value_mrsort_id_mrsort_criterion"),
            ondelete="CASCADE",
        ),
        sa.PrimaryKeyConstraint(
            "mrsort_id",
            "attribute_id",
            "alternative_id",
            name=op.f("pk_mrsort_inference_value"),
        ),
    )
    # ### end Alembic commands ###
    op.execute(
        sa.DDL(
            """
/**
 * Dissolve adjacent geometries if they have the same attributes.
 *
 * Warning: the function deletes the old features before inserting the new ones.
 */
CREATE OR REPLACE FUNCTION dissolve_adjacent (data_id int) RETURNS void AS $$
BEGIN
    -- Use a temporary table to store the attributes
    CREATE TEMP TABLE temp_dissolve AS
    WITH merge AS (
        SELECT f.geom AS geom, json_object_agg(v.attribute_id, v.value) AS props
        FROM feature f INNER JOIN data_value v ON f.id = v.feature_id
        WHERE f.geo_data_id = data_id
        GROUP BY f.id
    )
    -- Dissolve the adjacent geometries based on their attributes
    SELECT ST_SetSRID(ST_UnaryUnion(d.geom), 4326) AS geom, d.props AS props
    FROM (
        SELECT UNNEST(ST_ClusterIntersecting(m.geom)) AS geom, m.props::jsonb AS props
        FROM merge m
        GROUP BY m.props::jsonb
    ) AS d;

    -- Delete old features
    DELETE FROM feature WHERE geo_data_id = data_id;

    -- Insert the new dissolved features
    WITH ins AS (
        INSERT INTO feature (geom, geo_data_id)
        SELECT geom, data_id
        FROM temp_dissolve
        RETURNING id, geom
    )
    INSERT INTO data_value (attribute_id, value, feature_id, type)
    SELECT d.key, d.value, ins.id, a.type
    FROM ins INNER JOIN (
        SELECT t.geom, j.key::int, j.value
        FROM temp_dissolve t, jsonb_each(t.props) j
    ) AS d ON ins.geom = d.geom
    INNER JOIN data_attribute a ON d.key = a.id;

    -- Delete the temporary table
    DROP TABLE temp_dissolve;
END;
$$ LANGUAGE plpgsql;
        """  # noqa: E501
        )
    )
    op.execute(
        sa.DDL(
            """
/******************************************************************************
Copyright (c), Mapbox
All rights reserved.

Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:

1. Redistributions of source code must retain the above copyright notice, this
   list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright notice, this
   list of conditions and the following disclaimer in the documentation and/or
   other materials provided with the distribution.
3. Neither the name of the copyright holder nor the names of its contributors may
   be used to endorse or promote products derived from this software without
   specific prior written permission.


THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


https://github.com/mapbox/postgis-vt-util


### TileBBox ###
Given a Web Mercator tile ID as (z, x, y), returns a bounding-box
geometry of the area covered by that tile.
__Parameters:__
- `integer` z - A tile zoom level.
- `integer` x - A tile x-position.
- `integer` y - A tile y-position.
- `integer` srid - SRID of the desired target projection of the bounding
  box. Defaults to 3857 (Web Mercator).
__Returns:__ `geometry(polygon)`
******************************************************************************/
create or replace function TileBBox (z int, x int, y int, srid int = 3857)
    returns geometry
    language plpgsql immutable as
$func$
declare
    max numeric := 20037508.34;
    res numeric := (max*2)/(2^z);
    bbox geometry;
begin
    bbox := ST_MakeEnvelope(
        -max + (x * res),
        max - (y * res),
        -max + (x * res) + res,
        max - (y * res) - res,
        3857
    );
    if srid = 3857 then
        return bbox;
    else
        return ST_Transform(bbox, srid);
    end if;
end;
$func$;
        """  # noqa: E501
        )
    )
    op.execute(
        sa.DDL(
            """
/**
 * Trigger to correct the invalid geometries before an INSERT or an UPDATE.
 */
CREATE OR REPLACE FUNCTION feature_make_valid() RETURNS trigger AS $feature_make_valid$
BEGIN
    IF ST_IsValid(NEW.geom) = 'f' THEN
        NEW.geom := ST_MakeValid(NEW.geom);
    END IF;
    RETURN NEW;
END;
$feature_make_valid$ LANGUAGE plpgsql;

CREATE TRIGGER feature_make_valid BEFORE INSERT OR UPDATE ON feature
    FOR EACH ROW EXECUTE PROCEDURE feature_make_valid();
        """  # noqa: E501
        )
    )


def downgrade():
    op.execute(sa.DDL("DROP TRIGGER feature_make_valid ON feature;"))
    op.execute(sa.DDL("DROP FUNCTION feature_make_valid();"))
    op.execute(sa.DDL("DROP FUNCTION dissolve_adjacent(data_id int);"))
    op.execute(
        sa.DDL("DROP FUNCTION TileBBox(z int, x int, y int, srid int);")
    )

    # ### commands auto generated by Alembic - please adjust! ###
    op.drop_table("mrsort_inference_value")
    op.drop_table("geo_data_permission")
    op.drop_table("zone_proposition")
    op.drop_table("wms_geo_data")
    op.drop_table("wfs_geo_data")
    op.drop_table("weighted_sum_operand")
    op.drop_table("stream_geo_data_permission")
    op.drop_table("project_task")
    op.drop_table("project_shared_data")
    op.drop_table("project_root")
    op.drop_table("project_permission")
    op.drop_table("mrsort_inference_alternative")
    op.drop_table("mrsort_criterion")
    op.drop_table("geo_data")
    op.drop_table("default_value")
    with op.batch_alter_table("data_value", schema=None) as batch_op:
        batch_op.drop_index(batch_op.f("ix_data_value_feature_id"))
        batch_op.drop_index(batch_op.f("ix_data_value_attribute_id"))

    op.drop_table("data_value")
    op.drop_table("user_refresh_token")
    op.drop_table("user_permission")
    op.drop_table("stream_geo_data")
    with op.batch_alter_table("project", schema=None) as batch_op:
        batch_op.drop_geospatial_index(
            "idx_project_extent", postgresql_using="gist", column_name="extent"
        )
    op.drop_constraint(
        op.f("fk_project_data_project_id_project"), "project_data"
    )

    op.drop_geospatial_table("project")
    with op.batch_alter_table("feature", schema=None) as batch_op:
        batch_op.drop_index(batch_op.f("ix_feature_geo_data_id"))
        batch_op.drop_geospatial_index(
            "idx_feature_geom", postgresql_using="gist", column_name="geom"
        )

    op.drop_geospatial_table("feature")
    op.drop_table("discrete_category")
    op.drop_table("data_share")
    op.drop_table("data_input_association")
    with op.batch_alter_table("data_attribute", schema=None) as batch_op:
        batch_op.drop_index(batch_op.f("ix_data_attribute_geo_data_id"))

    op.drop_table("data_attribute")
    op.drop_table("user")
    op.drop_constraint(
        op.f("fk_preference_model_data_generator_id_project_data"),
        "preference_model",
    )
    op.drop_table("project_data")
    op.drop_table("preference_model")
    with op.batch_alter_table("base_geo_data", schema=None) as batch_op:
        batch_op.drop_geospatial_index(
            "idx_base_geo_data_extent",
            postgresql_using="gist",
            column_name="extent",
        )

    op.drop_geospatial_table("base_geo_data")
    # ### end Alembic commands ###

    sa.Enum(
        "geo_data",
        "generated_geo_data",
        "proposition_geo_data",
        name="geodatatype",
    ).drop(op.get_bind(), checkfirst=False)
    sa.Enum(
        "categories_rule",
        "continuous_rule",
        "geo_buffer",
        "mrsort",
        "weighted_sum",
        name="preftype",
    ).drop(op.get_bind(), checkfirst=False)
    sa.Enum("all", "min", "max", "sum", "average", name="keepoverlap").drop(
        op.get_bind(), checkfirst=False
    )
    sa.Enum("geo_data", "generator", name="datatype").drop(
        op.get_bind(), checkfirst=False
    )
    sa.Enum("quantitative", "nominal", "ordinal", name="attributetype").drop(
        op.get_bind(), checkfirst=False
    )
    sa.Enum("wfs", "wms", name="streamdatatype").drop(
        op.get_bind(), checkfirst=False
    )
    sa.Enum(
        "create_project", "create_geo_data", name="permissionability"
    ).drop(op.get_bind(), checkfirst=False)
    sa.Enum(
        "process_project_data",
        "process_zone_proposition",
        name="projecttasktype",
    ).drop(op.get_bind(), checkfirst=False)
