"""MySQL state and audit storage for real-time control."""

from __future__ import annotations

import os
import json
from contextlib import contextmanager
from datetime import date, datetime
from pathlib import Path
from typing import Any, Iterator

import pymysql
import pymysql.cursors


ROOT = Path(__file__).resolve().parent


def connect() -> pymysql.Connection:
    required = ["DB_HOST", "DB_USER", "DB_NAME"]
    missing = [key for key in required if not os.getenv(key)]
    if missing:
        raise RuntimeError(f"Missing database environment variables: {', '.join(missing)}")
    return pymysql.connect(
        host=os.environ["DB_HOST"],
        port=int(os.getenv("DB_PORT", "3306")),
        user=os.environ["DB_USER"],
        password=os.getenv("DB_PASSWORD", ""),
        database=os.environ["DB_NAME"],
        charset="utf8mb4",
        cursorclass=pymysql.cursors.DictCursor,
        autocommit=True,
        connect_timeout=int(os.getenv("DB_CONNECT_TIMEOUT", "10")),
        read_timeout=int(os.getenv("DB_READ_TIMEOUT", "60")),
        write_timeout=int(os.getenv("DB_WRITE_TIMEOUT", "60")),
    )


def initialize_schema() -> None:
    statements = [
        statement.strip()
        for statement in (ROOT / "schema.sql").read_text(encoding="utf-8").split(";")
        if statement.strip()
    ]
    connection = connect()
    try:
        with connection.cursor() as cursor:
            for statement in statements:
                cursor.execute(statement)
            cursor.execute(
                """
                SELECT COLUMN_NAME
                FROM information_schema.COLUMNS
                WHERE TABLE_SCHEMA=%s
                  AND TABLE_NAME='realtime_control_ad_state'
                """,
                (os.environ["DB_NAME"],),
            )
            existing_columns = {row["COLUMN_NAME"] for row in cursor.fetchall()}
            migrations = {
                "initial_base_bid_fen": "INT DEFAULT NULL",
                "last_roi_scaled_at": "DATETIME DEFAULT NULL",
                "bid_hold": "BOOLEAN NOT NULL DEFAULT FALSE",
                "bid_hold_reason": "VARCHAR(255) DEFAULT NULL",
                "operator_pause_mode": "VARCHAR(32) DEFAULT NULL",
                "operator_resume_at": "DATETIME DEFAULT NULL",
                "operator_command_id": "VARCHAR(64) DEFAULT NULL",
                "operator_paused_from_status": "VARCHAR(50) DEFAULT NULL",
                "operator_paused_at": "DATETIME DEFAULT NULL",
            }
            for column, definition in migrations.items():
                if column not in existing_columns:
                    cursor.execute(
                        f"ALTER TABLE realtime_control_ad_state "
                        f"ADD COLUMN {column} {definition}"
                    )
            cursor.execute(
                """
                UPDATE realtime_control_ad_state
                SET initial_base_bid_fen=base_bid_fen
                WHERE initial_base_bid_fen IS NULL
                """
            )
            cursor.execute(
                """
                SELECT INDEX_NAME
                FROM information_schema.STATISTICS
                WHERE TABLE_SCHEMA=%s
                  AND TABLE_NAME='realtime_control_ad_state'
                  AND INDEX_NAME='idx_operator_pause'
                """,
                (os.environ["DB_NAME"],),
            )
            if not cursor.fetchone():
                cursor.execute(
                    """
                    CREATE INDEX idx_operator_pause
                    ON realtime_control_ad_state
                        (operator_pause_mode, operator_resume_at)
                    """
                )

            operator_migrations = {
                "operator_command": {
                    "raw_text": "MEDIUMTEXT DEFAULT NULL",
                    "parse_source": "VARCHAR(32) NOT NULL DEFAULT 'deterministic'",
                    "intent_json": "MEDIUMTEXT DEFAULT NULL",
                    "preview_cost_fen": "BIGINT NOT NULL DEFAULT 0",
                    "preview_impressions": "BIGINT NOT NULL DEFAULT 0",
                    "preview_clicks": "BIGINT NOT NULL DEFAULT 0",
                    "preview_conversions": "BIGINT NOT NULL DEFAULT 0",
                    "previewed_at": "DATETIME DEFAULT NULL",
                    "resume_at": "DATETIME DEFAULT NULL",
                },
                "operator_command_item": {
                    "preview_cost_fen": "BIGINT NOT NULL DEFAULT 0",
                    "preview_impressions": "BIGINT NOT NULL DEFAULT 0",
                    "preview_clicks": "BIGINT NOT NULL DEFAULT 0",
                    "preview_conversions": "BIGINT NOT NULL DEFAULT 0",
                    "previewed_at": "DATETIME DEFAULT NULL",
                    "updated_at": (
                        "TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP "
                        "ON UPDATE CURRENT_TIMESTAMP"
                    ),
                },
            }
            for table_name, columns in operator_migrations.items():
                cursor.execute(
                    """
                    SELECT COLUMN_NAME
                    FROM information_schema.COLUMNS
                    WHERE TABLE_SCHEMA=%s AND TABLE_NAME=%s
                    """,
                    (os.environ["DB_NAME"], table_name),
                )
                table_columns = {row["COLUMN_NAME"] for row in cursor.fetchall()}
                for column, definition in columns.items():
                    if column not in table_columns:
                        cursor.execute(
                            f"ALTER TABLE {table_name} ADD COLUMN {column} {definition}"
                        )

            schema_migrations = {
                "roi_metric_run": {
                    "fission_parameter_version": "VARCHAR(64) DEFAULT NULL",
                    "fission_cohort_date": "DATE DEFAULT NULL",
                },
                "roi_entity_snapshot": {
                    "conversion_goal": "VARCHAR(255) DEFAULT NULL",
                    "fission_parameter_version": "VARCHAR(64) DEFAULT NULL",
                    "fission_cohort_date": "VARCHAR(8) DEFAULT NULL",
                    "fission_multiplier_vs_t0": "DECIMAL(18,8) DEFAULT NULL",
                    "fission_multiplier_vs_first": "DECIMAL(18,8) DEFAULT NULL",
                    "fission_match_level": "VARCHAR(64) DEFAULT NULL",
                    "fission_source": "VARCHAR(512) DEFAULT NULL",
                    "actual_total_revenue": "DECIMAL(20,4) DEFAULT NULL",
                    "actual_roi": "DECIMAL(18,8) DEFAULT NULL",
                    "predicted_tail_revenue": "DECIMAL(20,4) DEFAULT NULL",
                    "predicted_fission_revenue": "DECIMAL(20,4) DEFAULT NULL",
                },
                "roi_fission_parameter_value": {
                    "multiplier_vs_first": "DOUBLE DEFAULT NULL",
                },
                "roi_action_item": {
                    "approval_status": "VARCHAR(32) NOT NULL DEFAULT 'PENDING'",
                    "approval_source": "VARCHAR(32) DEFAULT NULL",
                    "approved_at": "DATETIME DEFAULT NULL",
                    "rejected_at": "DATETIME DEFAULT NULL",
                    "sheet_row_number": "INT DEFAULT NULL",
                    "result_notified_at": "DATETIME DEFAULT NULL",
                    "notification_error": "TEXT DEFAULT NULL",
                },
                "roi_agency_delivery": {
                    "creative_rows": "INT NOT NULL DEFAULT 0",
                    "ad_rows": "INT NOT NULL DEFAULT 0",
                },
                "revenue_forecast_result": {
                    "cost_reserve_date": "DATE DEFAULT NULL",
                    "channel_costs_json": "TEXT DEFAULT NULL",
                    "non_miniapp_reserved_cost": (
                        "DECIMAL(20,4) NOT NULL DEFAULT 0"
                    ),
                    "miniapp_target_daily_cost": (
                        "DECIMAL(20,4) NOT NULL DEFAULT 0"
                    ),
                    "forecast_method": (
                        "VARCHAR(64) NOT NULL DEFAULT 'weighted_30m_speed'"
                    ),
                    "parameter_version": "VARCHAR(64) DEFAULT NULL",
                    "current_cumulative_revenue": (
                        "DECIMAL(20,4) DEFAULT NULL"
                    ),
                    "speed_latest_weight": "DECIMAL(12,8) DEFAULT NULL",
                    "latest_interval_revenue": "DECIMAL(20,4) DEFAULT NULL",
                    "previous_interval_revenue": "DECIMAL(20,4) DEFAULT NULL",
                    "weighted_speed": "DECIMAL(20,4) DEFAULT NULL",
                    "remaining_multiplier_p10": "DECIMAL(20,8) DEFAULT NULL",
                    "remaining_multiplier_p50": "DECIMAL(20,8) DEFAULT NULL",
                    "remaining_multiplier_p90": "DECIMAL(20,8) DEFAULT NULL",
                    "parameter_sample_count": "INT DEFAULT NULL",
                },
                "revenue_forecast_observation": {
                    "source_version": (
                        "VARCHAR(64) NOT NULL DEFAULT "
                        "'legacy'"
                    ),
                },
            }
            for table_name, columns in schema_migrations.items():
                cursor.execute(
                    """
                    SELECT COLUMN_NAME
                    FROM information_schema.COLUMNS
                    WHERE TABLE_SCHEMA=%s AND TABLE_NAME=%s
                    """,
                    (os.environ["DB_NAME"], table_name),
                )
                table_columns = {
                    row["COLUMN_NAME"] for row in cursor.fetchall()
                }
                for column, definition in columns.items():
                    if column not in table_columns:
                        cursor.execute(
                            f"ALTER TABLE {table_name} "
                            f"ADD COLUMN {column} {definition}"
                        )
            cursor.execute(
                """
                SELECT COLUMN_NAME, IS_NULLABLE
                FROM information_schema.COLUMNS
                WHERE TABLE_SCHEMA=%s
                  AND TABLE_NAME='revenue_forecast_result'
                  AND COLUMN_NAME IN (
                    'signal_a', 'trend_growths_json', 'signal_a_weight',
                    'signal_b_weight', 'confidence_ratio'
                  )
                """,
                (os.environ["DB_NAME"],),
            )
            legacy_required_columns = {
                row["COLUMN_NAME"]
                for row in cursor.fetchall()
                if row["IS_NULLABLE"] == "NO"
            }
            legacy_definitions = {
                "signal_a": "DECIMAL(20,4) DEFAULT NULL",
                "trend_growths_json": "TEXT DEFAULT NULL",
                "signal_a_weight": "DECIMAL(8,6) DEFAULT NULL",
                "signal_b_weight": "DECIMAL(8,6) DEFAULT NULL",
                "confidence_ratio": "DECIMAL(8,6) DEFAULT NULL",
            }
            for column in sorted(legacy_required_columns):
                cursor.execute(
                    f"ALTER TABLE revenue_forecast_result MODIFY COLUMN "
                    f"{column} {legacy_definitions[column]}"
                )
            cursor.execute(
                """
                SELECT COLUMN_NAME, IS_NULLABLE
                FROM information_schema.COLUMNS
                WHERE TABLE_SCHEMA=%s
                  AND TABLE_NAME='revenue_forecast_observation'
                  AND COLUMN_NAME IN (
                    'yesterday_same_time_revenue',
                    'yesterday_total_revenue'
                  )
                """,
                (os.environ["DB_NAME"],),
            )
            for row in cursor.fetchall():
                if row["IS_NULLABLE"] == "NO":
                    cursor.execute(
                        f"ALTER TABLE revenue_forecast_observation "
                        f"MODIFY COLUMN {row['COLUMN_NAME']} "
                        f"DECIMAL(20,4) DEFAULT NULL"
                    )
            cursor.execute(
                """
                UPDATE revenue_forecast_observation o
                JOIN (
                    SELECT report_time, MIN(forecast_version) AS forecast_version
                    FROM revenue_forecast_result
                    GROUP BY report_time
                    HAVING COUNT(DISTINCT forecast_version) = 1
                ) r ON r.report_time = o.report_time
                LEFT JOIN revenue_forecast_observation exact_observation
                  ON exact_observation.report_time = o.report_time
                 AND exact_observation.source_version = r.forecast_version
                 AND exact_observation.id <> o.id
                SET o.source_version = r.forecast_version
                WHERE o.source_version IN (
                    'legacy', 'revenue_forecast_v4_45m'
                )
                  AND exact_observation.id IS NULL
                """
            )
            cursor.execute(
                """
                SELECT INDEX_NAME
                FROM information_schema.STATISTICS
                WHERE TABLE_SCHEMA=%s
                  AND TABLE_NAME='revenue_forecast_observation'
                  AND INDEX_NAME='uk_revenue_observation_report_time'
                LIMIT 1
                """,
                (os.environ["DB_NAME"],),
            )
            if cursor.fetchone():
                cursor.execute(
                    """
                    ALTER TABLE revenue_forecast_observation
                    DROP INDEX uk_revenue_observation_report_time
                    """
                )
            cursor.execute(
                """
                SELECT INDEX_NAME
                FROM information_schema.STATISTICS
                WHERE TABLE_SCHEMA=%s
                  AND TABLE_NAME='revenue_forecast_observation'
                  AND INDEX_NAME='uk_revenue_observation_version_time'
                LIMIT 1
                """,
                (os.environ["DB_NAME"],),
            )
            if not cursor.fetchone():
                cursor.execute(
                    """
                    CREATE UNIQUE INDEX uk_revenue_observation_version_time
                    ON revenue_forecast_observation
                       (report_time, source_version)
                    """
                )
            cursor.execute(
                """
                SELECT INDEX_NAME
                FROM information_schema.STATISTICS
                WHERE TABLE_SCHEMA=%s
                  AND TABLE_NAME='roi_action_item'
                  AND INDEX_NAME='idx_roi_action_approval'
                """,
                (os.environ["DB_NAME"],),
            )
            if not cursor.fetchone():
                cursor.execute(
                    """
                    CREATE INDEX idx_roi_action_approval
                    ON roi_action_item (run_id, approval_status)
                    """
                )
    finally:
        connection.close()


@contextmanager
def advisory_lock(lock_name: str) -> Iterator[bool]:
    connection = connect()
    acquired = False
    try:
        with connection.cursor() as cursor:
            cursor.execute("SELECT GET_LOCK(%s, 0) AS acquired", (lock_name,))
            acquired = bool((cursor.fetchone() or {}).get("acquired"))
        yield acquired
    finally:
        if acquired:
            try:
                with connection.cursor() as cursor:
                    cursor.execute("SELECT RELEASE_LOCK(%s)", (lock_name,))
            except Exception:
                pass
        connection.close()


def load_enabled_accounts() -> list[dict[str, Any]]:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                SELECT c.account_id, c.audience_name, c.bid_scene
                FROM ad_creation_account_config c
                JOIN account_whitelist w ON w.account_id = c.account_id
                WHERE c.enabled = TRUE
                  AND w.enabled = TRUE
                ORDER BY c.account_id
                """
            )
            return list(cursor.fetchall())
    finally:
        connection.close()


def load_automation_spend_accounts() -> list[dict[str, Any]]:
    """Load historical automation accounts that remain whitelisted."""
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                SELECT c.account_id
                FROM ad_creation_account_config c
                JOIN account_whitelist w ON w.account_id = c.account_id
                WHERE w.enabled = TRUE
                ORDER BY c.account_id
                """
            )
            return list(cursor.fetchall())
    finally:
        connection.close()


def load_all_spend_accounts() -> list[dict[str, Any]]:
    """Load every enabled account from the local account whitelist."""
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                SELECT w.account_id
                FROM account_whitelist w
                WHERE w.enabled = TRUE
                ORDER BY w.account_id
                """
            )
            return list(cursor.fetchall())
    finally:
        connection.close()


def load_realtime_accounts() -> list[dict[str, Any]]:
    """Load full-control automation accounts plus explicit extra scope."""
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                SELECT c.account_id, c.audience_name, c.bid_scene,
                       'FULL' AS control_mode
                FROM ad_creation_account_config c
                JOIN account_whitelist w ON w.account_id = c.account_id
                WHERE w.enabled = TRUE
                ORDER BY c.account_id
                """
            )
            accounts = {
                int(row["account_id"]): row for row in cursor.fetchall()
            }
            cursor.execute(
                """
                SELECT account_id, audience_name, bid_scene, control_mode
                FROM realtime_control_account_scope
                WHERE enabled=TRUE
                ORDER BY account_id
                """
            )
            for row in cursor.fetchall():
                accounts.setdefault(int(row["account_id"]), row)
            return [accounts[key] for key in sorted(accounts)]
    finally:
        connection.close()


def upsert_realtime_account_scope(
    *,
    account_id: int,
    control_mode: str,
    audience_name: str,
    bid_scene: str | None,
    source: str,
    note: str,
) -> None:
    if control_mode not in {"FULL", "PAUSE_ONLY"}:
        raise ValueError(f"Unsupported real-time control mode: {control_mode}")
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                INSERT INTO realtime_control_account_scope
                    (account_id, control_mode, audience_name, bid_scene,
                     enabled, source, note)
                VALUES (%s,%s,%s,%s,TRUE,%s,%s)
                ON DUPLICATE KEY UPDATE
                    control_mode=VALUES(control_mode),
                    audience_name=VALUES(audience_name),
                    bid_scene=VALUES(bid_scene),
                    enabled=TRUE,
                    source=VALUES(source),
                    note=VALUES(note)
                """,
                (
                    account_id,
                    control_mode,
                    audience_name,
                    bid_scene,
                    source,
                    note,
                ),
            )
    finally:
        connection.close()


def load_managed_accounts() -> list[dict[str, Any]]:
    """Historical automation accounts still allowed by the account whitelist."""
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                SELECT c.account_id, c.audience_name, c.bid_scene, c.enabled
                FROM ad_creation_account_config c
                JOIN account_whitelist w ON w.account_id = c.account_id
                WHERE w.enabled = TRUE
                ORDER BY c.account_id
                """
            )
            return list(cursor.fetchall())
    finally:
        connection.close()


def load_daily_state(control_date: date) -> dict[str, Any]:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                "SELECT * FROM realtime_control_daily_state WHERE control_date=%s",
                (control_date,),
            )
            return cursor.fetchone() or {}
    finally:
        connection.close()


def save_daily_state(control_date: date, **values: Any) -> None:
    allowed = {
        "morning_recovery_done",
        "cutoff_done",
        "last_observed_partition",
        "last_observed_cpm",
        "last_decision",
        "last_inventory_refresh_at",
        "last_evaluated_at",
    }
    unknown = set(values) - allowed
    if unknown:
        raise ValueError(f"Unsupported daily-state fields: {sorted(unknown)}")
    columns = ["control_date", *values]
    params = [control_date, *values.values()]
    updates = ", ".join(f"{column}=VALUES({column})" for column in values)
    placeholders = ", ".join(["%s"] * len(columns))
    sql = (
        f"INSERT INTO realtime_control_daily_state ({', '.join(columns)}) "
        f"VALUES ({placeholders}) ON DUPLICATE KEY UPDATE {updates}"
    )
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(sql, params)
    finally:
        connection.close()


def load_ad_states(account_id: int) -> dict[int, dict[str, Any]]:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                "SELECT * FROM realtime_control_ad_state WHERE account_id=%s",
                (account_id,),
            )
            return {int(row["adgroup_id"]): row for row in cursor.fetchall()}
    finally:
        connection.close()


def upsert_ad_state(
    *,
    account_id: int,
    adgroup_id: int,
    adgroup_name: str,
    bid_field: str,
    base_bid_fen: int,
    boosted_date: date | None,
    paused_by_strategy: bool,
    pause_reason: str | None,
    last_action: str,
    action_at: datetime,
) -> None:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                INSERT INTO realtime_control_ad_state
                    (account_id, adgroup_id, adgroup_name, bid_field, base_bid_fen,
                     initial_base_bid_fen,
                     boosted_date, paused_by_strategy, pause_reason, last_action,
                     last_action_at, last_seen_at)
                VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
                ON DUPLICATE KEY UPDATE
                    adgroup_name=VALUES(adgroup_name),
                    bid_field=VALUES(bid_field),
                    initial_base_bid_fen=COALESCE(
                        initial_base_bid_fen,
                        VALUES(initial_base_bid_fen)
                    ),
                    boosted_date=VALUES(boosted_date),
                    paused_by_strategy=VALUES(paused_by_strategy),
                    pause_reason=VALUES(pause_reason),
                    last_action=VALUES(last_action),
                    last_action_at=VALUES(last_action_at),
                    last_seen_at=VALUES(last_seen_at)
                """,
                (
                    account_id,
                    adgroup_id,
                    adgroup_name,
                    bid_field,
                    base_bid_fen,
                    base_bid_fen,
                    boosted_date,
                    paused_by_strategy,
                    pause_reason,
                    last_action,
                    action_at,
                    action_at,
                ),
            )
    finally:
        connection.close()


def sync_roi_base_bid(
    *,
    account_id: int,
    adgroup_id: int,
    adgroup_name: str,
    bid_field: str,
    initial_base_bid_fen: int,
    new_base_bid_fen: int,
    boosted_date: date | None,
    action_at: datetime,
) -> None:
    """Persist a permanent ROI base bid without losing intraday boost state."""

    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                INSERT INTO realtime_control_ad_state
                    (account_id, adgroup_id, adgroup_name, bid_field,
                     base_bid_fen, initial_base_bid_fen, boosted_date,
                     paused_by_strategy, pause_reason, last_action,
                     last_action_at, last_seen_at, last_roi_scaled_at)
                VALUES (%s,%s,%s,%s,%s,%s,%s,FALSE,NULL,'ROI_SCALE',%s,%s,%s)
                ON DUPLICATE KEY UPDATE
                    adgroup_name=VALUES(adgroup_name),
                    bid_field=VALUES(bid_field),
                    base_bid_fen=VALUES(base_bid_fen),
                    initial_base_bid_fen=COALESCE(
                        initial_base_bid_fen,
                        VALUES(initial_base_bid_fen)
                    ),
                    boosted_date=VALUES(boosted_date),
                    last_action='ROI_SCALE',
                    last_action_at=VALUES(last_action_at),
                    last_seen_at=VALUES(last_seen_at),
                    last_roi_scaled_at=VALUES(last_roi_scaled_at)
                """,
                (
                    account_id,
                    adgroup_id,
                    adgroup_name,
                    bid_field,
                    new_base_bid_fen,
                    initial_base_bid_fen,
                    boosted_date,
                    action_at,
                    action_at,
                    action_at,
                ),
            )
    finally:
        connection.close()


def set_bid_experiment_hold(
    *,
    account_id: int,
    adgroup_id: int,
    adgroup_name: str,
    bid_field: str,
    base_bid_fen: int,
    action_at: datetime,
    reason: str,
) -> None:
    """Persist an experiment base bid and prevent real-time bid overrides."""

    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                INSERT INTO realtime_control_ad_state
                    (account_id, adgroup_id, adgroup_name, bid_field,
                     base_bid_fen, initial_base_bid_fen, boosted_date,
                     bid_hold, bid_hold_reason, paused_by_strategy,
                     last_action, last_action_at, last_seen_at)
                VALUES (%s,%s,%s,%s,%s,%s,%s,TRUE,%s,FALSE,
                        'BID_EXPERIMENT_HOLD',%s,%s)
                ON DUPLICATE KEY UPDATE
                    adgroup_name=VALUES(adgroup_name),
                    bid_field=VALUES(bid_field),
                    base_bid_fen=VALUES(base_bid_fen),
                    initial_base_bid_fen=COALESCE(
                        initial_base_bid_fen,
                        VALUES(initial_base_bid_fen)
                    ),
                    boosted_date=VALUES(boosted_date),
                    bid_hold=TRUE,
                    bid_hold_reason=VALUES(bid_hold_reason),
                    last_action='BID_EXPERIMENT_HOLD',
                    last_action_at=VALUES(last_action_at),
                    last_seen_at=VALUES(last_seen_at)
                """,
                (
                    account_id,
                    adgroup_id,
                    adgroup_name,
                    bid_field,
                    base_bid_fen,
                    base_bid_fen,
                    action_at.date(),
                    reason,
                    action_at,
                    action_at,
                ),
            )
    finally:
        connection.close()


def clear_bid_experiment_hold(
    account_id: int,
    adgroup_id: int,
    *,
    action_at: datetime,
) -> None:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                UPDATE realtime_control_ad_state
                SET bid_hold=FALSE,
                    bid_hold_reason=NULL,
                    last_action='BID_EXPERIMENT_RELEASE',
                    last_action_at=%s,
                    last_seen_at=%s
                WHERE account_id=%s AND adgroup_id=%s
                """,
                (action_at, action_at, account_id, adgroup_id),
            )
            if cursor.rowcount != 1:
                raise ValueError(
                    "未找到实验出价状态: "
                    f"account={account_id} adgroup={adgroup_id}"
                )
    finally:
        connection.close()

def insert_action_log(record: dict[str, Any]) -> None:
    columns = [
        "run_id",
        "control_date",
        "observed_partition",
        "observed_cpm",
        "decision",
        "account_id",
        "adgroup_id",
        "adgroup_name",
        "bid_field",
        "base_bid_fen",
        "before_bid_fen",
        "target_bid_fen",
        "before_status",
        "target_status",
        "apply_mode",
        "execution_status",
        "error_message",
    ]
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                f"INSERT INTO realtime_control_action_log ({', '.join(columns)}) "
                f"VALUES ({', '.join(['%s'] * len(columns))})",
                [record.get(column) for column in columns],
            )
    finally:
        connection.close()


def create_operator_command(record: dict[str, Any]) -> dict[str, Any]:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            try:
                cursor.execute(
                    """
                    INSERT INTO operator_command
                        (command_id, source_message_id, chat_id, sender_open_id,
                         sender_name, action, scope_type, target_account_ids,
                         status, preview_account_count, preview_ad_count, expires_at)
                    VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
                    """,
                    (
                        record["command_id"],
                        record["source_message_id"],
                        record["chat_id"],
                        record["sender_open_id"],
                        record.get("sender_name"),
                        record["action"],
                        record["scope_type"],
                        json.dumps(record["target_account_ids"]),
                        record["status"],
                        record.get("preview_account_count", 0),
                        record.get("preview_ad_count", 0),
                        record.get("expires_at"),
                    ),
                )
            except pymysql.err.IntegrityError:
                cursor.execute(
                    "SELECT * FROM operator_command WHERE source_message_id=%s",
                    (record["source_message_id"],),
                )
                existing = cursor.fetchone()
                if existing:
                    existing["target_account_ids"] = json.loads(
                        existing.get("target_account_ids") or "[]"
                    )
                    return existing
                raise
        return load_operator_command(record["command_id"]) or {}
    finally:
        connection.close()


def create_operator_command_with_items(
    record: dict[str, Any],
    items: list[dict[str, Any]],
) -> dict[str, Any]:
    """Atomically persist an immutable command preview and its ad snapshots."""
    connection = connect()
    try:
        connection.begin()
        with connection.cursor() as cursor:
            try:
                cursor.execute(
                    """
                    INSERT INTO operator_command
                        (command_id, source_message_id, chat_id, sender_open_id,
                         sender_name, raw_text, parse_source, intent_json,
                         action, scope_type, target_account_ids, status,
                         preview_account_count, preview_ad_count,
                         preview_cost_fen, preview_impressions, preview_clicks,
                         preview_conversions, previewed_at, resume_at, expires_at)
                    VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s,%s)
                    """,
                    (
                        record["command_id"], record["source_message_id"],
                        record["chat_id"], record["sender_open_id"],
                        record.get("sender_name"), record.get("raw_text"),
                        record.get("parse_source", "deterministic"),
                        json.dumps(record.get("intent") or {}, ensure_ascii=False),
                        record["action"], record["scope_type"],
                        json.dumps(record["target_account_ids"]), record["status"],
                        record.get("preview_account_count", 0),
                        record.get("preview_ad_count", 0),
                        record.get("preview_cost_fen", 0),
                        record.get("preview_impressions", 0),
                        record.get("preview_clicks", 0),
                        record.get("preview_conversions", 0),
                        record.get("previewed_at"), record.get("resume_at"),
                        record.get("expires_at"),
                    ),
                )
            except pymysql.err.IntegrityError:
                connection.rollback()
                existing = load_operator_command_by_source(record["source_message_id"])
                if existing:
                    return existing
                raise

            columns = [
                "command_id", "account_id", "audience_name", "adgroup_id",
                "adgroup_name", "before_status", "target_status",
                "preview_cost_fen", "preview_impressions", "preview_clicks",
                "preview_conversions", "previewed_at", "execution_status",
            ]
            for item in items:
                cursor.execute(
                    f"INSERT INTO operator_command_item ({', '.join(columns)}) "
                    f"VALUES ({', '.join(['%s'] * len(columns))})",
                    [item.get(column) for column in columns],
                )
        connection.commit()
        return load_operator_command(record["command_id"]) or {}
    except Exception:
        connection.rollback()
        raise
    finally:
        connection.close()


def load_operator_command_by_source(source_message_id: str) -> dict[str, Any] | None:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                "SELECT command_id FROM operator_command WHERE source_message_id=%s",
                (source_message_id,),
            )
            row = cursor.fetchone()
        return load_operator_command(row["command_id"]) if row else None
    finally:
        connection.close()


def load_operator_command(command_id: str) -> dict[str, Any] | None:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                "SELECT * FROM operator_command WHERE command_id=%s",
                (command_id,),
            )
            row = cursor.fetchone()
            if row:
                row["target_account_ids"] = json.loads(
                    row.get("target_account_ids") or "[]"
                )
                row["intent"] = json.loads(row.get("intent_json") or "{}")
            return row
    finally:
        connection.close()


def update_operator_command(command_id: str, status: str, **values: Any) -> None:
    allowed = {"confirmed_at", "executed_at", "error_message"}
    unknown = set(values) - allowed
    if unknown:
        raise ValueError(f"Unsupported operator-command fields: {sorted(unknown)}")
    assignments = ["status=%s"]
    params: list[Any] = [status]
    for column, value in values.items():
        assignments.append(f"{column}=%s")
        params.append(value)
    params.append(command_id)
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                f"UPDATE operator_command SET {', '.join(assignments)} "
                "WHERE command_id=%s",
                params,
            )
    finally:
        connection.close()


def transition_operator_command(
    command_id: str,
    *,
    expected_statuses: set[str],
    target_status: str,
    **values: Any,
) -> bool:
    allowed = {"confirmed_at", "executed_at", "error_message"}
    unknown = set(values) - allowed
    if unknown:
        raise ValueError(f"Unsupported operator-command fields: {sorted(unknown)}")
    if not expected_statuses:
        raise ValueError("expected_statuses must not be empty")
    assignments = ["status=%s"]
    params: list[Any] = [target_status]
    for column, value in values.items():
        assignments.append(f"{column}=%s")
        params.append(value)
    placeholders = ", ".join(["%s"] * len(expected_statuses))
    params.extend([command_id, *sorted(expected_statuses)])
    connection = connect()
    try:
        with connection.cursor() as cursor:
            affected = cursor.execute(
                f"""
                UPDATE operator_command
                SET {', '.join(assignments)}
                WHERE command_id=%s
                  AND status IN ({placeholders})
                """,
                params,
            )
            return affected == 1
    finally:
        connection.close()


def insert_operator_command_item(record: dict[str, Any]) -> None:
    columns = [
        "command_id",
        "account_id",
        "audience_name",
        "adgroup_id",
        "adgroup_name",
        "before_status",
        "target_status",
        "readback_status",
        "execution_status",
        "error_message",
    ]
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                f"INSERT INTO operator_command_item ({', '.join(columns)}) "
                f"VALUES ({', '.join(['%s'] * len(columns))})",
                [record.get(column) for column in columns],
            )
    finally:
        connection.close()


def load_operator_command_items(command_id: str) -> list[dict[str, Any]]:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                SELECT * FROM operator_command_item
                WHERE command_id=%s
                ORDER BY account_id, adgroup_id, id
                """,
                (command_id,),
            )
            return list(cursor.fetchall())
    finally:
        connection.close()


def update_operator_command_item(item_id: int, **values: Any) -> None:
    allowed = {
        "adgroup_id", "adgroup_name", "before_status", "target_status",
        "readback_status", "execution_status", "error_message",
    }
    unknown = set(values) - allowed
    if unknown:
        raise ValueError(f"Unsupported operator-command item fields: {sorted(unknown)}")
    if not values:
        return
    assignments = [f"{column}=%s" for column in values]
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                f"UPDATE operator_command_item SET {', '.join(assignments)} WHERE id=%s",
                [*values.values(), item_id],
            )
    finally:
        connection.close()


def find_pending_command_conflict(
    targets: list[tuple[int, int]],
    *,
    now: datetime,
) -> dict[str, Any] | None:
    if not targets:
        return None
    connection = connect()
    try:
        with connection.cursor() as cursor:
            for start in range(0, len(targets), 200):
                chunk = targets[start:start + 200]
                conditions = " OR ".join(
                    ["(i.account_id=%s AND i.adgroup_id=%s)"] * len(chunk)
                )
                params: list[Any] = []
                for account_id, adgroup_id in chunk:
                    params.extend([account_id, adgroup_id])
                params.append(now.replace(tzinfo=None))
                cursor.execute(
                    f"""
                    SELECT c.command_id, c.action, i.account_id, i.adgroup_id
                    FROM operator_command c
                    JOIN operator_command_item i ON i.command_id=c.command_id
                    WHERE ({conditions})
                      AND (
                          (c.status='PENDING_CONFIRMATION' AND c.expires_at >= %s)
                          OR c.status='EXECUTING'
                      )
                    ORDER BY c.created_at
                    LIMIT 1
                    """,
                    params,
                )
                conflict = cursor.fetchone()
                if conflict:
                    return conflict
            return None
    finally:
        connection.close()


def list_pending_operator_commands(
    chat_id: str,
    sender_open_id: str,
    now: datetime,
) -> list[dict[str, Any]]:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                SELECT command_id, action, preview_account_count, preview_ad_count,
                       preview_cost_fen, expires_at
                FROM operator_command
                WHERE chat_id=%s AND sender_open_id=%s
                  AND status='PENDING_CONFIRMATION' AND expires_at >= %s
                ORDER BY created_at
                """,
                (chat_id, sender_open_id, now.replace(tzinfo=None)),
            )
            return list(cursor.fetchall())
    finally:
        connection.close()


def load_active_operator_draft(
    chat_id: str,
    sender_open_id: str,
    now: datetime,
) -> dict[str, Any] | None:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                SELECT * FROM operator_command_draft
                WHERE chat_id=%s AND sender_open_id=%s
                  AND status='ACTIVE' AND expires_at >= %s
                """,
                (chat_id, sender_open_id, now.replace(tzinfo=None)),
            )
            row = cursor.fetchone()
            if row:
                for field in ("account_ids", "missing_fields", "source_message_ids"):
                    row[field] = json.loads(row.get(field) or "[]")
            return row
    finally:
        connection.close()


def save_operator_draft(record: dict[str, Any]) -> dict[str, Any]:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                INSERT INTO operator_command_draft
                    (draft_id, chat_id, sender_open_id, raw_text, action, scope_type,
                     account_ids, missing_fields, source_message_ids, status, expires_at)
                VALUES (%s,%s,%s,%s,%s,%s,%s,%s,%s,'ACTIVE',%s)
                ON DUPLICATE KEY UPDATE
                    draft_id=VALUES(draft_id), raw_text=VALUES(raw_text),
                    action=VALUES(action),
                    scope_type=VALUES(scope_type), account_ids=VALUES(account_ids),
                    missing_fields=VALUES(missing_fields),
                    source_message_ids=VALUES(source_message_ids),
                    status='ACTIVE', expires_at=VALUES(expires_at)
                """,
                (
                    record["draft_id"], record["chat_id"], record["sender_open_id"],
                    record.get("raw_text"), record.get("action"),
                    record.get("scope_type", "MISSING"),
                    json.dumps(record.get("account_ids") or []),
                    json.dumps(record.get("missing_fields") or []),
                    json.dumps(record.get("source_message_ids") or []),
                    record["expires_at"],
                ),
            )
        return record
    finally:
        connection.close()


def close_operator_draft(chat_id: str, sender_open_id: str, status: str) -> None:
    if status not in {"COMPLETED", "CANCELLED", "SUPERSEDED", "EXPIRED"}:
        raise ValueError(f"Unsupported draft status: {status}")
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                UPDATE operator_command_draft SET status=%s
                WHERE chat_id=%s AND sender_open_id=%s AND status='ACTIVE'
                """,
                (status, chat_id, sender_open_id),
            )
    finally:
        connection.close()


def set_operator_pause(
    *,
    account_id: int,
    adgroup_id: int,
    mode: str,
    resume_at: datetime | None,
    command_id: str,
    paused_from_status: str,
    paused_at: datetime,
) -> None:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                UPDATE realtime_control_ad_state
                SET operator_pause_mode=%s,
                    operator_resume_at=%s,
                    operator_command_id=%s,
                    operator_paused_from_status=%s,
                    operator_paused_at=%s,
                    last_action='OPERATOR_PAUSE',
                    last_action_at=%s,
                    last_seen_at=%s
                WHERE account_id=%s AND adgroup_id=%s
                """,
                (
                    mode,
                    resume_at,
                    command_id,
                    paused_from_status,
                    paused_at,
                    paused_at,
                    paused_at,
                    account_id,
                    adgroup_id,
                ),
            )
    finally:
        connection.close()


def clear_operator_pause(
    account_id: int,
    adgroup_id: int,
    *,
    action: str,
    action_at: datetime,
) -> None:
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                """
                UPDATE realtime_control_ad_state
                SET operator_pause_mode=NULL,
                    operator_resume_at=NULL,
                    operator_command_id=NULL,
                    operator_paused_from_status=NULL,
                    operator_paused_at=NULL,
                    last_action=%s,
                    last_action_at=%s,
                    last_seen_at=%s
                WHERE account_id=%s AND adgroup_id=%s
                """,
                (action, action_at, action_at, account_id, adgroup_id),
            )
    finally:
        connection.close()


def load_operator_pauses(
    account_ids: list[int] | None = None,
) -> list[dict[str, Any]]:
    params: list[Any] = []
    where = "WHERE operator_pause_mode IS NOT NULL"
    if account_ids is not None:
        if not account_ids:
            return []
        where += f" AND account_id IN ({', '.join(['%s'] * len(account_ids))})"
        params.extend(account_ids)
    connection = connect()
    try:
        with connection.cursor() as cursor:
            cursor.execute(
                f"""
                SELECT *
                FROM realtime_control_ad_state
                {where}
                ORDER BY account_id, adgroup_id
                """,
                params,
            )
            return list(cursor.fetchall())
    finally:
        connection.close()
