"""tasks.scheduled_job_id 列(定时任务执行归属,DESIGN §8.5).

Revision ID: 0017
Revises: 0016
Create Date: 2026-06-26

给 tasks 加 scheduled_job_id(nullable FK → scheduled_jobs.job_id, ondelete SET NULL)。
非 NULL = 该 task 是某定时任务的一次执行(isolated 每次新建 / persistent 首次新建都填),
普通对话列表据此排除,不混进"用户项目"列表;job 软删不硬删,SET NULL 安全。

backfill 存量定时执行 task:
- persistent:bound_task_id 直接指向其常驻 task → 精确回填。
- isolated:working_dir 末段 'scheduled-<job_id 前 8 位>' → 按 8 位前缀匹配 job_id。
  匹配不上的孤行(job 已物理删等)留 NULL,由列表查询的 working_dir LIKE 兜底排除。
"""
from typing import Sequence, Union

import sqlalchemy as sa
from alembic import op
from sqlalchemy.dialects.postgresql import UUID as PG_UUID


revision: str = "0017"
down_revision: Union[str, None] = "0016"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
    op.add_column(
        "tasks",
        sa.Column("scheduled_job_id", PG_UUID(as_uuid=True), nullable=True),
    )
    op.create_foreign_key(
        "fk_tasks_scheduled_job_id",
        "tasks", "scheduled_jobs",
        ["scheduled_job_id"], ["job_id"],
        ondelete="SET NULL",
    )
    # persistent:bound_task_id 精确指向其常驻 task
    op.execute(
        "UPDATE tasks SET scheduled_job_id = j.job_id "
        "FROM scheduled_jobs j "
        "WHERE j.bound_task_id = tasks.task_id"
    )
    # isolated:working_dir 末段 scheduled-<8hex> 按 job_id 前 8 位匹配
    op.execute(
        "UPDATE tasks t SET scheduled_job_id = j.job_id "
        "FROM scheduled_jobs j "
        "WHERE t.scheduled_job_id IS NULL "
        "AND t.working_dir ~ 'scheduled-[0-9a-f]{8}' "
        "AND left(j.job_id::text, 8) = substring(t.working_dir from 'scheduled-([0-9a-f]{8})')"
    )


def downgrade() -> None:
    op.drop_constraint("fk_tasks_scheduled_job_id", "tasks", type_="foreignkey")
    op.drop_column("tasks", "scheduled_job_id")
