package database // TimetableConstraintMigrations returns the DDL for all 15 constraint tables. // Each table follows the same shape: // - id / created_by_user_id / is_hard / weight / active / note / created_at // - one or more FKs to tt_teacher / tt_class / tt_subject / tt_room // // FK ON DELETE CASCADE removes constraints when their parent (teacher/room/etc.) // is deleted — the rules become meaningless without the referenced resource. func TimetableConstraintMigrations() []string { return []string{ // ---------- Teacher constraints (6) ---------- `CREATE TABLE IF NOT EXISTS tt_constraint_teacher_unavailable_day ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, teacher_id UUID NOT NULL REFERENCES tt_teacher(id) ON DELETE CASCADE, day_of_week INT NOT NULL CHECK (day_of_week BETWEEN 1 AND 7), is_hard BOOLEAN NOT NULL DEFAULT true, weight INT NOT NULL DEFAULT 100 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(teacher_id, day_of_week) )`, `CREATE TABLE IF NOT EXISTS tt_constraint_teacher_unavailable_window ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, teacher_id UUID NOT NULL REFERENCES tt_teacher(id) ON DELETE CASCADE, day_of_week INT NOT NULL CHECK (day_of_week BETWEEN 1 AND 7), start_time TIME NOT NULL, end_time TIME NOT NULL, is_hard BOOLEAN NOT NULL DEFAULT true, weight INT NOT NULL DEFAULT 100 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), CHECK (end_time > start_time) )`, `CREATE TABLE IF NOT EXISTS tt_constraint_teacher_max_hours_day ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, teacher_id UUID NOT NULL REFERENCES tt_teacher(id) ON DELETE CASCADE, max_hours INT NOT NULL CHECK (max_hours BETWEEN 1 AND 12), is_hard BOOLEAN NOT NULL DEFAULT false, weight INT NOT NULL DEFAULT 50 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(teacher_id) )`, `CREATE TABLE IF NOT EXISTS tt_constraint_teacher_max_hours_week ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, teacher_id UUID NOT NULL REFERENCES tt_teacher(id) ON DELETE CASCADE, max_hours INT NOT NULL CHECK (max_hours BETWEEN 1 AND 40), is_hard BOOLEAN NOT NULL DEFAULT true, weight INT NOT NULL DEFAULT 100 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(teacher_id) )`, `CREATE TABLE IF NOT EXISTS tt_constraint_teacher_excluded_subject ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, teacher_id UUID NOT NULL REFERENCES tt_teacher(id) ON DELETE CASCADE, subject_id UUID NOT NULL REFERENCES tt_subject(id) ON DELETE CASCADE, is_hard BOOLEAN NOT NULL DEFAULT true, weight INT NOT NULL DEFAULT 100 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(teacher_id, subject_id) )`, `CREATE TABLE IF NOT EXISTS tt_constraint_teacher_excluded_room ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, teacher_id UUID NOT NULL REFERENCES tt_teacher(id) ON DELETE CASCADE, room_id UUID NOT NULL REFERENCES tt_room(id) ON DELETE CASCADE, is_hard BOOLEAN NOT NULL DEFAULT true, weight INT NOT NULL DEFAULT 100 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(teacher_id, room_id) )`, // ---------- Subject constraints (5) ---------- `CREATE TABLE IF NOT EXISTS tt_constraint_subject_min_day_gap ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, subject_id UUID NOT NULL REFERENCES tt_subject(id) ON DELETE CASCADE, min_gap_days INT NOT NULL CHECK (min_gap_days BETWEEN 1 AND 4), is_hard BOOLEAN NOT NULL DEFAULT false, weight INT NOT NULL DEFAULT 70 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(subject_id) )`, `CREATE TABLE IF NOT EXISTS tt_constraint_subject_max_consecutive ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, subject_id UUID NOT NULL REFERENCES tt_subject(id) ON DELETE CASCADE, max_consecutive INT NOT NULL CHECK (max_consecutive BETWEEN 1 AND 5), is_hard BOOLEAN NOT NULL DEFAULT true, weight INT NOT NULL DEFAULT 100 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(subject_id) )`, `CREATE TABLE IF NOT EXISTS tt_constraint_subject_contiguous_when_repeated ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, subject_id UUID NOT NULL REFERENCES tt_subject(id) ON DELETE CASCADE, is_hard BOOLEAN NOT NULL DEFAULT true, weight INT NOT NULL DEFAULT 100 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(subject_id) )`, `CREATE TABLE IF NOT EXISTS tt_constraint_subject_preferred_period ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, subject_id UUID NOT NULL REFERENCES tt_subject(id) ON DELETE CASCADE, period_from INT NOT NULL CHECK (period_from BETWEEN 1 AND 12), period_to INT NOT NULL CHECK (period_to BETWEEN 1 AND 12), is_hard BOOLEAN NOT NULL DEFAULT false, weight INT NOT NULL DEFAULT 40 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), CHECK (period_to >= period_from) )`, `CREATE TABLE IF NOT EXISTS tt_constraint_subject_double_lesson ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, subject_id UUID NOT NULL REFERENCES tt_subject(id) ON DELETE CASCADE, is_hard BOOLEAN NOT NULL DEFAULT false, weight INT NOT NULL DEFAULT 60 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(subject_id) )`, // ---------- Class constraints (2) ---------- `CREATE TABLE IF NOT EXISTS tt_constraint_class_max_hours_day ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, class_id UUID NOT NULL REFERENCES tt_class(id) ON DELETE CASCADE, max_hours INT NOT NULL CHECK (max_hours BETWEEN 1 AND 12), is_hard BOOLEAN NOT NULL DEFAULT true, weight INT NOT NULL DEFAULT 100 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(class_id) )`, `CREATE TABLE IF NOT EXISTS tt_constraint_class_no_gaps ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, class_id UUID NOT NULL REFERENCES tt_class(id) ON DELETE CASCADE, is_hard BOOLEAN NOT NULL DEFAULT false, weight INT NOT NULL DEFAULT 80 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(class_id) )`, // ---------- Room constraints (2) ---------- `CREATE TABLE IF NOT EXISTS tt_constraint_room_requires_type ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, subject_id UUID NOT NULL REFERENCES tt_subject(id) ON DELETE CASCADE, room_type VARCHAR(30) NOT NULL, is_hard BOOLEAN NOT NULL DEFAULT true, weight INT NOT NULL DEFAULT 100 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(subject_id, room_type) )`, `CREATE TABLE IF NOT EXISTS tt_constraint_room_unavailable ( id UUID PRIMARY KEY DEFAULT gen_random_uuid(), created_by_user_id UUID NOT NULL, room_id UUID NOT NULL REFERENCES tt_room(id) ON DELETE CASCADE, day_of_week INT NOT NULL CHECK (day_of_week BETWEEN 1 AND 7), period_index INT NOT NULL CHECK (period_index BETWEEN 1 AND 12), is_hard BOOLEAN NOT NULL DEFAULT true, weight INT NOT NULL DEFAULT 100 CHECK (weight BETWEEN 0 AND 100), active BOOLEAN NOT NULL DEFAULT true, note TEXT, created_at TIMESTAMPTZ DEFAULT NOW(), UNIQUE(room_id, day_of_week, period_index) )`, // ---------- Indexes ---------- `CREATE INDEX IF NOT EXISTS idx_tt_c_teacher_unav_day_teacher ON tt_constraint_teacher_unavailable_day(teacher_id)`, `CREATE INDEX IF NOT EXISTS idx_tt_c_teacher_unav_win_teacher ON tt_constraint_teacher_unavailable_window(teacher_id)`, `CREATE INDEX IF NOT EXISTS idx_tt_c_teacher_excl_subj_teacher ON tt_constraint_teacher_excluded_subject(teacher_id)`, `CREATE INDEX IF NOT EXISTS idx_tt_c_teacher_excl_room_teacher ON tt_constraint_teacher_excluded_room(teacher_id)`, `CREATE INDEX IF NOT EXISTS idx_tt_c_room_unav_room ON tt_constraint_room_unavailable(room_id)`, } }