"""003_business_tables

Revision ID: 003
Revises: 002
Create Date: 2026-06-09
Alembic 003: 业务主体7张表 + ai_call_logs
products / benchmark_notes / banned_words /
generation_tasks / text_candidates / image_candidates /
delivery_packages / ai_call_logs
"""

from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op

revision: str = "003"
down_revision: Union[str, None] = "002"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None

_TASK_STATUS = sa.Enum(
    "pending", "generating", "pending_selection",
    "pending_review", "approved", "rejected", "archived",
    name="taskstatus",
)
_REVIEW_STATUS = sa.Enum("pending", "approved", "rejected", name="reviewstatus")
_CANDIDATE_SOURCE = sa.Enum("ai", "import", name="candidatesource")
_BANNED_LEVEL = sa.Enum("auto_fix", "soft_warn", "hard_block", name="bannedwordlevel")
_BANNED_STATUS = sa.Enum("pass", "auto_fixed", "soft_warn", "hard_block", name="bannedwordstatus")
_IMAGE_ROLE = sa.Enum("hook", "pain", "proof", "quality", "credit", "convert", "main", name="imagerole")
_PKG_STATUS = sa.Enum("pending", "ready", "downloaded", name="packagestatus")
_PRODUCT_SOURCE = sa.Enum("preset", "custom", name="productsource")


def upgrade() -> None:
    op.create_table(
        "products",
        sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
        sa.Column("workspace_id", sa.BigInteger(), nullable=False),
        sa.Column("name", sa.String(128), nullable=False),
        sa.Column("category", sa.String(64), nullable=True),
        sa.Column("source", _PRODUCT_SOURCE, nullable=False, server_default="custom"),
        sa.Column("selling_points", sa.Text(), nullable=True),
        sa.Column("style_tone", sa.String(128), nullable=True),
        sa.Column("text_angles", sa.Text(), nullable=True),
        sa.Column("custom_prompt", sa.Text(), nullable=True),
        sa.Column("is_active", sa.Boolean(), nullable=False, server_default="1"),
        sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False),
        sa.Column("updated_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False),
        sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index("idx_products_workspace_id", "products", ["workspace_id"])

    op.create_table(
        "benchmark_notes",
        sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
        sa.Column("workspace_id", sa.BigInteger(), nullable=False),
        sa.Column("product_id", sa.BigInteger(), nullable=False),
        sa.Column("screenshot_url", sa.String(512), nullable=True),
        sa.Column("highlights", sa.Text(), nullable=True),
        sa.Column("link_url", sa.String(512), nullable=True),
        sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False),
        sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], ondelete="CASCADE"),
        sa.ForeignKeyConstraint(["product_id"], ["products.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index("idx_benchmark_notes_product_id", "benchmark_notes", ["product_id"])

    op.create_table(
        "banned_words",
        sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
        sa.Column("workspace_id", sa.BigInteger(), nullable=False),
        sa.Column("word", sa.String(64), nullable=False),
        sa.Column("level", _BANNED_LEVEL, nullable=False),
        sa.Column("replacement", sa.String(128), nullable=True),
        sa.Column("updatable", sa.Boolean(), nullable=False, server_default="1"),
        sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False),
        sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index("idx_banned_words_workspace_id", "banned_words", ["workspace_id"])

    op.create_table(
        "generation_tasks",
        sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
        sa.Column("workspace_id", sa.BigInteger(), nullable=False),
        sa.Column("product_id", sa.BigInteger(), nullable=False),
        sa.Column("operator_id", sa.BigInteger(), nullable=False),
        sa.Column("theme", sa.String(256), nullable=True),
        sa.Column("text_count", sa.Integer(), nullable=False, server_default="5"),
        sa.Column("image_count", sa.Integer(), nullable=False, server_default="3"),
        sa.Column("track", sa.String(16), nullable=False, server_default="ai"),
        sa.Column("status", _TASK_STATUS, nullable=False, server_default="pending"),
        sa.Column("mongo_trace_id", sa.String(24), nullable=True),
        sa.Column("review_status", _REVIEW_STATUS, nullable=True),
        sa.Column("reviewer_id", sa.BigInteger(), nullable=True),
        sa.Column("reviewed_at", sa.DateTime(), nullable=True),
        sa.Column("reject_reason", sa.Text(), nullable=True),
        sa.Column("approved_at", sa.DateTime(), nullable=True),
        sa.Column("archived_at", sa.DateTime(), nullable=True),
        sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False),
        sa.Column("updated_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False),
        sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], ondelete="CASCADE"),
        sa.ForeignKeyConstraint(["product_id"], ["products.id"]),
        sa.ForeignKeyConstraint(["operator_id"], ["users.id"]),
        sa.ForeignKeyConstraint(["reviewer_id"], ["users.id"]),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index("idx_generation_tasks_workspace_status", "generation_tasks", ["workspace_id", "status"])

    op.create_table(
        "text_candidates",
        sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
        sa.Column("workspace_id", sa.BigInteger(), nullable=False),
        sa.Column("task_id", sa.BigInteger(), nullable=False),
        sa.Column("source", _CANDIDATE_SOURCE, nullable=False, server_default="ai"),
        sa.Column("angle_label", sa.String(64), nullable=True),
        sa.Column("content", sa.Text(), nullable=True),
        sa.Column("score_json", sa.Text(), nullable=True),
        sa.Column("banned_word_status", _BANNED_STATUS, nullable=False, server_default="pass"),
        sa.Column("eval_score", sa.Float(), nullable=True),
        sa.Column("is_selected", sa.Boolean(), nullable=False, server_default="0"),
        sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False),
        sa.ForeignKeyConstraint(["task_id"], ["generation_tasks.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index("idx_text_candidates_task_id", "text_candidates", ["task_id"])

    op.create_table(
        "image_candidates",
        sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
        sa.Column("workspace_id", sa.BigInteger(), nullable=False),
        sa.Column("task_id", sa.BigInteger(), nullable=False),
        sa.Column("role", _IMAGE_ROLE, nullable=False, server_default="main"),
        sa.Column("url", sa.String(512), nullable=True),
        sa.Column("strategy", sa.String(4), nullable=True),
        sa.Column("seq", sa.Integer(), nullable=False, server_default="1"),
        sa.Column("is_selected", sa.Boolean(), nullable=False, server_default="0"),
        sa.Column("eval_score", sa.Float(), nullable=True),
        sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False),
        sa.ForeignKeyConstraint(["task_id"], ["generation_tasks.id"], ondelete="CASCADE"),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index("idx_image_candidates_task_id", "image_candidates", ["task_id"])

    op.create_table(
        "delivery_packages",
        sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
        sa.Column("workspace_id", sa.BigInteger(), nullable=False),
        sa.Column("task_id", sa.BigInteger(), nullable=False),
        sa.Column("status", _PKG_STATUS, nullable=False, server_default="pending"),
        sa.Column("package_path", sa.String(512), nullable=True),
        sa.Column("download_url", sa.String(512), nullable=True),
        sa.Column("expires_at", sa.DateTime(), nullable=True),
        sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False),
        sa.ForeignKeyConstraint(["workspace_id"], ["workspaces.id"], ondelete="CASCADE"),
        sa.ForeignKeyConstraint(["task_id"], ["generation_tasks.id"]),
        sa.PrimaryKeyConstraint("id"),
    )

    op.create_table(
        "ai_call_logs",
        sa.Column("id", sa.BigInteger(), autoincrement=True, nullable=False),
        sa.Column("workspace_id", sa.BigInteger(), nullable=False),
        sa.Column("user_id", sa.BigInteger(), nullable=False),
        sa.Column("key_id", sa.BigInteger(), nullable=True),
        sa.Column("task_id", sa.BigInteger(), nullable=True),
        sa.Column("provider", sa.String(32), nullable=True),
        sa.Column("model", sa.String(64), nullable=True),
        sa.Column("call_type", sa.String(32), nullable=True),
        sa.Column("prompt_tokens", sa.Integer(), nullable=True),
        sa.Column("completion_tokens", sa.Integer(), nullable=True),
        sa.Column("success", sa.Boolean(), nullable=False, server_default="1"),
        sa.Column("error_code", sa.String(32), nullable=True),
        sa.Column("latency_ms", sa.Integer(), nullable=True),
        sa.Column("created_at", sa.DateTime(), server_default=sa.text("now()"), nullable=False),
        sa.ForeignKeyConstraint(["user_id"], ["users.id"]),
        sa.ForeignKeyConstraint(["key_id"], ["user_api_keys.id"]),
        sa.ForeignKeyConstraint(["task_id"], ["generation_tasks.id"]),
        sa.PrimaryKeyConstraint("id"),
    )
    op.create_index("idx_ai_call_logs_workspace_user", "ai_call_logs", ["workspace_id", "user_id"])
    op.create_index("idx_ai_call_logs_task_id", "ai_call_logs", ["task_id"])


def downgrade() -> None:
    op.drop_index("idx_ai_call_logs_task_id", "ai_call_logs")
    op.drop_index("idx_ai_call_logs_workspace_user", "ai_call_logs")
    op.drop_table("ai_call_logs")
    op.drop_table("delivery_packages")
    op.drop_index("idx_image_candidates_task_id", "image_candidates")
    op.drop_table("image_candidates")
    op.drop_index("idx_text_candidates_task_id", "text_candidates")
    op.drop_table("text_candidates")
    op.drop_index("idx_generation_tasks_workspace_status", "generation_tasks")
    op.drop_table("generation_tasks")
    op.drop_index("idx_banned_words_workspace_id", "banned_words")
    op.drop_table("banned_words")
    op.drop_index("idx_benchmark_notes_product_id", "benchmark_notes")
    op.drop_table("benchmark_notes")
    op.drop_index("idx_products_workspace_id", "products")
    op.drop_table("products")
    # MySQL 不支持 DROP TYPE，枚举存 VARCHAR 无需清理
