"""PostgreSQL migration: add account_type to accounts and channel_accounts.

- Adds ``account_type`` column to ``telegram_accounts`` (enum-like TEXT).
- Adds ``account_type`` column to ``channel_accounts`` junction table.
- Replaces the old ``UNIQUE(channel_id, account_id)`` constraint with:
  - ``UNIQUE(channel_id, account_type)`` — one account per type per channel.
  - ``UNIQUE(channel_id, account_id)`` — no duplicate assignment of same account.
- Sets ``account_type='manager'`` for existing platform-manager accounts.
- Adds index on ``telegram_accounts.account_type`` for lease queries.

Because the builder does not support raw DDL (CHECK constraints, UPDATE
statements), this module defines a lightweight :class:`RawSQLOp` for those
operations alongside the standard :class:`MigrationBuilder`-based migrations.
"""

from __future__ import annotations

from typing import Any

from repo_core.migrations.builder import MigrationBuilder, Op
from repo_core.migrations.runner import Migration

VERSION = "0003"


# ── Raw SQL operation (for CHECK constraints, UPDATE, etc.) ──────────────

class RawSQLOp(Op):
    """A migration operation backed by literal SQL strings.

    Used when the :class:`MigrationBuilder` cannot express the desired DDL
    (e.g. ``ADD CONSTRAINT … CHECK``, ``UPDATE`` statements).

    Each instance holds exactly one set of SQL statements — use separate
    instances for the ``up`` and ``down`` sides of a :class:`Migration`.
    """

    def __init__(self, stmts: list[str]) -> None:
        self._stmts = stmts

    def sql(self) -> list[str]:
        return self._stmts


# ── Migrations ────────────────────────────────────────────────────────────

def get_postgres_migrations() -> list[Migration]:
    """Return account_type-related migrations."""
    return [

        # ── 1. Add account_type to telegram_accounts ──────────────────
        Migration(
            version="tg_account_type_add_col_0001",
            description="Add account_type column to telegram_accounts",
            up=(
                MigrationBuilder("telegram_accounts")
                .add_column("account_type", "TEXT", nullable=False, default="'crawler'")
                .alter_table()
            ),
            down=(
                MigrationBuilder("telegram_accounts")
                .drop_column("account_type")
                .alter_table()
            ),
        ),

        # ── 2. CHECK constraint on account_type ───────────────────────
        Migration(
            version="tg_account_type_check_0001",
            description="Add CHECK constraint on telegram_accounts.account_type",
            up=RawSQLOp(
                stmts=[
                    """ALTER TABLE telegram_accounts
                       ADD CONSTRAINT chk_account_type
                       CHECK (account_type IN ('crawler', 'downloader', 'manager'))""",
                ],
            ),
            down=RawSQLOp(
                stmts=[
                    "ALTER TABLE telegram_accounts DROP CONSTRAINT IF EXISTS chk_account_type",
                ],
            ),
        ),

        # ── 3. Add account_type to channel_accounts ──────────────────
        Migration(
            version="tg_channel_account_type_col_0001",
            description="Add account_type column to channel_accounts junction table",
            up=(
                MigrationBuilder("channel_accounts")
                .add_column("account_type", "TEXT", nullable=False, default="'crawler'")
                .alter_table()
            ),
            down=(
                MigrationBuilder("channel_accounts")
                .drop_column("account_type")
                .alter_table()
            ),
        ),

        # ── 4. Replace unique constraints on channel_accounts ────────
        Migration(
            version="tg_channel_account_unique_0001",
            description="Replace UNIQUE(channel_id,account_id) with "
                        "UNIQUE(channel_id,account_type) + UNIQUE(channel_id,account_id)",
            up=RawSQLOp(
                stmts=[
                    "DROP INDEX IF EXISTS idx_channel_accounts_channel_id_account_id",
                    "CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_accounts_channel_type "
                    "ON channel_accounts (channel_id, account_type)",
                    "CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_accounts_channel_account "
                    "ON channel_accounts (channel_id, account_id)",
                ],
            ),
            down=RawSQLOp(
                stmts=[
                    "DROP INDEX IF EXISTS idx_channel_accounts_channel_type",
                    "DROP INDEX IF EXISTS idx_channel_accounts_channel_account",
                    "CREATE UNIQUE INDEX IF NOT EXISTS idx_channel_accounts_channel_id_account_id "
                    "ON channel_accounts (channel_id, account_id)",
                ],
            ),
        ),

        # ── 5. Set manager accounts ──────────────────────────────────
        Migration(
            version="tg_account_type_manager_0001",
            description="Set account_type='manager' for is_platform_manager=true accounts",
            up=RawSQLOp(
                stmts=[
                    """UPDATE telegram_accounts
                       SET account_type = 'manager'
                       WHERE is_platform_manager = true AND account_type = 'crawler'""",
                ],
            ),
            down=RawSQLOp(
                stmts=["SELECT 1"],  # lossy — no reverse
            ),
        ),

        # ── 6. Index on account_type for lease queries ───────────────
        Migration(
            version="tg_account_type_index_0001",
            description="Add index on telegram_accounts.account_type for lease filtering",
            up=RawSQLOp(
                stmts=[
                    "CREATE INDEX IF NOT EXISTS idx_telegram_accounts_account_type "
                    "ON telegram_accounts (account_type)",
                ],
            ),
            down=RawSQLOp(
                stmts=[
                    "DROP INDEX IF EXISTS idx_telegram_accounts_account_type",
                ],
            ),
        ),
    ]

