-- 스캔 기능: 잡 모델 + 배치 + 에이전트 생존 (plan: scan-feature-build r3)
-- 웹(fastapi)=intent/명령, 호스트 스캔 에이전트=결과. 싱글톤 스캐너 직렬화.
-- 주: 러너 규약상 이 파일은 schema_migrations 를 건드리지 않음(스탬프는 외부). BEGIN/COMMIT 없음.
-- 순서: 테이블 먼저 → 시드 → 인덱스 (인덱스 실패가 테이블 생성 막지 않게).

-- 잡: 한 스캔 세션 = 한 논리 문서 (배치 N개 → 합본 1 PDF → Inbox)
CREATE TABLE IF NOT EXISTS scan_jobs (
    id                BIGSERIAL PRIMARY KEY,
    title             TEXT NOT NULL,                       -- 사람 입력 제목 (commit 시 documents.title 로 전파)
    settings          JSONB NOT NULL DEFAULT '{}'::jsonb,  -- mode/resolution/source(ADF Duplex) 등 스캔 프로파일
    status            TEXT NOT NULL DEFAULT 'draft',       -- draft|queued|ready|scanning|assembling|preview|committing|committed|failed|canceled
    batch_count       INTEGER NOT NULL DEFAULT 0,          -- 스캔 완료 배치 수
    page_count        INTEGER,                             -- 최종 합본 페이지 수 (assembling 후)
    last_activity_at  TIMESTAMPTZ,                         -- ready 휴지 벽시계 idle 타임아웃 기준 (방치 데드락 방지)
    last_progress_at  TIMESTAMPTZ,                         -- 잡 진행 갱신 (에이전트 생존과 분리)
    staging_path      TEXT,                                -- 호스트 로컬 잡 스테이징 디렉토리
    nas_staging_path  TEXT,                                -- NAS .scan-staging 합본 경로 (B안 미리보기/commit 소스)
    inbox_path        TEXT,                                -- 최종 PKM/Inbox 경로 (commit 후)
    file_hash         CHAR(64),                            -- 합본 sha256 = 정체성/멱등 커밋 키 (commit 시 채움)
    doc_id            BIGINT REFERENCES documents(id) ON DELETE SET NULL,  -- commit 후 연결 (title 전파)
    error             TEXT,                                -- failed 사유 (no-silent)
    created_at        TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at        TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

-- 배치: 스캔 1회(ADF 한 묶음) 단위. batch_seq = 결합 순서(글롭 정렬 아님).
CREATE TABLE IF NOT EXISTS scan_job_batches (
    id            BIGSERIAL PRIMARY KEY,
    job_id        BIGINT REFERENCES scan_jobs(id) ON DELETE CASCADE NOT NULL,
    batch_seq     INTEGER NOT NULL,                    -- 1-based 결합 순서
    staging_path  TEXT,                                -- 이 배치 PDF (호스트 로컬)
    page_count    INTEGER,
    status        TEXT NOT NULL DEFAULT 'scanned',     -- scanned | discarded (잼 폐기 후 재스캔)
    created_at    TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    UNIQUE (job_id, batch_seq)
);

-- 에이전트 생존: 싱글톤 1행. 잡 진행(last_progress_at)과 분리 — queued 잡 stale 오탐 방지.
CREATE TABLE IF NOT EXISTS scan_agent_status (
    id              INTEGER PRIMARY KEY DEFAULT 1 CHECK (id = 1),  -- 단일 행 강제
    last_heartbeat  TIMESTAMPTZ,
    agent_version   TEXT,
    current_job_id  BIGINT REFERENCES scan_jobs(id) ON DELETE SET NULL,
    updated_at      TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
INSERT INTO scan_agent_status (id) VALUES (1) ON CONFLICT (id) DO NOTHING;  -- 시드 1행

-- 활성 잡 락: 스캐너 싱글톤 → in-progress 잡은 전체에서 1개만(나머지 queued).
-- 상수 TRUE 에 unique + in-progress 필터 = 그 상태 행 최대 1개 강제.
CREATE UNIQUE INDEX IF NOT EXISTS uq_scan_jobs_single_active
    ON scan_jobs ((TRUE))
    WHERE status IN ('ready','scanning','assembling','preview','committing');

CREATE INDEX IF NOT EXISTS idx_scan_jobs_queued     ON scan_jobs (created_at) WHERE status = 'queued';
CREATE INDEX IF NOT EXISTS idx_scan_jobs_file_hash  ON scan_jobs (file_hash)  WHERE file_hash IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_scan_job_batches_job ON scan_job_batches (job_id, batch_seq);
