"""Add dimension tables (dim_data_source, dim_channel_function, dim_device, dim_date) and migration tracking to RawMeasurement

Revision ID: bfaa66d237cf
Revises: e0d5bb6c3974
Create Date: 2026-01-10 00:09:42.459855

"""

from typing import Sequence, Union

import sqlalchemy as sa
from sqlalchemy.dialects import postgresql

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "bfaa66d237cf"
down_revision: Union[str, None] = "e0d5bb6c3974"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    op.create_table(
        "dim_data_source",
        sa.Column("id", sa.SmallInteger(), nullable=False),
        sa.Column("name", sa.Text(), nullable=False),
        sa.Column("description", sa.Text(), nullable=True),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index(
        op.f("ix_dim_data_source_name"), "dim_data_source", ["name"], unique=True
    )
    op.create_table(
        "dim_date",
        sa.Column("date_id", sa.Integer(), nullable=False),
        sa.Column("date", sa.Date(), nullable=False),
        sa.Column("year", sa.SmallInteger(), nullable=False),
        sa.Column("quarter", sa.SmallInteger(), nullable=False),
        sa.Column("month", sa.SmallInteger(), nullable=False),
        sa.Column("week", sa.SmallInteger(), nullable=False),
        sa.Column("day_of_month", sa.SmallInteger(), nullable=False),
        sa.Column("day_of_week", sa.SmallInteger(), nullable=False),
        sa.Column("is_weekend", sa.Boolean(), nullable=False),
        sa.Column("is_holiday", sa.Boolean(), nullable=False),
        sa.PrimaryKeyConstraint("date_id"),
    )
    op.create_index(op.f("ix_dim_date_date"), "dim_date", ["date"], unique=True)
    op.create_index(
        op.f("ix_dim_date_is_holiday"), "dim_date", ["is_holiday"], unique=False
    )
    op.create_index(
        op.f("ix_dim_date_is_weekend"), "dim_date", ["is_weekend"], unique=False
    )
    op.create_index(op.f("ix_dim_date_month"), "dim_date", ["month"], unique=False)
    op.create_index(op.f("ix_dim_date_year"), "dim_date", ["year"], unique=False)
    op.create_table(
        "dim_channel_function",
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column("source_id", sa.SmallInteger(), nullable=True),
        sa.Column("code", sa.Text(), nullable=False),
        sa.Column("name", sa.Text(), nullable=False),
        sa.Column("category", sa.Text(), nullable=False),
        sa.Column("data_type", sa.Text(), nullable=False),
        sa.Column("unit", sa.Text(), nullable=True),
        sa.Column("legacy_type_id", sa.SmallInteger(), nullable=True),
        sa.ForeignKeyConstraint(
            ["source_id"], ["dim_data_source.id"], ondelete="SET NULL"
        ),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index(
        op.f("ix_dim_channel_function_category"),
        "dim_channel_function",
        ["category"],
        unique=False,
    )
    op.create_index(
        op.f("ix_dim_channel_function_code"),
        "dim_channel_function",
        ["code"],
        unique=True,
    )
    op.create_index(
        op.f("ix_dim_channel_function_legacy_type_id"),
        "dim_channel_function",
        ["legacy_type_id"],
        unique=False,
    )
    op.create_index(
        op.f("ix_dim_channel_function_source_id"),
        "dim_channel_function",
        ["source_id"],
        unique=False,
    )
    op.create_table(
        "dim_device",
        sa.Column("source_id", sa.SmallInteger(), nullable=False),
        sa.Column("source_name", sa.Text(), nullable=True),
        sa.Column("function_id", sa.Integer(), nullable=True),
        sa.Column("function_code", sa.Text(), nullable=True),
        sa.Column("function_category", sa.Text(), nullable=True),
        # Multi-channel support fields
        sa.Column("device_id", sa.Text(), nullable=True),
        sa.Column("device_channel_id", sa.Text(), nullable=True),
        sa.Column("device_name", sa.Text(), nullable=True),
        sa.Column("device_model", sa.Text(), nullable=True),
        sa.Column(
            "source_metadata", postgresql.JSON(astext_type=sa.Text()), nullable=True
        ),
        sa.Column("location_name", sa.Text(), nullable=True),
        sa.Column("location_building", sa.Text(), nullable=True),
        sa.Column("location_room", sa.Text(), nullable=True),
        sa.Column(
            "location_coordinates",
            postgresql.JSON(astext_type=sa.Text()),
            nullable=True,
        ),
        sa.Column("is_active", sa.Boolean(), nullable=False),
        sa.Column("id", sa.Integer(), nullable=False),
        sa.Column("tenant", sa.UUID(), nullable=True),
        sa.Column("external_id", sa.Text(), nullable=True),
        sa.Column("meter_id", sa.Text(), nullable=True),
        sa.Column("created_ts", sa.DateTime(), nullable=True),
        sa.Column("updated_ts", sa.DateTime(), nullable=True),
        sa.Column("parent_device_id", sa.Integer(), nullable=True),
        sa.ForeignKeyConstraint(
            ["function_id"], ["dim_channel_function.id"], ondelete="RESTRICT"
        ),
        sa.ForeignKeyConstraint(
            ["parent_device_id"], ["dim_device.id"], ondelete="CASCADE"
        ),
        sa.ForeignKeyConstraint(
            ["source_id"], ["dim_data_source.id"], ondelete="RESTRICT"
        ),
        sa.PrimaryKeyConstraint("id"),
        sa.UniqueConstraint("tenant", "meter_id", name="uq_device_tenant_meter"),
    )
    op.create_index(
        "idx_device_device_id",
        "dim_device",
        ["device_id"],
        unique=False,
    )
    op.create_index(
        "idx_device_device_channel",
        "dim_device",
        ["device_id", "device_channel_id"],
        unique=False,
    )
    op.create_index(
        "idx_device_tenant_active", "dim_device", ["tenant", "is_active"], unique=False
    )
    op.create_index(
        "idx_device_tenant_source", "dim_device", ["tenant", "source_id"], unique=False
    )
    op.create_index(
        op.f("ix_dim_device_function_category"),
        "dim_device",
        ["function_category"],
        unique=False,
    )
    op.create_index(
        op.f("ix_dim_device_function_code"),
        "dim_device",
        ["function_code"],
        unique=False,
    )
    op.create_index(
        op.f("ix_dim_device_function_id"), "dim_device", ["function_id"], unique=False
    )
    op.create_index(
        op.f("ix_dim_device_is_active"), "dim_device", ["is_active"], unique=False
    )
    op.create_index(
        op.f("ix_dim_device_source_id"), "dim_device", ["source_id"], unique=False
    )
    op.create_index(
        op.f("ix_dim_device_source_name"), "dim_device", ["source_name"], unique=False
    )
    op.create_index(
        op.f("ix_dim_device_device_id"), "dim_device", ["device_id"], unique=False
    )
    op.create_index(
        op.f("ix_dim_device_parent_device_id"),
        "dim_device",
        ["parent_device_id"],
        unique=False,
    )

    # Add migration tracking and processing fields to measurement_raw
    op.add_column(
        "measurement_raw",
        sa.Column(
            "migrated_to_dwh", sa.Boolean(), nullable=False, server_default="false"
        ),
    )
    op.add_column(
        "measurement_raw", sa.Column("migrated_ts", sa.DateTime(), nullable=True)
    )
    op.add_column(
        "measurement_raw", sa.Column("processed_ts", sa.DateTime(), nullable=True)
    )
    op.add_column(
        "measurement_raw", sa.Column("processing_error", sa.Text(), nullable=True)
    )
    op.create_index(
        op.f("ix_measurement_raw_migrated_to_dwh"),
        "measurement_raw",
        ["migrated_to_dwh"],
        unique=False,
    )
    # Make type field nullable for backward compatibility
    op.alter_column(
        "measurement_raw", "type", nullable=True, existing_type=sa.SmallInteger()
    )
    # ### end Alembic commands ###


def downgrade() -> None:
    # ### commands auto generated by Alembic - please adjust! ###
    # Revert type field to not nullable
    op.alter_column(
        "measurement_raw", "type", nullable=False, existing_type=sa.SmallInteger()
    )

    # Remove migration tracking and processing fields from measurement_raw
    op.drop_index(
        op.f("ix_measurement_raw_migrated_to_dwh"), table_name="measurement_raw"
    )
    op.drop_column("measurement_raw", "processing_error")
    op.drop_column("measurement_raw", "processed_ts")
    op.drop_column("measurement_raw", "migrated_ts")
    op.drop_column("measurement_raw", "migrated_to_dwh")

    op.drop_index(op.f("ix_dim_device_parent_device_id"), table_name="dim_device")
    op.drop_index(op.f("ix_dim_device_device_id"), table_name="dim_device")
    op.drop_index(op.f("ix_dim_device_source_name"), table_name="dim_device")
    op.drop_index(op.f("ix_dim_device_source_id"), table_name="dim_device")
    op.drop_index(op.f("ix_dim_device_is_active"), table_name="dim_device")
    op.drop_index(op.f("ix_dim_device_function_id"), table_name="dim_device")
    op.drop_index(op.f("ix_dim_device_function_code"), table_name="dim_device")
    op.drop_index(op.f("ix_dim_device_function_category"), table_name="dim_device")
    op.drop_index("idx_device_tenant_source", table_name="dim_device")
    op.drop_index("idx_device_tenant_active", table_name="dim_device")
    op.drop_index("idx_device_device_channel", table_name="dim_device")
    op.drop_index("idx_device_device_id", table_name="dim_device")
    op.drop_table("dim_device")
    op.drop_index(
        op.f("ix_dim_channel_function_source_id"), table_name="dim_channel_function"
    )
    op.drop_index(
        op.f("ix_dim_channel_function_legacy_type_id"),
        table_name="dim_channel_function",
    )
    op.drop_index(
        op.f("ix_dim_channel_function_code"), table_name="dim_channel_function"
    )
    op.drop_index(
        op.f("ix_dim_channel_function_category"), table_name="dim_channel_function"
    )
    op.drop_table("dim_channel_function")
    op.drop_index(op.f("ix_dim_date_year"), table_name="dim_date")
    op.drop_index(op.f("ix_dim_date_month"), table_name="dim_date")
    op.drop_index(op.f("ix_dim_date_is_weekend"), table_name="dim_date")
    op.drop_index(op.f("ix_dim_date_is_holiday"), table_name="dim_date")
    op.drop_index(op.f("ix_dim_date_date"), table_name="dim_date")
    op.drop_table("dim_date")
    op.drop_index(op.f("ix_dim_data_source_name"), table_name="dim_data_source")
    op.drop_table("dim_data_source")
    # ### end Alembic commands ###
