-- Data processing migration.
--
-- IMPORTANT: This file is intentionally NOT wired into application startup.
-- Apply it explicitly in a controlled deployment, or call
-- DataProcessStore.ensure_schema() from an administrative command.

BEGIN;

-- This migration targets the current runtime schema created by
-- 001_platform_runtime.sql. Refuse the UUID/JSONB target-design schema instead
-- of partially altering it with incompatible TEXT foreign keys.
DO $$
DECLARE
    datasets_id_type TEXT;
BEGIN
    SELECT format_type(a.atttypid, a.atttypmod)
      INTO datasets_id_type
      FROM pg_attribute a
      JOIN pg_class c ON c.oid = a.attrelid
      JOIN pg_namespace n ON n.oid = c.relnamespace
     WHERE n.nspname = current_schema()
       AND c.relname = 'datasets'
       AND a.attname = 'id'
       AND a.attnum > 0
       AND NOT a.attisdropped;
    IF datasets_id_type IS NULL THEN
        RAISE EXCEPTION '002_data_process.sql requires 001_platform_runtime.sql first';
    END IF;
    IF datasets_id_type <> 'text' THEN
        RAISE EXCEPTION
          '002_data_process.sql supports only the current TEXT runtime schema; found datasets.id type %',
          datasets_id_type;
    END IF;
END $$;

ALTER TABLE datasets ADD COLUMN IF NOT EXISTS source_task_id TEXT;
ALTER TABLE datasets ADD COLUMN IF NOT EXISTS size_bytes BIGINT NOT NULL DEFAULT 0;
ALTER TABLE datasets ADD COLUMN IF NOT EXISTS record_count BIGINT NOT NULL DEFAULT 0;
ALTER TABLE datasets ADD COLUMN IF NOT EXISTS metadata TEXT NOT NULL DEFAULT '{}';
ALTER TABLE datasets ADD COLUMN IF NOT EXISTS tenant_id TEXT;
ALTER TABLE datasets ADD COLUMN IF NOT EXISTS project_id TEXT;
ALTER TABLE datasets ADD COLUMN IF NOT EXISTS owner_id TEXT;
ALTER TABLE datasets ADD COLUMN IF NOT EXISTS created_by TEXT;
ALTER TABLE datasets ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT now();
ALTER TABLE datasets ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT now();
ALTER TABLE datasets ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;

ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS storage_object_id TEXT;
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS current_version_id TEXT;
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS size_bytes BIGINT NOT NULL DEFAULT 0;
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS record_count BIGINT NOT NULL DEFAULT 0;
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS file_format VARCHAR(40);
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS checksum_sha256 CHAR(64);
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS version_no INTEGER NOT NULL DEFAULT 1;
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS source_task_id TEXT;
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS tenant_id TEXT;
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS project_id TEXT;
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS created_by TEXT;
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS metadata TEXT NOT NULL DEFAULT '{}';
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ NOT NULL DEFAULT now();
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS updated_at TIMESTAMPTZ NOT NULL DEFAULT now();
ALTER TABLE dataset_files ADD COLUMN IF NOT EXISTS deleted_at TIMESTAMPTZ;

CREATE TABLE IF NOT EXISTS data_process_tasks (
    id TEXT PRIMARY KEY,
    name VARCHAR(150) NOT NULL,
    description TEXT,
    status VARCHAR(20) NOT NULL DEFAULT 'pending'
        CHECK (status IN ('pending', 'running', 'completed', 'failed', 'stopped')),
    process_type VARCHAR(20) NOT NULL
        CHECK (process_type IN ('structured', 'unstructured', 'external')),
    source_dataset_id TEXT REFERENCES datasets(id) ON DELETE SET NULL,
    output_dataset_id TEXT REFERENCES datasets(id) ON DELETE SET NULL,
    config TEXT NOT NULL DEFAULT '{}',
    progress NUMERIC(5,2) NOT NULL DEFAULT 0 CHECK (progress >= 0 AND progress <= 100),
    input_count BIGINT NOT NULL DEFAULT 0 CHECK (input_count >= 0),
    output_count BIGINT NOT NULL DEFAULT 0 CHECK (output_count >= 0),
    filtered_count BIGINT NOT NULL DEFAULT 0 CHECK (filtered_count >= 0),
    duplicate_count BIGINT NOT NULL DEFAULT 0 CHECK (duplicate_count >= 0),
    error_count BIGINT NOT NULL DEFAULT 0 CHECK (error_count >= 0),
    failure_reason TEXT,
    generation_run_id TEXT,
    tenant_id TEXT,
    project_id TEXT,
    owner_id TEXT,
    approval_status VARCHAR(30) NOT NULL DEFAULT 'not_required',
    created_by TEXT,
    updated_by TEXT,
    deleted_by TEXT,
    started_at TIMESTAMPTZ,
    completed_at TIMESTAMPTZ,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    deleted_at TIMESTAMPTZ
);

ALTER TABLE data_process_tasks ADD COLUMN IF NOT EXISTS generation_run_id TEXT;

CREATE UNIQUE INDEX IF NOT EXISTS uq_data_process_tasks_name_alive
    ON data_process_tasks(name) WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_data_process_tasks_scope_status
    ON data_process_tasks(tenant_id, project_id, status, created_at DESC)
    WHERE deleted_at IS NULL;
CREATE INDEX IF NOT EXISTS idx_data_process_tasks_creator_created
    ON data_process_tasks(created_by, created_at DESC) WHERE deleted_at IS NULL;

CREATE TABLE IF NOT EXISTS data_process_source_files (
    id TEXT PRIMARY KEY,
    task_id TEXT NOT NULL REFERENCES data_process_tasks(id) ON DELETE CASCADE,
    storage_object_id TEXT,
    name TEXT NOT NULL,
    size_bytes BIGINT NOT NULL DEFAULT 0 CHECK (size_bytes >= 0),
    record_count BIGINT NOT NULL DEFAULT 0 CHECK (record_count >= 0),
    file_format VARCHAR(40),
    checksum_sha256 CHAR(64) NOT NULL,
    version_no INTEGER NOT NULL DEFAULT 1 CHECK (version_no > 0),
    content TEXT NOT NULL,
    content_preview TEXT,
    metadata TEXT NOT NULL DEFAULT '{}',
    tenant_id TEXT,
    project_id TEXT,
    created_by TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    deleted_at TIMESTAMPTZ
);

CREATE INDEX IF NOT EXISTS idx_data_process_source_files_task
    ON data_process_source_files(task_id, created_at) WHERE deleted_at IS NULL;
CREATE UNIQUE INDEX IF NOT EXISTS uq_data_process_source_checksum_alive
    ON data_process_source_files(task_id, checksum_sha256) WHERE deleted_at IS NULL;

CREATE TABLE IF NOT EXISTS data_process_preview_items (
    id TEXT PRIMARY KEY,
    task_id TEXT NOT NULL REFERENCES data_process_tasks(id) ON DELETE CASCADE,
    source_file_id TEXT REFERENCES data_process_source_files(id) ON DELETE CASCADE,
    original_content TEXT NOT NULL DEFAULT '',
    edited_content TEXT NOT NULL DEFAULT '',
    source_start INTEGER CHECK (source_start IS NULL OR source_start >= 0),
    source_end INTEGER CHECK (source_end IS NULL OR source_end >= 0),
    source_start_line INTEGER CHECK (source_start_line IS NULL OR source_start_line > 0),
    source_end_line INTEGER CHECK (source_end_line IS NULL OR source_end_line > 0),
    token_count INTEGER NOT NULL DEFAULT 0 CHECK (token_count >= 0),
    status VARCHAR(20) NOT NULL DEFAULT 'original'
        CHECK (status IN ('original', 'modified', 'manual', 'invalid')),
    quality_score TEXT NOT NULL DEFAULT '{}',
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    CHECK (source_start IS NULL OR source_end IS NULL OR source_end >= source_start),
    CHECK (source_start_line IS NULL OR source_end_line IS NULL OR source_end_line >= source_start_line)
);

CREATE INDEX IF NOT EXISTS idx_data_process_preview_task_file
    ON data_process_preview_items(task_id, source_file_id, created_at);

CREATE TABLE IF NOT EXISTS data_process_results (
    id TEXT PRIMARY KEY,
    task_id TEXT NOT NULL REFERENCES data_process_tasks(id) ON DELETE CASCADE,
    preview_item_id TEXT REFERENCES data_process_preview_items(id) ON DELETE SET NULL,
    instruction TEXT NOT NULL,
    input TEXT NOT NULL DEFAULT '',
    output TEXT NOT NULL,
    original_instruction TEXT,
    original_input TEXT,
    original_output TEXT,
    status VARCHAR(20) NOT NULL DEFAULT 'valid'
        CHECK (status IN ('valid', 'modified', 'invalid')),
    error TEXT,
    split VARCHAR(20) CHECK (split IS NULL OR split IN ('train', 'validation', 'test')),
    quality_score TEXT NOT NULL DEFAULT '{}',
    created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

CREATE INDEX IF NOT EXISTS idx_data_process_results_task_status
    ON data_process_results(task_id, status, id);
CREATE INDEX IF NOT EXISTS idx_data_process_results_task_split
    ON data_process_results(task_id, split);

CREATE TABLE IF NOT EXISTS dataset_file_versions (
    id TEXT PRIMARY KEY,
    dataset_file_id TEXT NOT NULL REFERENCES dataset_files(id) ON DELETE CASCADE,
    version_no INTEGER NOT NULL CHECK (version_no > 0),
    storage_object_id TEXT NOT NULL,
    content_preview TEXT,
    description TEXT,
    base_version_id TEXT REFERENCES dataset_file_versions(id) ON DELETE SET NULL,
    size_bytes BIGINT NOT NULL DEFAULT 0 CHECK (size_bytes >= 0),
    record_count BIGINT NOT NULL DEFAULT 0 CHECK (record_count >= 0),
    checksum_sha256 CHAR(64) NOT NULL,
    source_task_id TEXT REFERENCES data_process_tasks(id) ON DELETE SET NULL,
    metadata TEXT NOT NULL DEFAULT '{}',
    created_by TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

ALTER TABLE dataset_file_versions ADD COLUMN IF NOT EXISTS source_task_id TEXT;
ALTER TABLE dataset_file_versions ADD COLUMN IF NOT EXISTS metadata TEXT NOT NULL DEFAULT '{}';
CREATE UNIQUE INDEX IF NOT EXISTS uq_dataset_file_versions_no_002
    ON dataset_file_versions(dataset_file_id, version_no);
CREATE INDEX IF NOT EXISTS idx_dataset_file_versions_source_task_002
    ON dataset_file_versions(source_task_id) WHERE source_task_id IS NOT NULL;

CREATE TABLE IF NOT EXISTS dataset_records (
    id TEXT PRIMARY KEY,
    dataset_id TEXT NOT NULL REFERENCES datasets(id) ON DELETE CASCADE,
    dataset_file_id TEXT REFERENCES dataset_files(id) ON DELETE CASCADE,
    version_id TEXT REFERENCES dataset_file_versions(id) ON DELETE CASCADE,
    line_no INTEGER,
    split VARCHAR(20) CHECK (split IS NULL OR split IN ('train', 'validation', 'test')),
    instruction TEXT,
    input TEXT,
    output TEXT,
    raw TEXT NOT NULL DEFAULT '{}',
    status VARCHAR(20) NOT NULL DEFAULT 'valid'
        CHECK (status IN ('valid', 'modified', 'invalid')),
    source_task_id TEXT REFERENCES data_process_tasks(id) ON DELETE SET NULL,
    source_result_id TEXT REFERENCES data_process_results(id) ON DELETE SET NULL,
    preview_item_id TEXT REFERENCES data_process_preview_items(id) ON DELETE SET NULL,
    created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);

ALTER TABLE dataset_records ADD COLUMN IF NOT EXISTS source_task_id TEXT;
ALTER TABLE dataset_records ADD COLUMN IF NOT EXISTS source_result_id TEXT;
ALTER TABLE dataset_records ADD COLUMN IF NOT EXISTS preview_item_id TEXT;
CREATE INDEX IF NOT EXISTS idx_dataset_records_dataset_002
    ON dataset_records(dataset_id, id);
CREATE INDEX IF NOT EXISTS idx_dataset_records_source_task_002
    ON dataset_records(source_task_id, source_result_id);
CREATE INDEX IF NOT EXISTS idx_datasets_source_task_002
    ON datasets(source_task_id) WHERE source_task_id IS NOT NULL;
CREATE INDEX IF NOT EXISTS idx_dataset_files_source_task_002
    ON dataset_files(source_task_id) WHERE source_task_id IS NOT NULL;

COMMIT;
