from collections.abc import Sequence
from pathlib import Path

import sqlalchemy as sa
from alembic import op


def get_revision() -> str:
    return Path(__file__).name.split("-")[0]


def get_down_revision() -> str | None:
    versions_path = Path(__file__).parent
    migration_files = sorted([f.name for f in versions_path.iterdir() if f.is_file()])
    for i, file in enumerate(migration_files):
        if file == Path(__file__).name:
            return None if i == 0 else migration_files[i - 1].split("-")[0]
    return None


# revision identifiers, used by Alembic.
revision: str = get_revision()
down_revision: str | None = get_down_revision()
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None

DEFINED_DECIMAL = sa.DECIMAL(precision=36, scale=12)


def upgrade() -> None:
    connection = op.get_bind()
    inspector = sa.inspect(connection)
    if not inspector.has_table("tenants"):
        op.create_table(
            "tenants",
            sa.Column("id", sa.UUID, primary_key=True, nullable=False, server_default=sa.text("gen_random_uuid()")),
            sa.Column("application_client_id", sa.String(50), nullable=False, unique=True, index=False),
            sa.Column("legal_name", sa.String(255), nullable=False, unique=False, index=True),
            sa.Column("whi_customer_id", sa.ARRAY(sa.Text), nullable=False, unique=True, index=False),
            sa.Column("cydas_customer_id", sa.ARRAY(sa.Text), nullable=True, unique=True, index=False),
        )

    quota_allocation_status = sa.Enum(
        "active",
        "exceeded",
        "scheduled",
        "expired",
        name="quota_allocation_status",
    )
    quota_allocation_type = sa.Enum(
        "base",
        "top_up",
        name="quota_allocation_type",
    )

    if not inspector.has_table("quota_allocations"):
        op.create_table(
            "quota_allocations",
            sa.Column("id", sa.UUID, primary_key=True, nullable=False, server_default=sa.text("gen_random_uuid()")),
            sa.Column("tenant_id", sa.UUID, nullable=False),
            sa.Column("contract_id", sa.String(100), nullable=False, unique=False, index=False),
            sa.Column(
                "valid_from_date",
                sa.DateTime,
                nullable=False,
                index=False,
            ),
            sa.Column("valid_to_date", sa.DateTime, nullable=False, index=False),
            sa.Column("amount", DEFINED_DECIMAL, nullable=False, index=False),
            sa.Column("used", DEFINED_DECIMAL, nullable=False, index=False),
            sa.Column("status", quota_allocation_status, nullable=False, index=False),
            sa.Column("type", quota_allocation_type, nullable=False, index=False),
            sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"]),
        )

    if not inspector.has_table("clients"):
        op.create_table(
            "clients",
            sa.Column("id", sa.UUID, primary_key=True, nullable=False, server_default=sa.text("gen_random_uuid()")),
            sa.Column("name", sa.String(128), nullable=False),
            sa.Column("stage", sa.String(64), nullable=False),
            sa.Column("parent_service", sa.String(64), nullable=True, index=False),
        )

    if not inspector.has_table("client_api_keys"):
        op.create_table(
            "client_api_keys",
            sa.Column(
                "id",
                sa.String(64),
                primary_key=True,
                nullable=False,
                server_default=sa.text("gen_random_uuid()"),
            ),
            sa.Column("client_id", sa.UUID, nullable=False),
            sa.ForeignKeyConstraint(["client_id"], ["clients.id"]),
            sa.Column("hashed_secret", sa.String(128), nullable=False),
        )

    if not inspector.has_table("client_tenants"):
        op.create_table(
            "client_tenants",
            sa.Column("id", sa.UUID, primary_key=True, nullable=False, server_default=sa.text("gen_random_uuid()")),
            sa.Column("tenant_id", sa.UUID, nullable=False),
            sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"]),
            sa.Column("name", sa.String(128), nullable=False),
            sa.UniqueConstraint("name", "parent_service", name="uq_client_tenants_name_parent_service"),
            sa.Column("parent_service", sa.String(64), nullable=True, index=False),
        )

    if not inspector.has_table("usage_events"):
        op.create_table(
            "usage_events",
            sa.Column("id", sa.UUID, primary_key=True, nullable=False, server_default=sa.text("gen_random_uuid()")),
            sa.Column("tenant_id", sa.UUID, nullable=False),
            sa.ForeignKeyConstraint(["tenant_id"], ["tenants.id"]),
            sa.Column("client_tenant_id", sa.UUID, nullable=False),
            sa.ForeignKeyConstraint(["client_tenant_id"], ["client_tenants.id"]),
            sa.Column("client_id", sa.UUID, nullable=False),
            sa.ForeignKeyConstraint(["client_id"], ["clients.id"]),
            sa.Column("code", sa.String(128), nullable=False),
            sa.Column("quantity", DEFINED_DECIMAL, nullable=False),
            sa.Column("unit", DEFINED_DECIMAL, nullable=False),
            sa.Column("service", sa.String(64), nullable=False),
            sa.Column("feature", sa.String(64), nullable=False),
            sa.Column("user_id", sa.String(64), nullable=True),
            sa.Column("trace_id", sa.String(64), nullable=True),
            sa.Column(
                "timestamp",
                sa.DateTime,
                nullable=False,
                server_default=sa.text("timezone('Asia/Tokyo', now())"),
            ),
        )
    if not inspector.has_table("quota_usages"):
        op.create_table(
            "quota_usages",
            sa.Column("id", sa.UUID, primary_key=True, nullable=False, server_default=sa.text("gen_random_uuid()")),
            sa.Column("tenant_id", sa.UUID, nullable=False),
            sa.Column("usage_event_id", sa.UUID, nullable=False),
            sa.ForeignKeyConstraint(["usage_event_id"], ["usage_events.id"]),
            sa.Column("quota_allocation_id", sa.UUID, nullable=True),
            sa.ForeignKeyConstraint(["quota_allocation_id"], ["quota_allocations.id"]),
            sa.Column("unit", DEFINED_DECIMAL, nullable=False),
        )

    if not inspector.has_table("usage_event_requests"):
        op.create_table(
            "usage_event_requests",
            sa.Column("client_id", sa.UUID, nullable=False),
            sa.Column("idempotency_key", sa.UUID, nullable=False),
            sa.PrimaryKeyConstraint("client_id", "idempotency_key"),
            sa.ForeignKeyConstraint(
                ["client_id"],
                ["clients.id"],
                name="fk_usage_event_requests_client_id_clients",
            ),
            sa.Column(
                "timestamp",
                sa.TIMESTAMP,
                nullable=False,
                server_default=sa.text("timezone('Asia/Tokyo', now())"),
            ),
        )
        op.create_index(
            "idx_usage_event_requests_client_id_idempotency_key",
            "usage_event_requests",
            ["client_id", "idempotency_key"],
        )

    if not inspector.has_table("prices"):
        op.create_table(
            "prices",
            sa.Column(
                "code",
                sa.String(128),
                primary_key=True,
                nullable=False,
            ),
            sa.Column("price", DEFINED_DECIMAL, nullable=False, index=False),
            sa.Column(
                "updated_at",
                sa.DateTime,
                nullable=False,
                server_default=sa.text("timezone('Asia/Tokyo', now())"),
            ),
        )


def downgrade() -> None:
    connection = op.get_bind()
    inspector = sa.inspect(connection)
    if inspector.has_table("prices"):
        op.drop_table("prices")
    if inspector.has_table("usage_event_requests"):
        op.drop_table("usage_event_requests")
    if inspector.has_table("quota_usages"):
        op.drop_table("quota_usages")
    if inspector.has_table("usage_events"):
        op.drop_table("usage_events")
    if inspector.has_table("client_tenants"):
        op.drop_table("client_tenants")
    if inspector.has_table("client_api_keys"):
        op.drop_table("client_api_keys")
    if inspector.has_table("clients"):
        op.drop_table("clients")
    if inspector.has_table("quota_allocations"):
        op.drop_table("quota_allocations")
    if inspector.has_table("tenants"):
        op.drop_table("tenants")

    op.execute("DROP TYPE quota_allocation_status CASCADE")
    op.execute("DROP TYPE quota_allocation_type CASCADE")
