"""add_order_item_exceptions

Revision ID: d2e3f4a5b6c7
Revises: c1d2e3f4a5b6
Create Date: 2025-12-20

This migration adds the Order Item Exception system:
- Adds needs_product_match column to order_items table
- Creates order_item_exceptions table for tracking unmatched products

The exception system allows marketplace orders to be imported even when
products are not found by GTIN. Items are linked to a placeholder product
and exceptions are tracked for QC resolution.
"""
from collections.abc import Sequence

import sqlalchemy as sa
from sqlalchemy import inspect

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "d2e3f4a5b6c7"
down_revision: str | None = "c1d2e3f4a5b6"
branch_labels: str | Sequence[str] | None = None
depends_on: str | Sequence[str] | None = None


def column_exists(table_name: str, column_name: str) -> bool:
    """Check if a column exists in a table."""
    bind = op.get_bind()
    inspector = inspect(bind)
    columns = [col["name"] for col in inspector.get_columns(table_name)]
    return column_name in columns


def table_exists(table_name: str) -> bool:
    """Check if a table exists in the database."""
    bind = op.get_bind()
    inspector = inspect(bind)
    return table_name in inspector.get_table_names()


def index_exists(index_name: str, table_name: str) -> bool:
    """Check if an index exists on a table."""
    bind = op.get_bind()
    inspector = inspect(bind)
    try:
        indexes = inspector.get_indexes(table_name)
        return any(idx["name"] == index_name for idx in indexes)
    except Exception:
        return False


def upgrade() -> None:
    # =========================================================================
    # Step 1: Add needs_product_match column to order_items
    # =========================================================================
    if not column_exists("order_items", "needs_product_match"):
        op.add_column(
            "order_items",
            sa.Column(
                "needs_product_match",
                sa.Boolean(),
                server_default="0",
                nullable=False
            )
        )

    if not index_exists("ix_order_items_needs_product_match", "order_items"):
        op.create_index(
            "ix_order_items_needs_product_match",
            "order_items",
            ["needs_product_match"]
        )

    # =========================================================================
    # Step 2: Create order_item_exceptions table
    # =========================================================================
    if not table_exists("order_item_exceptions"):
        op.create_table(
            "order_item_exceptions",
            sa.Column("id", sa.Integer(), nullable=False),
            sa.Column("order_item_id", sa.Integer(), nullable=False),
            sa.Column("vendor_id", sa.Integer(), nullable=False),
            sa.Column("original_gtin", sa.String(length=50), nullable=True),
            sa.Column("original_product_name", sa.String(length=500), nullable=True),
            sa.Column("original_sku", sa.String(length=100), nullable=True),
            sa.Column(
                "exception_type",
                sa.String(length=50),
                nullable=False,
                server_default="product_not_found"
            ),
            sa.Column(
                "status",
                sa.String(length=50),
                nullable=False,
                server_default="pending"
            ),
            sa.Column("resolved_product_id", sa.Integer(), nullable=True),
            sa.Column("resolved_at", sa.DateTime(timezone=True), nullable=True),
            sa.Column("resolved_by", sa.Integer(), nullable=True),
            sa.Column("resolution_notes", sa.Text(), nullable=True),
            sa.Column(
                "created_at",
                sa.DateTime(timezone=True),
                server_default=sa.text("(CURRENT_TIMESTAMP)"),
                nullable=False
            ),
            sa.Column(
                "updated_at",
                sa.DateTime(timezone=True),
                server_default=sa.text("(CURRENT_TIMESTAMP)"),
                nullable=False
            ),
            sa.ForeignKeyConstraint(
                ["order_item_id"],
                ["order_items.id"],
                ondelete="CASCADE"
            ),
            sa.ForeignKeyConstraint(["vendor_id"], ["vendors.id"]),
            sa.ForeignKeyConstraint(["resolved_product_id"], ["products.id"]),
            sa.ForeignKeyConstraint(["resolved_by"], ["users.id"]),
            sa.PrimaryKeyConstraint("id")
        )

        # Create indexes
        op.create_index(
            "ix_order_item_exceptions_id",
            "order_item_exceptions",
            ["id"]
        )
        op.create_index(
            "ix_order_item_exceptions_vendor_id",
            "order_item_exceptions",
            ["vendor_id"]
        )
        op.create_index(
            "ix_order_item_exceptions_status",
            "order_item_exceptions",
            ["status"]
        )
        op.create_index(
            "idx_exception_vendor_status",
            "order_item_exceptions",
            ["vendor_id", "status"]
        )
        op.create_index(
            "idx_exception_gtin",
            "order_item_exceptions",
            ["vendor_id", "original_gtin"]
        )

        # Unique constraint on order_item_id (one exception per item)
        op.create_index(
            "uq_order_item_exception",
            "order_item_exceptions",
            ["order_item_id"],
            unique=True
        )


def downgrade() -> None:
    # Drop order_item_exceptions table
    if table_exists("order_item_exceptions"):
        op.drop_index("uq_order_item_exception", table_name="order_item_exceptions")
        op.drop_index("idx_exception_gtin", table_name="order_item_exceptions")
        op.drop_index("idx_exception_vendor_status", table_name="order_item_exceptions")
        op.drop_index("ix_order_item_exceptions_status", table_name="order_item_exceptions")
        op.drop_index("ix_order_item_exceptions_vendor_id", table_name="order_item_exceptions")
        op.drop_index("ix_order_item_exceptions_id", table_name="order_item_exceptions")
        op.drop_table("order_item_exceptions")

    # Remove needs_product_match column from order_items
    if column_exists("order_items", "needs_product_match"):
        if index_exists("ix_order_items_needs_product_match", "order_items"):
            op.drop_index("ix_order_items_needs_product_match", table_name="order_items")
        op.drop_column("order_items", "needs_product_match")
