-- =============================================
-- Education 模块 — 练习会话与题目快照 DDL
-- Ticket #6: 练习会话创建、题目快照、恢复与状态机
-- Migration: 002
-- Prerequisites: 000-education-schema.sql (database creation)
--                001-education-tenant-seed.sql (tenant seed data)
-- =============================================

-- =============================================
-- Preconditions
-- =============================================
-- This migration MUST fail if either table already exists (no IF NOT EXISTS).
-- Operator is expected to verify:
--   SELECT COUNT(*) FROM information_schema.tables
--     WHERE table_schema = current_database()
--       AND table_name IN ('education_practice_session', 'education_practice_question');
-- Result MUST be 0 before executing this migration.

-- =============================================
-- 练习会话表
-- =============================================
-- Indexes:
--   uk_tenant_client_session — per-tenant uniqueness for clientSessionId idempotency.
--     Used by: selectByTenantAndClientSessionId (idempotent create check),
--     INSERT ... ON CONFLICT ... for concurrent-create race resolution.
--   idx_tenant_user_status — covers getCurrentSession (latest ACTIVE by tenant+user)
--     and ownership queries. Column order: (tenant_id, user_id, status) so the
--     index supports both filtering by tenant+user and tenant+user+status.
-- Lock impact: INSERT acquires a row-level lock on the unique index entry;
--     concurrent inserts with same (tenant_id, client_session_id) serialize naturally.
--     No additional table-level locks required.
CREATE TABLE education_practice_session (
    id               BIGINT       GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY /* 会话主键 */,
    tenant_id        BIGINT       NOT NULL              /* 租户编号 */,
    user_id          BIGINT       NOT NULL              /* Member 用户编号 */,
    client_session_id VARCHAR(36) NOT NULL              /* 客户端生成的会话标识（UUID），用于幂等创建 */,
    status           VARCHAR(20)  NOT NULL DEFAULT 'ACTIVE'
        /* 会话状态：ACTIVE-进行中, SUBMITTED-已提交, EXPIRED-已过期, CANCELLED-已取消 */,
    question_count   INT          NOT NULL DEFAULT 0    /* 题目总数 */,
    collection_id    VARCHAR(64)  DEFAULT NULL           /* 源题集 ID */,
    node_id          VARCHAR(64)  DEFAULT NULL           /* 源目录节点 ID */,
    type             VARCHAR(32)  DEFAULT NULL           /* 筛选题型 */,
    difficulty       VARCHAR(32)  DEFAULT NULL           /* 筛选难度 */,
    version          INT          NOT NULL DEFAULT 0    /* 乐观锁版本号 */,
    creator          VARCHAR(64)  DEFAULT ''            /* 创建者 */,
    create_time      TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP /* 创建时间 */,
    updater          VARCHAR(64)  DEFAULT ''            /* 更新者 */,
    update_time      TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP /* 更新时间 */,
    deleted          BOOLEAN      NOT NULL DEFAULT false /* 是否删除 */
);
COMMENT ON TABLE education_practice_session IS '教育-练习会话';

CREATE UNIQUE INDEX uk_tenant_client_session ON education_practice_session (tenant_id, client_session_id);
CREATE INDEX idx_tenant_user_status ON education_practice_session (tenant_id, user_id, status);

-- =============================================
-- 练习会话题目快照表
-- =============================================
-- Indexes:
--   uk_session_sequence — per-session uniqueness for question sequence numbers.
--     Used by: insertBatch to ensure no duplicate sequences within a session.
--   idx_session_id — covers selectBySessionIdOrderBySequence (load all questions
--     for a session, ordered by sequence). Also used by cascade delete lookups.
-- Lock impact: INSERT acquires gap locks within session_id range on uk_session_sequence;
--     concurrent inserts into different sessions are independent.
-- Options column: JSONB data type stores only label, content, order — never isCorrect.
--     Application layer (optionsToSafeJson) strips correctness before storage.
CREATE TABLE education_practice_question (
    id               BIGINT       GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY /* 主键 */,
    tenant_id        BIGINT       NOT NULL              /* 租户编号 */,
    session_id       BIGINT       NOT NULL              /* 会话 ID */,
    sequence         INT          NOT NULL              /* 题目序号（1-based，服务端固定） */,
    question_id      VARCHAR(64)  NOT NULL              /* 原始题目 ID */,
    content_version  VARCHAR(64)  NOT NULL DEFAULT ''   /* 快照时的题目内容版本 */,
    stem             TEXT         NOT NULL              /* 题干快照 */,
    type             VARCHAR(32)  NOT NULL              /* 题型快照 */,
    difficulty       VARCHAR(32)  DEFAULT NULL           /* 难度快照 */,
    options          JSONB        NOT NULL              /* 选项快照 JSON（不含 isCorrect） */,
    correct_answer   TEXT         DEFAULT NULL           /* 正确答案快照 */,
    explanation      TEXT         DEFAULT NULL           /* 解析快照 */,
    selected_answer  TEXT         DEFAULT NULL           /* 学生已选答案 */,
    is_answered      BOOLEAN      NOT NULL DEFAULT false /* 是否已作答 */,
    creator          VARCHAR(64)  DEFAULT ''            /* 创建者 */,
    create_time      TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP /* 创建时间 */,
    updater          VARCHAR(64)  DEFAULT ''            /* 更新者 */,
    update_time      TIMESTAMP    NOT NULL DEFAULT CURRENT_TIMESTAMP /* 更新时间 */,
    deleted          BOOLEAN      NOT NULL DEFAULT false /* 是否删除 */
);
COMMENT ON TABLE education_practice_question IS '教育-练习会话题目快照';

CREATE UNIQUE INDEX uk_session_sequence ON education_practice_question (session_id, sequence);
CREATE INDEX idx_session_id ON education_practice_question (session_id);

-- =============================================
-- Post-migration verification queries
-- =============================================
-- Verify tables exist with correct structure:
--   \d education_practice_session
--   \d education_practice_question
-- Verify unique keys are enforced:
--   SELECT * FROM pg_indexes WHERE tablename = 'education_practice_session' AND indexname = 'uk_tenant_client_session';
--   SELECT * FROM pg_indexes WHERE tablename = 'education_practice_question' AND indexname = 'uk_session_sequence';
-- Verify no orphan data (should be 0 after fresh migration):
--   SELECT COUNT(*) FROM education_practice_session;
--   SELECT COUNT(*) FROM education_practice_question;
