#!/usr/bin/env python3
"""Sync late demo schema pieces and seed representative demo rows.

This is a direct DB fallback for environments where PHP/Artisan is not
available locally. It reads the Laravel .env database settings and is
idempotent: it creates missing late-migration tables/columns, then inserts
one or more demo rows only when a table is empty.
"""

from __future__ import annotations

import hashlib
import json
import time
import uuid
from pathlib import Path

import pymysql


ROOT = Path(__file__).resolve().parents[1]


def read_env() -> dict[str, str]:
    values: dict[str, str] = {}
    for line in (ROOT / ".env").read_text(encoding="utf-8").splitlines():
        line = line.strip()
        if not line or line.startswith("#") or "=" not in line:
            continue
        key, value = line.split("=", 1)
        values[key] = value.strip().strip('"').strip("'")
    return values


def connect():
    env = read_env()
    last_error: Exception | None = None
    for attempt in range(1, 6):
        try:
            print(f"connecting_to_database attempt={attempt}", flush=True)
            return pymysql.connect(
                host=env["DB_HOST"],
                port=int(env.get("DB_PORT", "3306")),
                user=env["DB_USERNAME"],
                password=env["DB_PASSWORD"],
                database=env["DB_DATABASE"],
                charset="utf8mb4",
                autocommit=False,
                connect_timeout=12,
                read_timeout=30,
                write_timeout=30,
                cursorclass=pymysql.cursors.DictCursor,
            )
        except Exception as exc:  # remote host can be slow/intermittent
            last_error = exc
            print(f"connect_failed attempt={attempt} error={exc}", flush=True)
            time.sleep(attempt * 2)
    raise RuntimeError(f"Could not connect to database after retries: {last_error}")


def q(cur, sql: str, params: tuple | list | None = None):
    cur.execute(sql, params or ())
    return cur


def table_exists(cur, table: str) -> bool:
    q(cur, "SELECT COUNT(*) c FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=%s", (table,))
    return cur.fetchone()["c"] > 0


def column_exists(cur, table: str, column: str) -> bool:
    q(
        cur,
        "SELECT COUNT(*) c FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=%s AND COLUMN_NAME=%s",
        (table, column),
    )
    return cur.fetchone()["c"] > 0


def table_columns(cur, table: str) -> set[str]:
    q(cur, "SELECT COLUMN_NAME FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=%s", (table,))
    return {row["COLUMN_NAME"] for row in cur.fetchall()}


def add_column(cur, table: str, column: str, ddl: str) -> None:
    if table_exists(cur, table) and not column_exists(cur, table, column):
        q(cur, f"ALTER TABLE `{table}` ADD COLUMN {ddl}")


def exec_ignore(cur, sql: str) -> None:
    try:
        q(cur, sql)
    except Exception as exc:
        print(f"warn: ignored SQL error: {exc}")


def count(cur, table: str) -> int:
    if not table_exists(cur, table):
        return -1
    q(cur, f"SELECT COUNT(*) c FROM `{table}`")
    return int(cur.fetchone()["c"])


def first_id(cur, table: str, where: str = "1=1", params: tuple | list | None = None) -> int | None:
    if not table_exists(cur, table):
        return None
    q(cur, f"SELECT id FROM `{table}` WHERE {where} ORDER BY id LIMIT 1", params or ())
    row = cur.fetchone()
    return int(row["id"]) if row else None


def first_value(cur, table: str, column: str, where: str = "1=1", params: tuple | list | None = None):
    if not table_exists(cur, table) or not column_exists(cur, table, column):
        return None
    q(cur, f"SELECT `{column}` v FROM `{table}` WHERE {where} ORDER BY id LIMIT 1", params or ())
    row = cur.fetchone()
    return row["v"] if row else None


def insert(cur, table: str, data: dict) -> int:
    cols = table_columns(cur, table)
    filtered = {k: v for k, v in data.items() if k in cols}
    keys = list(filtered.keys())
    placeholders = ", ".join(["%s"] * len(keys))
    col_sql = ", ".join(f"`{k}`" for k in keys)
    q(cur, f"INSERT INTO `{table}` ({col_sql}) VALUES ({placeholders})", [filtered[k] for k in keys])
    return int(cur.lastrowid or 0)


def insert_if_empty(cur, table: str, data: dict) -> int | None:
    if not table_exists(cur, table) or count(cur, table) > 0:
        return first_id(cur, table)
    return insert(cur, table, data)


def now_sql() -> str:
    return time.strftime("%Y-%m-%d %H:%M:%S")


def sync_schema(cur) -> None:
    add_column(cur, "grades", "reject_reason", "reject_reason TEXT NULL AFTER status")
    add_column(cur, "grades", "teacher_comment", "teacher_comment TEXT NULL AFTER description")
    add_column(cur, "grades", "school_year_id", "school_year_id BIGINT UNSIGNED NULL AFTER school_id")
    add_column(cur, "grades", "term", "term TINYINT UNSIGNED NULL AFTER school_year_id")
    add_column(cur, "grades", "grade_type", "grade_type VARCHAR(40) NULL AFTER exam_id")
    exec_ignore(cur, "CREATE INDEX idx_grades_grade_type ON grades (grade_type)")
    exec_ignore(cur, "CREATE INDEX idx_grades_school_year_id ON grades (school_year_id)")

    add_column(cur, "school_settings", "eteraz_enabled_grades", "eteraz_enabled_grades JSON NULL AFTER qualitative_thresholds")
    add_column(cur, "school_settings", "grade_weights_json", "grade_weights_json JSON NULL AFTER qualitative_thresholds")
    add_column(cur, "school_settings", "absence_sms_recipients", "absence_sms_recipients JSON NULL AFTER absence_sms_mode")
    add_column(cur, "school_settings", "two_factor_admins_enabled", "two_factor_admins_enabled TINYINT(1) NOT NULL DEFAULT 0 AFTER sms_low_balance_threshold")
    add_column(cur, "users", "two_factor_enabled", "two_factor_enabled TINYINT(1) NOT NULL DEFAULT 0 AFTER must_complete_profile")

    add_column(cur, "announcements", "color", "color VARCHAR(20) NOT NULL DEFAULT '#d97706' AFTER is_important")
    add_column(cur, "announcements", "has_pulse", "has_pulse TINYINT(1) NOT NULL DEFAULT 1 AFTER color")
    add_column(cur, "announcements", "expires_at", "expires_at TIMESTAMP NULL AFTER has_pulse")
    add_column(cur, "attendance_approval_requests", "attendance_record_id", "attendance_record_id BIGINT UNSIGNED NULL AFTER attendance_session_id")
    exec_ignore(cur, "ALTER TABLE class_students MODIFY COLUMN status ENUM('active','inactive','transferred') NOT NULL DEFAULT 'active'")

    add_column(cur, "dashboard_news", "image_path", "image_path VARCHAR(255) NULL AFTER body")
    add_column(cur, "dashboard_news", "template", "template VARCHAR(40) NOT NULL DEFAULT 'info' AFTER is_important")
    add_column(cur, "dashboard_news", "accent_color", "accent_color VARCHAR(20) NOT NULL DEFAULT '#0EA678' AFTER template")
    add_column(cur, "dashboard_news", "layout_json", "layout_json JSON NULL AFTER accent_color")
    add_column(cur, "subjects", "default_max_score", "default_max_score DECIMAL(6,2) NOT NULL DEFAULT 20 AFTER weekly_hours")
    add_column(cur, "subjects", "passing_score", "passing_score DECIMAL(6,2) NULL AFTER default_max_score")
    add_column(cur, "modules", "default_enabled", "default_enabled TINYINT(1) NOT NULL DEFAULT 1 AFTER is_sellable")
    add_column(cur, "files", "folder", "folder VARCHAR(190) NULL AFTER visibility")
    add_column(cur, "files", "thumbnail_path", "thumbnail_path VARCHAR(255) NULL AFTER folder")
    exec_ignore(cur, "ALTER TABLE schools MODIFY status ENUM('active','inactive','suspended','archived') NOT NULL DEFAULT 'active'")

    create_sql = [
        """CREATE TABLE IF NOT EXISTS teacher_calendar_items (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            school_id BIGINT UNSIGNED NOT NULL,
            teacher_id BIGINT UNSIGNED NOT NULL,
            classroom_id BIGINT UNSIGNED NULL,
            title VARCHAR(190) NOT NULL,
            body TEXT NULL,
            kind ENUM('todo','exam','note') NOT NULL DEFAULT 'todo',
            starts_at DATETIME NOT NULL,
            ends_at DATETIME NULL,
            color VARCHAR(20) NOT NULL DEFAULT '#0EA678',
            status ENUM('open','done','cancelled') NOT NULL DEFAULT 'open',
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            INDEX idx_teacher_cal_teacher_date (school_id, teacher_id, starts_at),
            CONSTRAINT fk_teacher_cal_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE,
            CONSTRAINT fk_teacher_cal_teacher FOREIGN KEY (teacher_id) REFERENCES teachers(id) ON DELETE CASCADE,
            CONSTRAINT fk_teacher_cal_class FOREIGN KEY (classroom_id) REFERENCES classrooms(id) ON DELETE SET NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
        """CREATE TABLE IF NOT EXISTS discipline_incidents (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            school_id BIGINT UNSIGNED NOT NULL,
            student_id BIGINT UNSIGNED NOT NULL,
            classroom_id BIGINT UNSIGNED NULL,
            created_by BIGINT UNSIGNED NULL,
            incident_date DATE NOT NULL,
            severity ENUM('low','medium','high') NOT NULL DEFAULT 'medium',
            title VARCHAR(190) NOT NULL,
            description TEXT NULL,
            action_taken TEXT NULL,
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            INDEX idx_disc_student_date (school_id, student_id, incident_date),
            CONSTRAINT fk_disc_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE,
            CONSTRAINT fk_disc_student FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE,
            CONSTRAINT fk_disc_class FOREIGN KEY (classroom_id) REFERENCES classrooms(id) ON DELETE SET NULL,
            CONSTRAINT fk_disc_user FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
        """CREATE TABLE IF NOT EXISTS support_tickets (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            school_id BIGINT UNSIGNED NULL,
            created_by BIGINT UNSIGNED NULL,
            subject VARCHAR(255) NOT NULL,
            priority VARCHAR(30) NOT NULL DEFAULT 'normal',
            status VARCHAR(30) NOT NULL DEFAULT 'open',
            body TEXT NOT NULL,
            admin_note TEXT NULL,
            closed_by BIGINT UNSIGNED NULL,
            closed_at TIMESTAMP NULL,
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            INDEX idx_support_school_status (school_id, status),
            CONSTRAINT fk_support_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE SET NULL,
            CONSTRAINT fk_support_creator FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL,
            CONSTRAINT fk_support_closed_by FOREIGN KEY (closed_by) REFERENCES users(id) ON DELETE SET NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
        """CREATE TABLE IF NOT EXISTS online_class_reactions (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            online_class_id BIGINT UNSIGNED NOT NULL,
            user_id BIGINT UNSIGNED NOT NULL,
            emoji VARCHAR(20) NOT NULL,
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            INDEX idx_reactions_class_id (online_class_id, id),
            CONSTRAINT fk_reactions_class FOREIGN KEY (online_class_id) REFERENCES online_classes(id) ON DELETE CASCADE,
            CONSTRAINT fk_reactions_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
        """CREATE TABLE IF NOT EXISTS grade_reconsideration_requests (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            school_id BIGINT UNSIGNED NOT NULL,
            grade_id BIGINT UNSIGNED NOT NULL,
            teacher_user_id BIGINT UNSIGNED NULL,
            note TEXT NOT NULL,
            status VARCHAR(30) NOT NULL DEFAULT 'pending',
            resolved_by BIGINT UNSIGNED NULL,
            admin_note TEXT NULL,
            resolved_at TIMESTAMP NULL,
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            UNIQUE KEY uniq_grade_reconsider_pending (grade_id, teacher_user_id, status),
            CONSTRAINT fk_reconsider_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE,
            CONSTRAINT fk_reconsider_grade FOREIGN KEY (grade_id) REFERENCES grades(id) ON DELETE CASCADE,
            CONSTRAINT fk_reconsider_teacher_user FOREIGN KEY (teacher_user_id) REFERENCES users(id) ON DELETE SET NULL,
            CONSTRAINT fk_reconsider_resolver FOREIGN KEY (resolved_by) REFERENCES users(id) ON DELETE SET NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
        """CREATE TABLE IF NOT EXISTS api_tokens (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            school_id BIGINT UNSIGNED NULL,
            user_id BIGINT UNSIGNED NULL,
            name VARCHAR(160) NOT NULL,
            token_hash VARCHAR(64) NOT NULL UNIQUE,
            abilities JSON NULL,
            last_used_at TIMESTAMP NULL,
            expires_at TIMESTAMP NULL,
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            CONSTRAINT fk_api_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE SET NULL,
            CONSTRAINT fk_api_user FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
        """CREATE TABLE IF NOT EXISTS clubs (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            school_id BIGINT UNSIGNED NOT NULL,
            name VARCHAR(160) NOT NULL,
            description TEXT NULL,
            teacher_id BIGINT UNSIGNED NULL,
            meeting_day VARCHAR(40) NULL,
            meeting_time TIME NULL,
            status VARCHAR(40) NOT NULL DEFAULT 'active',
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            CONSTRAINT fk_clubs_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE,
            CONSTRAINT fk_clubs_teacher FOREIGN KEY (teacher_id) REFERENCES teachers(id) ON DELETE SET NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
        """CREATE TABLE IF NOT EXISTS club_members (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            club_id BIGINT UNSIGNED NOT NULL,
            student_id BIGINT UNSIGNED NOT NULL,
            joined_at DATE NULL,
            status VARCHAR(40) NOT NULL DEFAULT 'active',
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            UNIQUE KEY uniq_club_student (club_id, student_id),
            CONSTRAINT fk_club_members_club FOREIGN KEY (club_id) REFERENCES clubs(id) ON DELETE CASCADE,
            CONSTRAINT fk_club_members_student FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
        """CREATE TABLE IF NOT EXISTS student_awards (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            school_id BIGINT UNSIGNED NOT NULL,
            student_id BIGINT UNSIGNED NOT NULL,
            title VARCHAR(190) NOT NULL,
            description TEXT NULL,
            badge_color VARCHAR(20) NOT NULL DEFAULT '#0EA678',
            awarded_at DATE NULL,
            awarded_by BIGINT UNSIGNED NULL,
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            CONSTRAINT fk_awards_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE,
            CONSTRAINT fk_awards_student FOREIGN KEY (student_id) REFERENCES students(id) ON DELETE CASCADE,
            CONSTRAINT fk_awards_user FOREIGN KEY (awarded_by) REFERENCES users(id) ON DELETE SET NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
        """CREATE TABLE IF NOT EXISTS substitute_teachers (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            school_id BIGINT UNSIGNED NOT NULL,
            classroom_id BIGINT UNSIGNED NULL,
            subject_id BIGINT UNSIGNED NULL,
            absent_teacher_id BIGINT UNSIGNED NULL,
            substitute_teacher_id BIGINT UNSIGNED NULL,
            starts_at DATETIME NOT NULL,
            ends_at DATETIME NULL,
            note TEXT NULL,
            status VARCHAR(40) NOT NULL DEFAULT 'scheduled',
            created_by BIGINT UNSIGNED NULL,
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            CONSTRAINT fk_sub_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE,
            CONSTRAINT fk_sub_class FOREIGN KEY (classroom_id) REFERENCES classrooms(id) ON DELETE SET NULL,
            CONSTRAINT fk_sub_subject FOREIGN KEY (subject_id) REFERENCES subjects(id) ON DELETE SET NULL,
            CONSTRAINT fk_sub_absent FOREIGN KEY (absent_teacher_id) REFERENCES teachers(id) ON DELETE SET NULL,
            CONSTRAINT fk_sub_substitute FOREIGN KEY (substitute_teacher_id) REFERENCES teachers(id) ON DELETE SET NULL,
            CONSTRAINT fk_sub_user FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
        """CREATE TABLE IF NOT EXISTS question_bank_items (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            school_id BIGINT UNSIGNED NOT NULL,
            subject_id BIGINT UNSIGNED NULL,
            created_by BIGINT UNSIGNED NULL,
            question_type VARCHAR(40) NOT NULL DEFAULT 'test',
            difficulty VARCHAR(40) NOT NULL DEFAULT 'medium',
            body TEXT NOT NULL,
            options JSON NULL,
            correct_index TINYINT UNSIGNED NULL,
            score DECIMAL(6,2) NOT NULL DEFAULT 1,
            tags JSON NULL,
            status VARCHAR(40) NOT NULL DEFAULT 'active',
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            CONSTRAINT fk_qbank_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE,
            CONSTRAINT fk_qbank_subject FOREIGN KEY (subject_id) REFERENCES subjects(id) ON DELETE SET NULL,
            CONSTRAINT fk_qbank_user FOREIGN KEY (created_by) REFERENCES users(id) ON DELETE SET NULL
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
        """CREATE TABLE IF NOT EXISTS grade_average_cache_meta (
            id BIGINT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
            school_id BIGINT UNSIGNED NOT NULL,
            classroom_id BIGINT UNSIGNED NULL,
            cache_key VARCHAR(190) NOT NULL UNIQUE,
            computed_at TIMESTAMP NULL,
            created_at TIMESTAMP NULL,
            updated_at TIMESTAMP NULL,
            CONSTRAINT fk_grade_cache_school FOREIGN KEY (school_id) REFERENCES schools(id) ON DELETE CASCADE,
            CONSTRAINT fk_grade_cache_class FOREIGN KEY (classroom_id) REFERENCES classrooms(id) ON DELETE CASCADE
        ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci""",
    ]

    for sql in create_sql:
        q(cur, sql)

def clean_demo_labels(cur) -> None:
    updates = [
        ("schools", 1, {"name_fa": "دبیرستان دانشیار", "name_en": "Daneshyar Middle School", "school_code": "DEMO-001", "province": "تهران", "city": "تهران", "address": "تهران، خیابان ولیعصر، پلاک ۱۲"}),
        ("schools", 2, {"name_fa": "دبستان امام رضا", "name_en": "Imam Reza Elementary", "school_code": "DEMO-002", "province": "تهران", "city": "کرج", "address": "کرج، خیابان آزادی، پلاک ۴۵"}),
        ("classrooms", 1, {"name": "هفتم الف"}),
        ("classrooms", 2, {"name": "هشتم الف"}),
        ("classrooms", 3, {"name": "دوم الف"}),
        ("classrooms", 4, {"name": "سوم الف"}),
        ("subjects", 1, {"name": "ریاضی ۷"}),
        ("subjects", 2, {"name": "ادبیات فارسی ۷"}),
        ("subjects", 3, {"name": "علوم تجربی ۷"}),
        ("subjects", 4, {"name": "ریاضی ۸"}),
        ("subjects", 5, {"name": "ادبیات فارسی ۸"}),
        ("subjects", 6, {"name": "ریاضی"}),
        ("subjects", 7, {"name": "فارسی"}),
        ("subjects", 8, {"name": "علوم"}),
    ]
    for table, row_id, data in updates:
        if not table_exists(cur, table):
            continue
        cols = table_columns(cur, table)
        pairs = [(k, v) for k, v in data.items() if k in cols]
        if not pairs:
            continue
        sql = ", ".join(f"`{k}`=%s" for k, _ in pairs)
        q(cur, f"UPDATE `{table}` SET {sql}, updated_at=NOW() WHERE id=%s", [v for _, v in pairs] + [row_id])

    names = {
        2: ("احمد", "محمدی"),
        3: ("رضا", "کریمی"),
        4: ("مریم", "صادقی"),
        5: ("محمد", "رضوی"),
        10: ("علی", "رضایی"),
        11: ("محمد", "کریمی"),
        12: ("سارا", "احمدی"),
    }
    for row_id, (first, last) in names.items():
        q(cur, "UPDATE users SET first_name=%s, last_name=%s, updated_at=NOW() WHERE id=%s", (first, last, row_id))
    for row_id, (first, last) in {1: ("علی", "رضایی"), 2: ("محمد", "کریمی"), 3: ("سارا", "احمدی")}.items():
        q(cur, "UPDATE students SET first_name=%s, last_name=%s, updated_at=NOW() WHERE id=%s", (first, last, row_id))
    for row_id, (first, last) in {1: ("مریم", "صادقی"), 2: ("محمد", "رضوی")}.items():
        q(cur, "UPDATE teachers SET first_name=%s, last_name=%s, updated_at=NOW() WHERE id=%s", (first, last, row_id))


def seed_tables(cur) -> None:
    school_id = first_id(cur, "schools") or 1
    owner_user_id = first_id(cur, "users", "school_id=%s AND role_key IN ('owner','principal')", (school_id,)) or first_id(cur, "users") or 1
    teacher_user_id = first_id(cur, "users", "school_id=%s AND role_key='teacher'", (school_id,)) or owner_user_id
    student_user_id = first_id(cur, "users", "school_id=%s AND role_key='student'", (school_id,)) or owner_user_id
    teacher_id = first_id(cur, "teachers", "school_id=%s", (school_id,)) or 1
    student_id = first_id(cur, "students", "school_id=%s", (school_id,)) or 1
    classroom_id = first_id(cur, "classrooms", "school_id=%s", (school_id,)) or 1
    subject_id = first_id(cur, "subjects", "school_id=%s", (school_id,)) or 1
    grade_id = first_id(cur, "grades", "school_id=%s", (school_id,)) or 1
    attendance_session_id = first_id(cur, "attendance_sessions", "school_id=%s", (school_id,)) or 1
    attendance_record_id = first_id(cur, "attendance_records", "school_id=%s", (school_id,)) or None
    homework_id = first_id(cur, "homeworks", "school_id=%s", (school_id,)) or 1
    module_id = first_id(cur, "modules", "key_name='grades'") or first_id(cur, "modules") or 1
    core_module_id = first_id(cur, "modules", "key_name='core'") or module_id
    permission_id = first_id(cur, "permissions") or 1
    grade_level_id = first_id(cur, "grade_levels") or 1

    insert_if_empty(cur, "plans", {"key_name": "demo_full", "name_fa": "پلن کامل دمو", "description": "پلن نمایشی برای مشاهده همه امکانات", "price": 0, "status": "active", "created_at": now_sql(), "updated_at": now_sql()})
    plan_id = first_id(cur, "plans")
    insert_if_empty(cur, "roles", {"school_id": school_id, "key_name": "principal", "name_fa": "مدیر مدرسه", "is_system": 1, "created_at": now_sql(), "updated_at": now_sql()})
    role_id = first_id(cur, "roles")
    insert_if_empty(cur, "role_permissions", {"role_id": role_id, "permission_id": permission_id, "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "module_dependencies", {"module_id": module_id, "required_module_id": core_module_id, "is_required": 1, "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "permission_dependencies", {"permission_id": permission_id, "required_permission_id": permission_id, "is_required": 0, "created_at": now_sql(), "updated_at": now_sql()})
    package_id = first_id(cur, "permission_packages") or 1
    insert_if_empty(cur, "permission_package_items", {"package_id": package_id, "permission_id": permission_id, "module_id": module_id, "grade_level_id": grade_level_id, "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "licenses", {"school_id": school_id, "plan_id": plan_id, "starts_at": "2026-07-13", "expires_at": "2027-07-13", "status": "active", "max_students": 500, "max_teachers": 60, "max_storage_mb": 20480, "created_by": owner_user_id, "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "school_years", {"school_id": school_id, "title": "سال تحصیلی ۱۴۰۵-۱۴۰۶", "starts_on": "2026-09-23", "ends_on": "2027-06-20", "status": "active", "promoted_students": 0, "graduated_students": 0, "created_at": now_sql(), "updated_at": now_sql()})
    school_year_id = first_id(cur, "school_years", "school_id=%s", (school_id,))
    q(cur, "UPDATE grades SET school_year_id=%s, term=1, grade_type=COALESCE(grade_type,'quiz') WHERE school_id=%s AND school_year_id IS NULL", (school_year_id, school_id))

    insert_if_empty(cur, "school_module_limits", {"school_id": school_id, "module_id": module_id, "limit_key": "max_demo_rows", "limit_value": "1000", "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "school_module_logs", {"school_id": school_id, "module_id": module_id, "action": "demo_enabled", "old_value": json.dumps({"enabled": False}), "new_value": json.dumps({"enabled": True}), "created_by": owner_user_id, "created_at": now_sql()})
    insert_if_empty(cur, "school_permissions", {"school_id": school_id, "permission_id": permission_id, "enabled": 1, "starts_at": "2026-07-13", "expires_at": "2027-07-13", "enabled_by": owner_user_id, "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "user_permissions", {"user_id": owner_user_id, "permission_id": permission_id, "effect": "allow", "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "contact_requests", {"school_name": "مدرسه نمونه تماس", "manager_name": "مدیر دمو", "mobile": "09120000999", "city": "تهران", "message": "درخواست دمو و راه‌اندازی سامانه", "ip_address": "127.0.0.1", "status": "new", "created_at": now_sql(), "updated_at": now_sql()})

    news_id = insert_if_empty(cur, "dashboard_news", {"school_id": school_id, "sender_id": owner_user_id, "title": "خبر تصویری دمو", "body": "این خبر برای نمایش کارت‌های داشبورد و اعلان تصویری ساخته شده است.", "is_important": 1, "template": "warning", "accent_color": "#F59E0B", "audience_type": "all", "layout_json": json.dumps({"blocks": [{"text": "شروع سال تحصیلی"}]}, ensure_ascii=False), "expires_at": "2027-01-01 00:00:00", "created_at": now_sql(), "updated_at": now_sql()})
    news_id = news_id or first_id(cur, "dashboard_news")
    insert_if_empty(cur, "dashboard_news_reads", {"news_id": news_id, "user_id": student_user_id, "read_at": now_sql()})
    insert_if_empty(cur, "notifications", {"school_id": school_id, "user_id": student_user_id, "title": "اعلان دمو", "body": "یک اعلان خوانده‌نشده برای تست زنگ اعلان.", "created_at": now_sql(), "updated_at": now_sql()})

    exam_id = insert_if_empty(cur, "exams", {"school_id": school_id, "classroom_id": classroom_id, "subject_id": subject_id, "teacher_id": teacher_id, "title": "امتحان دمو ریاضی", "type": "mixed", "starts_at": "2026-07-20 09:00:00", "ends_at": "2026-07-20 10:00:00", "duration_minutes": 60, "max_score": 20, "status": "published", "settings": json.dumps({"show_answers_after_submit": True}), "created_at": now_sql(), "updated_at": now_sql()})
    exam_id = exam_id or first_id(cur, "exams")
    question_id = insert_if_empty(cur, "exam_questions", {"school_id": school_id, "exam_id": exam_id, "question_type": "test", "body": "حاصل ۲ + ۲ چند است؟", "score": 10, "sort_order": 1, "created_at": now_sql(), "updated_at": now_sql()})
    question_id = question_id or first_id(cur, "exam_questions")
    option_id = insert_if_empty(cur, "exam_options", {"question_id": question_id, "body": "۴", "is_correct": 1, "sort_order": 1, "created_at": now_sql(), "updated_at": now_sql()})
    option_id = option_id or first_id(cur, "exam_options")
    exam_session_id = insert_if_empty(cur, "exam_sessions", {"school_id": school_id, "exam_id": exam_id, "student_id": student_id, "started_at": "2026-07-20 09:05:00", "submitted_at": "2026-07-20 09:35:00", "score": 10, "status": "submitted", "question_order": json.dumps([question_id]), "ip_address": "127.0.0.1", "user_agent": "demo-seed", "created_at": now_sql(), "updated_at": now_sql()})
    exam_session_id = exam_session_id or first_id(cur, "exam_sessions")
    insert_if_empty(cur, "exam_answers", {"school_id": school_id, "exam_session_id": exam_session_id, "question_id": question_id, "option_id": option_id, "answer_text": "۴", "score": 10, "created_at": now_sql(), "updated_at": now_sql()})

    insert_if_empty(cur, "grade_change_logs", {"school_id": school_id, "grade_id": grade_id, "student_id": student_id, "changed_by": teacher_user_id, "old_score": 17, "new_score": 18, "reason": "اصلاح نمره دمو", "ip_address": "127.0.0.1", "created_at": now_sql()})
    insert_if_empty(cur, "grade_objections", {"school_id": school_id, "grade_id": grade_id, "student_id": student_id, "reason": "درخواست بررسی مجدد نمره دمو", "status": "pending", "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "grade_reconsideration_requests", {"school_id": school_id, "grade_id": grade_id, "teacher_user_id": teacher_user_id, "note": "درخواست بازبینی دمو از طرف معلم", "status": "pending", "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "homework_submissions", {"school_id": school_id, "homework_id": homework_id, "student_id": student_id, "body": "پاسخ تکلیف دمو", "status": "submitted", "submitted_at": now_sql(), "reviewed_by": teacher_user_id, "reviewed_at": now_sql(), "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "report_cards", {"school_id": school_id, "student_id": student_id, "classroom_id": classroom_id, "period_key": "1405-term-1", "average_score": 18.25, "status": "published", "data": json.dumps({"subjects": [{"name": "ریاضی", "score": 18.25}]}, ensure_ascii=False), "published_by": owner_user_id, "published_at": now_sql(), "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "discipline_incidents", {"school_id": school_id, "student_id": student_id, "classroom_id": classroom_id, "created_by": owner_user_id, "incident_date": "2026-07-13", "severity": "low", "title": "یادداشت انضباطی دمو", "description": "نمونه ثبت انضباطی برای نمایش صفحه.", "action_taken": "گفتگو با دانش‌آموز", "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "teacher_calendar_items", {"school_id": school_id, "teacher_id": teacher_id, "classroom_id": classroom_id, "title": "امتحان ریاضی هفته بعد", "body": "یادآور دمو برای تقویم معلم", "kind": "exam", "starts_at": "2026-07-20 09:00:00", "ends_at": "2026-07-20 10:00:00", "color": "#DC2626", "status": "open", "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "support_tickets", {"school_id": school_id, "created_by": owner_user_id, "subject": "درخواست پشتیبانی دمو", "priority": "normal", "status": "open", "body": "این تیکت برای نمایش صندوق پشتیبانی ساخته شده است.", "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "api_tokens", {"school_id": school_id, "user_id": owner_user_id, "name": "توکن عمومی دمو", "token_hash": hashlib.sha256(b"demo-api-token").hexdigest(), "abilities": json.dumps(["grades", "attendance"]), "last_used_at": now_sql(), "expires_at": "2027-07-13 00:00:00", "created_at": now_sql(), "updated_at": now_sql()})
    club_id = insert_if_empty(cur, "clubs", {"school_id": school_id, "name": "باشگاه رباتیک", "description": "باشگاه دمو برای فوق برنامه", "teacher_id": teacher_id, "meeting_day": "شنبه", "meeting_time": "14:00:00", "status": "active", "created_at": now_sql(), "updated_at": now_sql()})
    club_id = club_id or first_id(cur, "clubs")
    insert_if_empty(cur, "club_members", {"club_id": club_id, "student_id": student_id, "joined_at": "2026-07-13", "status": "active", "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "student_awards", {"school_id": school_id, "student_id": student_id, "title": "دانش‌آموز برتر دمو", "description": "نشان نمونه برای پروفایل دانش‌آموز", "badge_color": "#0EA678", "awarded_at": "2026-07-13", "awarded_by": owner_user_id, "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "substitute_teachers", {"school_id": school_id, "classroom_id": classroom_id, "subject_id": subject_id, "absent_teacher_id": teacher_id, "substitute_teacher_id": teacher_id, "starts_at": "2026-07-21 08:00:00", "ends_at": "2026-07-21 09:30:00", "note": "جانشین دمو", "status": "scheduled", "created_by": owner_user_id, "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "question_bank_items", {"school_id": school_id, "subject_id": subject_id, "created_by": teacher_user_id, "question_type": "test", "difficulty": "medium", "body": "سوال بانک دمو: عدد اول کدام است؟", "options": json.dumps(["۴", "۶", "۷", "۹"], ensure_ascii=False), "correct_index": 2, "score": 1, "tags": json.dumps(["دمو", "ریاضی"], ensure_ascii=False), "status": "active", "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "grade_average_cache_meta", {"school_id": school_id, "classroom_id": classroom_id, "cache_key": f"demo:{school_id}:{classroom_id}:term1", "computed_at": now_sql(), "created_at": now_sql(), "updated_at": now_sql()})

    file_id = insert_if_empty(cur, "files", {"school_id": school_id, "uploaded_by": owner_user_id, "disk": "local", "path": "demo/sample.pdf", "original_name": "sample.pdf", "mime_type": "application/pdf", "size_bytes": 1024, "visibility": "private", "folder": "demo", "created_at": now_sql(), "updated_at": now_sql()})
    import_job_id = insert_if_empty(cur, "import_jobs", {"school_id": school_id, "type": "students", "file_path": "demo/students.csv", "status": "completed", "total_rows": 3, "valid_rows": 3, "invalid_rows": 0, "created_by": owner_user_id, "started_at": now_sql(), "finished_at": now_sql(), "created_at": now_sql(), "updated_at": now_sql()})
    import_job_id = import_job_id or first_id(cur, "import_jobs")
    insert_if_empty(cur, "import_job_rows", {"import_job_id": import_job_id, "row_index": 1, "raw_data": json.dumps({"name": "دانش‌آموز دمو"}, ensure_ascii=False), "status": "imported", "created_at": now_sql(), "updated_at": now_sql()})
    export_job_id = insert_if_empty(cur, "export_jobs", {"school_id": school_id, "type": "students", "format": "xlsx", "status": "completed", "file_path": "exports/demo-students.xlsx", "requested_by": owner_user_id, "expires_at": "2027-07-13 00:00:00", "created_at": now_sql(), "updated_at": now_sql()})
    export_job_id = export_job_id or first_id(cur, "export_jobs")
    insert_if_empty(cur, "export_files", {"export_job_id": export_job_id, "file_path": "exports/demo-students.xlsx", "size_bytes": 2048, "created_at": now_sql()})

    invoice_id = insert_if_empty(cur, "invoices", {"school_id": school_id, "invoice_number": "DEMO-INV-1405-001", "type": "sms_charge", "amount": 100000, "status": "paid", "description": "فاکتور شارژ پیامک دمو", "issued_by": owner_user_id, "paid_at": now_sql(), "created_at": now_sql(), "updated_at": now_sql()})
    invoice_id = invoice_id or first_id(cur, "invoices")
    payment_id = insert_if_empty(cur, "payments", {"school_id": school_id, "invoice_id": invoice_id, "provider_name": "demo", "provider_transaction_id": "DEMO-TXN-001", "amount": 100000, "status": "paid", "request_payload": json.dumps({"demo": True}), "response_payload": json.dumps({"ok": True}), "paid_at": now_sql(), "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "sms_charge_requests", {"school_id": school_id, "package_id": first_id(cur, "sms_packages"), "requested_by": owner_user_id, "sms_count": 100, "price": 100000, "status": "approved", "note": "درخواست شارژ دمو", "reviewed_by": owner_user_id, "reviewed_at": now_sql(), "created_at": now_sql(), "updated_at": now_sql()})
    sms_message_id = insert_if_empty(cur, "sms_messages", {"school_id": school_id, "sender_user_id": owner_user_id, "target_type": "student_contact", "message_body": "پیامک دمو دانشیار", "recipient_count": 1, "cost": 1, "status": "sent", "provider_message_id": "demo-sms-1", "created_at": now_sql(), "updated_at": now_sql()})
    sms_message_id = sms_message_id or first_id(cur, "sms_messages")
    insert_if_empty(cur, "sms_recipients", {"sms_message_id": sms_message_id, "recipient_type": "student_contact", "recipient_id": student_id, "mobile": "09121020001", "status": "sent", "provider_status": "demo", "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "sms_provider_logs", {"school_id": school_id, "sms_message_id": sms_message_id, "provider_name": "demo", "request_payload": json.dumps({"to": "09121020001"}), "response_payload": json.dumps({"status": "sent"}), "status": "sent", "created_at": now_sql()})
    insert_if_empty(cur, "sms_wallet_transactions", {"school_id": school_id, "type": "charge", "amount": 100, "balance_before": 0, "balance_after": 100, "payment_id": "DEMO-TXN-001", "created_by": owner_user_id, "description": "شارژ دمو", "created_at": now_sql()})
    approval_id = insert_if_empty(cur, "attendance_approval_requests", {"school_id": school_id, "attendance_session_id": attendance_session_id, "attendance_record_id": attendance_record_id, "requested_by": teacher_user_id, "assigned_to": owner_user_id, "type": "absence_sms", "status": "approved", "absent_count": 1, "sms_cost": 1, "reason": "تایید پیامک غیبت دمو", "approved_by": owner_user_id, "approved_at": now_sql(), "created_at": now_sql(), "updated_at": now_sql()})
    approval_id = approval_id or first_id(cur, "attendance_approval_requests")
    insert_if_empty(cur, "attendance_sms_logs", {"school_id": school_id, "attendance_session_id": attendance_session_id, "sms_message_id": sms_message_id, "approval_request_id": approval_id, "created_at": now_sql()})

    online_class_id = insert_if_empty(cur, "online_classes", {"room_key": str(uuid.uuid4()), "school_id": school_id, "classroom_id": classroom_id, "subject_id": subject_id, "teacher_id": teacher_id, "teacher_user_id": teacher_user_id, "title": "کلاس آنلاین دمو", "meeting_url": None, "starts_at": "2026-07-22 10:00:00", "ends_at": "2026-07-22 11:00:00", "session_date": "2026-07-22", "status": "live", "allow_student_chat": 1, "allow_student_files": 1, "require_token": 1, "started_at": now_sql(), "created_at": now_sql(), "updated_at": now_sql()})
    online_class_id = online_class_id or first_id(cur, "online_classes")
    token_id = insert_if_empty(cur, "online_class_tokens", {"school_id": school_id, "online_class_id": online_class_id, "student_id": student_id, "token": "DEMO-0001", "status": "active", "expires_at": "2027-07-13 00:00:00", "first_used_at": now_sql(), "last_used_at": now_sql(), "use_count": 1, "created_by": owner_user_id, "created_at": now_sql(), "updated_at": now_sql()})
    token_id = token_id or first_id(cur, "online_class_tokens")
    insert_if_empty(cur, "online_class_participants", {"school_id": school_id, "online_class_id": online_class_id, "user_id": student_user_id, "display_name": "علی رضایی", "role": "student", "token_id": token_id, "joined_at": now_sql(), "last_seen_at": now_sql(), "total_seconds": 900, "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "online_class_messages", {"school_id": school_id, "online_class_id": online_class_id, "user_id": teacher_user_id, "body": "سلام، کلاس دمو شروع شد.", "created_at": now_sql()})
    insert_if_empty(cur, "online_class_files", {"school_id": school_id, "online_class_id": online_class_id, "user_id": teacher_user_id, "path": "online/demo.pdf", "original_name": "جزوه دمو.pdf", "mime_type": "application/pdf", "size_bytes": 2048, "created_at": now_sql()})
    insert_if_empty(cur, "online_class_board_events", {"online_class_id": online_class_id, "user_id": teacher_user_id, "event": json.dumps({"tool": "pen", "points": [[10, 10], [40, 40]]}), "created_at": now_sql()})
    insert_if_empty(cur, "online_class_logs", {"school_id": school_id, "online_class_id": online_class_id, "student_id": student_id, "user_id": student_user_id, "token_id": token_id, "ip_address": "127.0.0.1", "user_agent": "demo-seed", "joined_at": now_sql()})
    poll_id = insert_if_empty(cur, "online_class_polls", {"online_class_id": online_class_id, "question": "کلاس را متوجه شدید؟", "options": json.dumps(["بله", "نیاز به توضیح بیشتر"], ensure_ascii=False), "status": "open", "created_by": teacher_user_id, "created_at": now_sql()})
    poll_id = poll_id or first_id(cur, "online_class_polls")
    insert_if_empty(cur, "online_class_poll_votes", {"poll_id": poll_id, "user_id": student_user_id, "option_index": 0, "created_at": now_sql()})
    insert_if_empty(cur, "online_class_reactions", {"online_class_id": online_class_id, "user_id": student_user_id, "emoji": "👍", "created_at": now_sql(), "updated_at": now_sql()})

    voting_id = insert_if_empty(cur, "votings", {"school_id": school_id, "title": "رأی‌گیری شورای دمو", "description": "نمونه رأی‌گیری برای نمایش ماژول", "type": "student_council", "is_secret": 1, "starts_at": "2026-07-13 08:00:00", "ends_at": "2026-07-20 18:00:00", "status": "published", "created_by": owner_user_id, "published_by": owner_user_id, "published_at": now_sql(), "created_at": now_sql(), "updated_at": now_sql()})
    voting_id = voting_id or first_id(cur, "votings")
    candidate_id = insert_if_empty(cur, "voting_candidates", {"school_id": school_id, "voting_id": voting_id, "student_id": student_id, "display_name": "علی رضایی", "classroom_id": classroom_id, "slogan": "مدرسه بهتر برای همه", "description": "کاندیدای دمو", "status": "approved", "created_at": now_sql(), "updated_at": now_sql()})
    candidate_id = candidate_id or first_id(cur, "voting_candidates")
    insert_if_empty(cur, "voting_eligible_voters", {"school_id": school_id, "voting_id": voting_id, "student_id": student_id, "grade_level_id": grade_level_id, "classroom_id": classroom_id, "created_at": now_sql(), "updated_at": now_sql()})
    insert_if_empty(cur, "voting_votes", {"school_id": school_id, "voting_id": voting_id, "candidate_id": candidate_id, "student_id": student_id, "vote_hash": hashlib.sha256(b"demo-vote").hexdigest(), "ip_address": "127.0.0.1", "user_agent": "demo-seed", "created_at": now_sql()})
    insert_if_empty(cur, "voting_logs", {"school_id": school_id, "voting_id": voting_id, "user_id": owner_user_id, "action": "created", "new_value": json.dumps({"demo": True}), "ip_address": "127.0.0.1", "user_agent": "demo-seed", "created_at": now_sql()})
    insert_if_empty(cur, "voting_results_snapshots", {"school_id": school_id, "voting_id": voting_id, "snapshot": json.dumps({"total": 1, "results": [{"candidate": "علی رضایی", "votes": 1}]}, ensure_ascii=False), "created_by": owner_user_id, "created_at": now_sql()})

    insert_if_empty(cur, "cache_locks", {"key": "demo-lock", "owner": "seed", "expiration": int(time.time()) + 3600})
    insert_if_empty(cur, "jobs", {"queue": "demo-seed-hold", "payload": json.dumps({"displayName": "DemoSeedJob", "job": "demo"}), "attempts": 0, "available_at": int(time.time()) + 31536000, "created_at": int(time.time())})
    insert_if_empty(cur, "otp_codes", {"mobile": "09120000000", "purpose": "demo", "code_hash": hashlib.sha256(b"12345").hexdigest(), "attempts": 0, "expires_at": "2027-07-13 00:00:00", "created_at": now_sql()})
    insert_if_empty(cur, "password_resets", {"school_id": school_id, "user_id": student_user_id, "mobile": "09121010001", "token": hashlib.sha256(b"demo-reset").hexdigest(), "expires_at": "2027-07-13 00:00:00", "created_at": now_sql()})
    insert_if_empty(cur, "user_activity_logs", {"school_id": school_id, "user_id": owner_user_id, "action": "demo_seed", "description": "فعالیت دمو برای گزارش‌ها", "ip_address": "127.0.0.1", "user_agent": "demo-seed", "created_at": now_sql()})
    insert_if_empty(cur, "user_usage_daily", {"school_id": school_id, "user_id": owner_user_id, "date": "2026-07-13", "login_count": 2, "active_minutes": 45, "important_actions_count": 5, "created_at": now_sql(), "updated_at": now_sql()})

    settings = {
        "asset_cdn_url": "",
        "landing_enamad_html": "",
        "landing_samandehi_html": "",
        "landing_achievements_json": json.dumps([
            {"title": "مدارس فعال", "subtitle": "دموی کامل با داده نمونه", "icon": "bi-buildings", "url": "/features"},
            {"title": "گزارش‌های آماده", "subtitle": "همه جدول‌ها seed شده‌اند", "icon": "bi-bar-chart", "url": "/super-admin/status"},
        ], ensure_ascii=False),
        "demo_school_enabled": "1",
        "demo_school_reset_minutes": "60",
        "demo_school_last_reset_at": now_sql(),
    }
    for key, value in settings.items():
        q(cur, "INSERT INTO system_settings (key_name, value, updated_at) VALUES (%s,%s,NOW()) ON DUPLICATE KEY UPDATE value=VALUES(value), updated_at=NOW()", (key, value))


def empty_tables(cur) -> list[str]:
    q(cur, "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA=DATABASE() AND TABLE_TYPE='BASE TABLE' ORDER BY TABLE_NAME")
    empties = []
    for row in cur.fetchall():
        table = row["TABLE_NAME"]
        if count(cur, table) == 0:
            empties.append(table)
    return empties


def main() -> None:
    conn = connect()
    try:
        cur = conn.cursor()
        print("checking_empty_tables_before", flush=True)
        before = empty_tables(cur)
        print(f"before_empty={len(before)}")
        if before:
            print("before_empty_tables=" + ", ".join(before))

        print("syncing_schema", flush=True)
        sync_schema(cur)
        print("cleaning_demo_labels", flush=True)
        clean_demo_labels(cur)
        print("seeding_demo_rows", flush=True)
        seed_tables(cur)
        print("committing_changes", flush=True)
        conn.commit()

        print("checking_empty_tables_after", flush=True)
        after = empty_tables(cur)
        print(f"after_empty={len(after)}")
        if after:
            print("after_empty_tables=" + ", ".join(after))
        else:
            print("all_tables_have_rows=1")
    except Exception:
        conn.rollback()
        raise
    finally:
        conn.close()


if __name__ == "__main__":
    main()
