# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# This file is the source Rails uses to define your schema when running `bin/rails
# db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to
# be faster and is potentially less error prone than running all of your
# migrations from scratch. Old migrations may fail to apply correctly if those
# migrations use external dependencies or application code.
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[8.0].define(version: 2026_07_27_123600) do
  create_table "active_storage_attachments", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "name", null: false
    t.string "record_type", null: false
    t.bigint "record_id", null: false
    t.bigint "blob_id", null: false
    t.datetime "created_at", null: false
    t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id"
    t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true
  end

  create_table "active_storage_blobs", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "key", null: false
    t.string "filename", null: false
    t.string "content_type"
    t.text "metadata"
    t.string "service_name", null: false
    t.bigint "byte_size", null: false
    t.string "checksum"
    t.datetime "created_at", null: false
    t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true
  end

  create_table "active_storage_variant_records", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "blob_id", null: false
    t.string "variation_digest", null: false
    t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true
  end

  create_table "deerjikists", primary_key: ["platform", "code"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "platform", limit: 16, null: false
    t.string "code", null: false
    t.bigint "tag_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["tag_id"], name: "index_deerjikists_on_tag_id"
  end

  create_table "gekanator_ai_runs", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "model", null: false
    t.integer "input_tokens", default: 0, null: false
    t.integer "output_tokens", default: 0, null: false
    t.decimal "estimated_cost_jpy", precision: 8, scale: 3, default: "0.0", null: false
    t.string "status", default: "pending", null: false
    t.bigint "gekanator_question_suggestion_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["gekanator_question_suggestion_id"], name: "index_gekanator_ai_runs_on_gekanator_question_suggestion_id"
  end

  create_table "gekanator_games", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "user_id", null: false
    t.bigint "guessed_post_id", null: false
    t.bigint "correct_post_id", null: false
    t.boolean "won", null: false
    t.integer "question_count", null: false
    t.json "answers", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["correct_post_id"], name: "index_gekanator_games_on_correct_post_id"
    t.index ["guessed_post_id"], name: "index_gekanator_games_on_guessed_post_id"
    t.index ["user_id"], name: "index_gekanator_games_on_user_id"
    t.check_constraint "`question_count` >= 0", name: "chk_gekanator_games_question_count_nonnegative"
  end

  create_table "gekanator_question_examples", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "gekanator_question_id", null: false
    t.bigint "post_id", null: false
    t.bigint "user_id", null: false
    t.bigint "gekanator_game_id"
    t.string "answer", null: false
    t.string "source", default: "post_game_extra", null: false
    t.float "weight", default: 1.0, null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.json "answer_counts", null: false
    t.integer "sample_count", default: 1, null: false
    t.index ["gekanator_game_id"], name: "index_gekanator_question_examples_on_gekanator_game_id"
    t.index ["gekanator_question_id", "post_id", "user_id"], name: "idx_gekanator_question_examples_on_question_post_user", unique: true
    t.index ["gekanator_question_id"], name: "index_gekanator_question_examples_on_gekanator_question_id"
    t.index ["post_id"], name: "index_gekanator_question_examples_on_post_id"
    t.index ["user_id"], name: "index_gekanator_question_examples_on_user_id"
  end

  create_table "gekanator_question_suggestions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "gekanator_game_id", null: false
    t.bigint "user_id", null: false
    t.text "question_text", null: false
    t.boolean "processed", default: false, null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "answer", null: false
    t.index ["gekanator_game_id"], name: "index_gekanator_question_suggestions_on_gekanator_game_id"
    t.index ["user_id"], name: "index_gekanator_question_suggestions_on_user_id"
  end

  create_table "gekanator_questions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "text", null: false
    t.string "kind", null: false
    t.json "condition", null: false
    t.string "source", default: "ai_generated", null: false
    t.string "status", default: "pending", null: false
    t.float "priority_weight", default: 1.0, null: false
    t.bigint "gekanator_question_suggestion_id"
    t.bigint "created_by_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["created_by_id"], name: "index_gekanator_questions_on_created_by_id"
    t.index ["gekanator_question_suggestion_id"], name: "index_gekanator_questions_on_gekanator_question_suggestion_id"
  end

  create_table "ip_addresses", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.binary "ip_address", limit: 16, null: false
    t.datetime "banned_at"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["banned_at"], name: "index_ip_addresses_on_banned_at"
    t.index ["ip_address"], name: "index_ip_addresses_on_ip_address", unique: true
  end

  create_table "material_export_items", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "material_id", null: false
    t.string "profile", default: "legacy_drive", null: false
    t.string "export_path", null: false
    t.boolean "enabled", default: true, null: false
    t.bigint "created_by_user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["created_by_user_id"], name: "index_material_export_items_on_created_by_user_id"
    t.index ["material_id", "profile"], name: "index_material_export_items_on_material_id_and_profile", unique: true
    t.index ["material_id"], name: "index_material_export_items_on_material_id"
    t.index ["profile", "export_path"], name: "index_material_export_items_on_profile_and_export_path", unique: true
  end

  create_table "material_import_blocks", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "match_kind", null: false
    t.string "sha256"
    t.string "external_path_pattern"
    t.string "reason", null: false
    t.text "note"
    t.bigint "created_by_user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["created_by_user_id"], name: "index_material_import_blocks_on_created_by_user_id"
  end

  create_table "material_sync_sources", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "name", null: false
    t.string "source_kind", null: false
    t.string "source_uri"
    t.string "source_path"
    t.string "source_file_id"
    t.string "profile", default: "legacy_drive", null: false
    t.boolean "enabled", default: true, null: false
    t.string "default_tag_name"
    t.string "export_path_prefix"
    t.datetime "last_synced_at"
    t.bigint "created_by_user_id"
    t.bigint "updated_by_user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["created_by_user_id"], name: "index_material_sync_sources_on_created_by_user_id"
    t.index ["enabled"], name: "index_material_sync_sources_on_enabled"
    t.index ["source_kind"], name: "index_material_sync_sources_on_source_kind"
    t.index ["updated_by_user_id"], name: "index_material_sync_sources_on_updated_by_user_id"
  end

  create_table "material_sync_suppressions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "source_kind", null: false
    t.string "source_uri"
    t.string "drive_path"
    t.string "drive_file_id"
    t.string "normalized_source_key", null: false
    t.string "reason", null: false
    t.bigint "created_by_user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["created_by_user_id"], name: "index_material_sync_suppressions_on_created_by_user_id"
    t.index ["drive_file_id"], name: "index_material_sync_suppressions_on_drive_file_id"
    t.index ["normalized_source_key"], name: "index_material_sync_suppressions_on_normalized_source_key", unique: true
    t.index ["source_kind"], name: "index_material_sync_suppressions_on_source_kind"
  end

  create_table "material_versions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "material_id", null: false
    t.integer "version_no", null: false
    t.string "url"
    t.bigint "parent_id"
    t.bigint "tag_id"
    t.bigint "created_by_user_id"
    t.bigint "updated_by_user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "discarded_at"
    t.string "event_type", null: false
    t.string "tag_name"
    t.string "tag_category"
    t.json "export_paths_json"
    t.bigint "file_blob_id"
    t.string "file_filename"
    t.string "file_content_type"
    t.bigint "file_byte_size"
    t.string "file_checksum"
    t.string "file_sha256"
    t.string "source_kind"
    t.string "source_uri"
    t.string "source_path"
    t.string "source_file_id"
    t.string "normalized_source_key"
    t.index ["created_by_user_id"], name: "index_material_versions_on_created_by_user_id"
    t.index ["discarded_at"], name: "index_material_versions_on_discarded_at"
    t.index ["file_blob_id"], name: "index_material_versions_on_file_blob_id"
    t.index ["material_id", "version_no"], name: "index_material_versions_on_material_id_and_version_no", unique: true
    t.index ["material_id"], name: "index_material_versions_on_material_id"
    t.index ["parent_id"], name: "index_material_versions_on_parent_id"
    t.index ["tag_id"], name: "index_material_versions_on_tag_id"
    t.index ["updated_by_user_id"], name: "index_material_versions_on_updated_by_user_id"
    t.index ["url"], name: "index_material_versions_on_url"
    t.check_constraint "`event_type` in (_utf8mb4'create',_utf8mb4'update',_utf8mb4'discard',_utf8mb4'restore')", name: "material_versions_event_type_valid"
  end

  create_table "materials", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "url"
    t.bigint "parent_id"
    t.bigint "tag_id"
    t.bigint "created_by_user_id"
    t.bigint "updated_by_user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "discarded_at"
    t.virtual "active_url", type: :string, as: "if((`discarded_at` is null),`url`,NULL)"
    t.integer "version_no", default: 1, null: false
    t.string "source_kind"
    t.string "source_uri"
    t.string "source_path"
    t.string "source_file_id"
    t.string "normalized_source_key"
    t.virtual "active_normalized_source_key", type: :string, as: "if((`discarded_at` is null),`normalized_source_key`,NULL)"
    t.index ["active_normalized_source_key"], name: "index_materials_on_active_normalized_source_key", unique: true
    t.index ["active_url"], name: "index_materials_on_active_url", unique: true
    t.index ["created_by_user_id"], name: "index_materials_on_created_by_user_id"
    t.index ["discarded_at"], name: "index_materials_on_discarded_at"
    t.index ["parent_id"], name: "index_materials_on_parent_id"
    t.index ["tag_id"], name: "index_materials_on_tag_id"
    t.index ["updated_by_user_id"], name: "index_materials_on_updated_by_user_id"
  end

  create_table "nico_tag_relations", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "nico_tag_id", null: false
    t.bigint "tag_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["nico_tag_id"], name: "index_nico_tag_relations_on_nico_tag_id"
    t.index ["tag_id"], name: "index_nico_tag_relations_on_tag_id"
  end

  create_table "nico_tag_versions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "tag_id", null: false
    t.integer "version_no", null: false
    t.string "event_type", null: false
    t.string "name", null: false
    t.text "linked_tags", null: false
    t.datetime "created_at", null: false
    t.bigint "created_by_user_id"
    t.index ["created_at"], name: "index_nico_tag_versions_on_created_at"
    t.index ["created_by_user_id", "created_at"], name: "index_nico_tag_versions_on_created_by_user_id_and_created_at", order: { created_at: :desc }
    t.index ["tag_id", "created_at"], name: "index_nico_tag_versions_on_tag_id_and_created_at", order: { created_at: :desc }
    t.index ["tag_id", "version_no"], name: "index_nico_tag_versions_on_tag_id_and_version_no", unique: true
    t.check_constraint "`version_no` > 0", name: "nico_tag_versions_version_no_positive"
  end

  create_table "post_implications", primary_key: ["post_id", "parent_post_id"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "post_id", null: false
    t.bigint "parent_post_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["parent_post_id"], name: "index_post_implications_on_parent_post_id"
    t.check_constraint "`post_id` <> `parent_post_id`", name: "chk_post_implications_no_self"
  end

  create_table "post_similarities", primary_key: ["post_id", "target_post_id"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "post_id", null: false
    t.bigint "target_post_id", null: false
    t.float "cos", null: false
    t.index ["post_id", "cos"], name: "index_post_similarities_on_post_id_and_cos", order: { cos: :desc }
    t.index ["target_post_id"], name: "index_post_similarities_on_target_post_id"
  end

  create_table "post_tag_sections", primary_key: ["post_id", "tag_id", "begin_ms"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "post_id", null: false
    t.bigint "tag_id", null: false
    t.integer "begin_ms", null: false
    t.integer "end_ms"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["post_id", "begin_ms"], name: "idx_post_tag_sections_post_id_begin_ms"
    t.index ["tag_id"], name: "fk_rails_8be3847903"
    t.check_constraint "(`end_ms` is null) or (`begin_ms` < `end_ms`)", name: "chk_post_tag_sections_end_ms_after_begin_ms"
    t.check_constraint "`begin_ms` >= 0", name: "chk_post_tag_sections_begin_ms_natural"
  end

  create_table "post_tags", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "post_id", null: false
    t.bigint "tag_id", null: false
    t.bigint "created_user_id"
    t.bigint "deleted_user_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "discarded_at"
    t.virtual "is_active", type: :boolean, as: "(`discarded_at` is null)", stored: true
    t.virtual "active_unique_key", type: :string, as: "(case when (`discarded_at` is null) then concat(`post_id`,_utf8mb4':',`tag_id`) else NULL end)", stored: true
    t.index ["active_unique_key"], name: "idx_post_tags_active_unique", unique: true
    t.index ["created_user_id"], name: "index_post_tags_on_created_user_id"
    t.index ["deleted_user_id"], name: "index_post_tags_on_deleted_user_id"
    t.index ["discarded_at"], name: "index_post_tags_on_discarded_at"
    t.index ["post_id", "discarded_at"], name: "index_post_tags_on_post_id_and_discarded_at"
    t.index ["post_id"], name: "index_post_tags_on_post_id"
    t.index ["tag_id", "discarded_at"], name: "index_post_tags_on_tag_id_and_discarded_at"
    t.index ["tag_id"], name: "index_post_tags_on_tag_id"
  end

  create_table "post_url_sanitisation_rules", primary_key: "priority", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "source_pattern", null: false
    t.string "replacement", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "discarded_at"
    t.index ["discarded_at"], name: "index_post_url_sanitisation_rules_on_discarded_at"
    t.index ["source_pattern"], name: "index_post_url_sanitisation_rules_on_source_pattern", unique: true
  end

  create_table "post_versions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "post_id", null: false
    t.integer "version_no", null: false
    t.string "event_type", null: false
    t.string "title"
    t.string "url", limit: 768, null: false
    t.string "thumbnail_base", limit: 2000
    t.text "tags", null: false
    t.json "tags_json", null: false
    t.text "parent_post_ids", null: false
    t.datetime "original_created_from"
    t.datetime "original_created_before"
    t.datetime "created_at", null: false
    t.bigint "created_by_user_id"
    t.integer "video_ms"
    t.index ["created_by_user_id"], name: "index_post_versions_on_created_by_user_id"
    t.index ["post_id", "version_no"], name: "index_post_versions_on_post_id_and_version_no", unique: true
    t.index ["post_id"], name: "index_post_versions_on_post_id"
    t.index ["video_ms", "post_id"], name: "idx_post_versions_video_ms_post_id"
    t.check_constraint "(`video_ms` is null) or (`video_ms` > 0)", name: "chk_post_versions_video_ms_positive"
    t.check_constraint "`event_type` in (_utf8mb4'create',_utf8mb4'update',_utf8mb4'discard',_utf8mb4'restore')", name: "post_versions_event_type_valid"
    t.check_constraint "`version_no` > 0", name: "post_versions_version_no_positive"
    t.check_constraint "json_schema_valid(_utf8mb4'{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"id\":{\"type\":\"integer\",\"minimum\":1},\"version_no\":{\"type\":\"integer\",\"minimum\":1},\"name\":{\"type\":\"string\",\"minLength\":1},\"category\":{\"type\":\"string\",\"enum\":[\"deerjikist\",\"meme\",\"character\",\"general\",\"material\",\"meta\",\"nico\"]},\"sections\":{\"type\":\"array\",\"items\":{\"type\":\"object\",\"properties\":{\"begin_ms\":{\"type\":\"integer\",\"minimum\":0},\"end_ms\":{\"type\":[\"integer\",\"null\"],\"minimum\":0}},\"required\":[\"begin_ms\",\"end_ms\"],\"additionalProperties\":false}}},\"required\":[\"id\",\"version_no\",\"name\",\"category\",\"sections\"],\"additionalProperties\":false}}',`tags_json`)", name: "chk_post_versions_tags_json_schema"
  end

  create_table "posts", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "title"
    t.string "url", limit: 768, null: false
    t.string "thumbnail_base", limit: 2000
    t.bigint "uploaded_user_id"
    t.datetime "created_at", null: false
    t.datetime "original_created_from"
    t.datetime "original_created_before"
    t.datetime "updated_at", null: false
    t.integer "version_no", null: false
    t.integer "video_ms"
    t.index ["uploaded_user_id"], name: "index_posts_on_uploaded_user_id"
    t.index ["url"], name: "index_posts_on_url", unique: true
    t.index ["video_ms", "id"], name: "idx_posts_video_ms_id"
    t.check_constraint "(`video_ms` is null) or (`video_ms` > 0)", name: "chk_posts_video_ms_positive"
    t.check_constraint "`version_no` > 0", name: "chk_posts_version_no_positive"
  end

  create_table "settings", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "user_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.string "theme", default: "system", null: false
    t.string "auto_fetch_title", default: "manual", null: false
    t.string "auto_fetch_thumbnail", default: "manual", null: false
    t.string "wiki_editor_mode", default: "split", null: false
    t.index ["user_id"], name: "index_settings_on_user_id", unique: true
  end

  create_table "wiki_assets", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "wiki_page_id", null: false
    t.integer "no", null: false
    t.string "alt_text"
    t.binary "sha256", limit: 32, null: false
    t.bigint "created_by_user_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["created_by_user_id"], name: "index_wiki_assets_on_created_by_user_id"
    t.index ["wiki_page_id", "no"], name: "index_wiki_assets_on_wiki_page_id_and_no", unique: true
    t.index ["wiki_page_id", "sha256"], name: "index_wiki_assets_on_wiki_page_id_and_sha256", unique: true
  end

  create_table "tag_implications", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "tag_id", null: false
    t.bigint "parent_tag_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["parent_tag_id"], name: "index_tag_implications_on_parent_tag_id"
    t.index ["tag_id", "parent_tag_id"], name: "index_tag_implications_on_tag_id_and_parent_tag_id", unique: true
    t.index ["tag_id"], name: "index_tag_implications_on_tag_id"
  end

  create_table "tag_name_sanitisation_rules", primary_key: "priority", id: :integer, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "source_pattern", null: false
    t.string "replacement", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "discarded_at"
    t.index ["discarded_at"], name: "index_tag_name_sanitisation_rules_on_discarded_at"
    t.index ["source_pattern"], name: "index_tag_name_sanitisation_rules_on_source_pattern", unique: true
  end

  create_table "tag_names", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "name", null: false
    t.bigint "canonical_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "discarded_at"
    t.index ["canonical_id"], name: "index_tag_names_on_canonical_id"
    t.index ["discarded_at"], name: "index_tag_names_on_discarded_at"
    t.index ["name"], name: "index_tag_names_on_name", unique: true
  end

  create_table "tag_similarities", primary_key: ["tag_id", "target_tag_id"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "tag_id", null: false
    t.bigint "target_tag_id", null: false
    t.float "cos", null: false
    t.index ["tag_id", "cos"], name: "index_tag_similarities_on_tag_id_and_cos", order: { cos: :desc }
    t.index ["target_tag_id"], name: "index_tag_similarities_on_target_tag_id"
  end

  create_table "tag_versions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "tag_id", null: false
    t.integer "version_no", null: false
    t.string "event_type", null: false
    t.string "name", null: false
    t.string "category", null: false
    t.text "aliases", null: false
    t.text "parent_tag_ids", null: false
    t.datetime "deprecated_at"
    t.datetime "created_at", null: false
    t.bigint "created_by_user_id"
    t.index ["created_at"], name: "index_tag_versions_on_created_at"
    t.index ["created_by_user_id", "created_at"], name: "index_tag_versions_on_created_by_user_id_and_created_at", order: { created_at: :desc }
    t.index ["tag_id", "created_at"], name: "index_tag_versions_on_tag_id_and_created_at", order: { created_at: :desc }
    t.index ["tag_id", "version_no"], name: "index_tag_versions_on_tag_id_and_version_no", unique: true
    t.check_constraint "`version_no` > 0", name: "tag_versions_version_no_positive"
  end

  create_table "tags", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "tag_name_id", null: false
    t.string "category", default: "general", null: false
    t.datetime "deprecated_at"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.integer "post_count", default: 0, null: false
    t.datetime "discarded_at"
    t.integer "version_no", null: false
    t.index ["deprecated_at"], name: "index_tags_on_deprecated_at"
    t.index ["discarded_at"], name: "index_tags_on_discarded_at"
    t.index ["tag_name_id"], name: "index_tags_on_tag_name_id", unique: true
    t.check_constraint "(`deprecated_at` is null) or (`category` <> _utf8mb4'nico')", name: "chk_tags_deprecated_at_not_nico"
    t.check_constraint "`version_no` > 0", name: "chk_tags_version_no_positive"
  end

  create_table "theatre_comments", primary_key: ["theatre_id", "no"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "theatre_id", null: false
    t.integer "no", null: false
    t.bigint "user_id"
    t.text "content", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "discarded_at"
    t.index ["discarded_at"], name: "index_theatre_comments_on_discarded_at"
    t.index ["theatre_id"], name: "index_theatre_comments_on_theatre_id"
    t.index ["user_id"], name: "index_theatre_comments_on_user_id"
  end

  create_table "theatre_programmes", primary_key: ["theatre_id", "position"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "theatre_id", null: false
    t.integer "position", null: false
    t.bigint "post_id", null: false
    t.datetime "created_at", null: false
    t.index ["post_id"], name: "index_theatre_programmes_on_post_id"
    t.index ["theatre_id"], name: "index_theatre_programmes_on_theatre_id"
  end

  create_table "theatre_skip_event_tags", primary_key: ["theatre_skip_event_id", "tag_id"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "theatre_skip_event_id", null: false
    t.bigint "tag_id", null: false
    t.index ["tag_id", "theatre_skip_event_id"], name: "idx_theatre_skip_event_tags_tag_event"
    t.index ["tag_id"], name: "index_theatre_skip_event_tags_on_tag_id"
    t.index ["theatre_skip_event_id"], name: "index_theatre_skip_event_tags_on_theatre_skip_event_id"
  end

  create_table "theatre_skip_event_voters", primary_key: ["theatre_skip_event_id", "user_id"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "theatre_skip_event_id", null: false
    t.bigint "user_id", null: false
    t.index ["theatre_skip_event_id"], name: "index_theatre_skip_event_voters_on_theatre_skip_event_id"
    t.index ["user_id", "theatre_skip_event_id"], name: "idx_theatre_skip_event_voters_user_event"
    t.index ["user_id"], name: "index_theatre_skip_event_voters_on_user_id"
  end

  create_table "theatre_skip_events", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "theatre_id", null: false
    t.bigint "post_id", null: false
    t.bigint "skipped_by_user_id", null: false
    t.integer "programme_position"
    t.datetime "created_at", null: false
    t.index ["post_id"], name: "index_theatre_skip_events_on_post_id"
    t.index ["skipped_by_user_id"], name: "index_theatre_skip_events_on_skipped_by_user_id"
    t.index ["theatre_id", "created_at"], name: "index_theatre_skip_events_on_theatre_id_and_created_at"
    t.index ["theatre_id"], name: "index_theatre_skip_events_on_theatre_id"
  end

  create_table "theatre_skip_votes", primary_key: ["theatre_id", "post_id", "user_id"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "theatre_id", null: false
    t.bigint "post_id", null: false
    t.bigint "user_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["post_id"], name: "index_theatre_skip_votes_on_post_id"
    t.index ["theatre_id", "post_id", "created_at"], name: "idx_theatre_skip_votes_theatre_post_created"
    t.index ["theatre_id"], name: "index_theatre_skip_votes_on_theatre_id"
    t.index ["user_id"], name: "index_theatre_skip_votes_on_user_id"
  end

  create_table "theatre_watching_users", primary_key: ["theatre_id", "user_id"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "theatre_id", null: false
    t.bigint "user_id", null: false
    t.datetime "expires_at", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["expires_at"], name: "index_theatre_watching_users_on_expires_at"
    t.index ["theatre_id", "expires_at"], name: "index_theatre_watching_users_on_theatre_id_and_expires_at"
    t.index ["theatre_id"], name: "index_theatre_watching_users_on_theatre_id"
    t.index ["user_id"], name: "index_theatre_watching_users_on_user_id"
  end

  create_table "theatres", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "name"
    t.datetime "opens_at", null: false
    t.datetime "closes_at"
    t.integer "kind", null: false
    t.bigint "current_post_id"
    t.datetime "current_post_started_at"
    t.integer "next_comment_no", default: 1, null: false
    t.bigint "host_user_id"
    t.bigint "created_by_user_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "discarded_at"
    t.index ["closes_at"], name: "index_theatres_on_closes_at"
    t.index ["created_by_user_id"], name: "index_theatres_on_created_by_user_id"
    t.index ["current_post_id"], name: "index_theatres_on_current_post_id"
    t.index ["discarded_at"], name: "index_theatres_on_discarded_at"
    t.index ["host_user_id"], name: "index_theatres_on_host_user_id"
    t.index ["kind"], name: "index_theatres_on_kind"
    t.index ["opens_at"], name: "index_theatres_on_opens_at"
  end

  create_table "user_ips", primary_key: ["user_id", "ip_address_id"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "user_id", null: false
    t.bigint "ip_address_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["ip_address_id"], name: "index_user_ips_on_ip_address_id"
  end

  create_table "user_post_views", primary_key: ["user_id", "post_id"], charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "user_id", null: false
    t.bigint "post_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["post_id"], name: "index_user_post_views_on_post_id"
  end

  create_table "user_theme_slots", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "user_id", null: false
    t.string "base_theme", null: false
    t.integer "slot_no", null: false
    t.json "tokens", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["user_id", "base_theme", "slot_no"], name: "index_user_theme_slots_on_user_theme_and_slot", unique: true
    t.index ["user_id"], name: "index_user_theme_slots_on_user_id"
    t.check_constraint "`base_theme` in (_utf8mb4'light',_utf8mb4'dark')", name: "user_theme_slots_base_theme_valid"
    t.check_constraint "`slot_no` between 1 and 3", name: "user_theme_slots_slot_no_valid"
  end

  create_table "users", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "name"
    t.string "inheritance_code", limit: 64, null: false
    t.string "role", null: false
    t.datetime "banned_at"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["banned_at"], name: "index_users_on_banned_at"
  end

  create_table "wiki_lines", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.string "sha256", limit: 64, null: false
    t.text "body", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["sha256"], name: "index_wiki_lines_on_sha256", unique: true
  end

  create_table "wiki_pages", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "tag_name_id", null: false
    t.text "body", null: false
    t.bigint "created_user_id", null: false
    t.bigint "updated_user_id", null: false
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.datetime "discarded_at"
    t.integer "next_asset_no", default: 1, null: false
    t.integer "version_no", null: false
    t.index ["created_user_id"], name: "index_wiki_pages_on_created_user_id"
    t.index ["discarded_at"], name: "index_wiki_pages_on_discarded_at"
    t.index ["tag_name_id"], name: "index_wiki_pages_on_tag_name_id", unique: true
    t.index ["updated_user_id"], name: "index_wiki_pages_on_updated_user_id"
    t.check_constraint "`version_no` > 0", name: "chk_wiki_pages_version_no_positive"
  end

  create_table "wiki_revision_lines", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "wiki_revision_id", null: false
    t.integer "position", null: false
    t.bigint "wiki_line_id", null: false
    t.index ["wiki_line_id"], name: "index_wiki_revision_lines_on_wiki_line_id"
    t.index ["wiki_revision_id", "position"], name: "index_wiki_revision_lines_on_wiki_revision_id_and_position", unique: true
    t.index ["wiki_revision_id", "wiki_line_id"], name: "index_wiki_revision_lines_on_wiki_revision_id_and_wiki_line_id"
    t.index ["wiki_revision_id"], name: "index_wiki_revision_lines_on_wiki_revision_id"
  end

  create_table "wiki_revisions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "wiki_page_id", null: false
    t.bigint "base_revision_id"
    t.bigint "created_user_id", null: false
    t.integer "kind", default: 0, null: false
    t.bigint "redirect_page_id"
    t.string "message"
    t.integer "lines_count", default: 0, null: false
    t.string "tree_sha256", limit: 64
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["base_revision_id"], name: "index_wiki_revisions_on_base_revision_id"
    t.index ["created_user_id"], name: "index_wiki_revisions_on_created_user_id"
    t.index ["kind"], name: "index_wiki_revisions_on_kind"
    t.index ["redirect_page_id"], name: "index_wiki_revisions_on_redirect_page_id"
    t.index ["tree_sha256"], name: "index_wiki_revisions_on_tree_sha256"
    t.index ["wiki_page_id", "id"], name: "index_wiki_revisions_on_wiki_page_id_and_id"
    t.index ["wiki_page_id"], name: "index_wiki_revisions_on_wiki_page_id"
  end

  create_table "wiki_versions", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t|
    t.bigint "wiki_page_id", null: false
    t.integer "version_no", null: false
    t.string "event_type", null: false
    t.string "title", null: false
    t.text "body", null: false
    t.text "reason"
    t.datetime "created_at", null: false
    t.bigint "created_by_user_id"
    t.index ["created_by_user_id"], name: "index_wiki_versions_on_created_by_user_id"
    t.index ["wiki_page_id", "version_no"], name: "index_wiki_versions_on_wiki_page_id_and_version_no", unique: true
    t.index ["wiki_page_id"], name: "index_wiki_versions_on_wiki_page_id"
    t.check_constraint "`event_type` in (_utf8mb4'create',_utf8mb4'update',_utf8mb4'discard',_utf8mb4'restore')", name: "wiki_versions_event_type_valid"
    t.check_constraint "`version_no` > 0", name: "wiki_versions_version_no_positive"
  end

  add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id"
  add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id"
  add_foreign_key "gekanator_ai_runs", "gekanator_question_suggestions"
  add_foreign_key "gekanator_games", "posts", column: "correct_post_id"
  add_foreign_key "gekanator_games", "posts", column: "guessed_post_id"
  add_foreign_key "gekanator_games", "users"
  add_foreign_key "gekanator_question_examples", "gekanator_games"
  add_foreign_key "gekanator_question_examples", "gekanator_questions"
  add_foreign_key "gekanator_question_examples", "posts"
  add_foreign_key "gekanator_question_examples", "users"
  add_foreign_key "gekanator_question_suggestions", "gekanator_games", on_delete: :cascade
  add_foreign_key "gekanator_question_suggestions", "users"
  add_foreign_key "gekanator_questions", "gekanator_question_suggestions"
  add_foreign_key "gekanator_questions", "users", column: "created_by_id"
  add_foreign_key "material_export_items", "materials"
  add_foreign_key "material_export_items", "users", column: "created_by_user_id"
  add_foreign_key "material_import_blocks", "users", column: "created_by_user_id"
  add_foreign_key "material_sync_sources", "users", column: "created_by_user_id"
  add_foreign_key "material_sync_sources", "users", column: "updated_by_user_id"
  add_foreign_key "material_sync_suppressions", "users", column: "created_by_user_id"
  add_foreign_key "material_versions", "materials"
  add_foreign_key "material_versions", "materials", column: "parent_id"
  add_foreign_key "material_versions", "tags"
  add_foreign_key "material_versions", "users", column: "created_by_user_id"
  add_foreign_key "material_versions", "users", column: "updated_by_user_id"
  add_foreign_key "materials", "materials", column: "parent_id"
  add_foreign_key "materials", "tags"
  add_foreign_key "materials", "users", column: "created_by_user_id"
  add_foreign_key "materials", "users", column: "updated_by_user_id"
  add_foreign_key "nico_tag_relations", "tags"
  add_foreign_key "nico_tag_relations", "tags", column: "nico_tag_id"
  add_foreign_key "nico_tag_versions", "tags"
  add_foreign_key "nico_tag_versions", "users", column: "created_by_user_id"
  add_foreign_key "post_implications", "posts"
  add_foreign_key "post_implications", "posts", column: "parent_post_id"
  add_foreign_key "post_similarities", "posts"
  add_foreign_key "post_similarities", "posts", column: "target_post_id"
  add_foreign_key "post_tag_sections", "posts"
  add_foreign_key "post_tag_sections", "tags"
  add_foreign_key "post_tags", "posts"
  add_foreign_key "post_tags", "tags"
  add_foreign_key "post_tags", "users", column: "created_user_id"
  add_foreign_key "post_tags", "users", column: "deleted_user_id"
  add_foreign_key "post_versions", "posts"
  add_foreign_key "post_versions", "users", column: "created_by_user_id"
  add_foreign_key "posts", "users", column: "uploaded_user_id"
  add_foreign_key "settings", "users"
  add_foreign_key "tag_implications", "tags"
  add_foreign_key "tag_implications", "tags", column: "parent_tag_id"
  add_foreign_key "tag_names", "tag_names", column: "canonical_id"
  add_foreign_key "tag_similarities", "tags"
  add_foreign_key "tag_similarities", "tags", column: "target_tag_id"
  add_foreign_key "tag_versions", "tags"
  add_foreign_key "tag_versions", "users", column: "created_by_user_id"
  add_foreign_key "tags", "tag_names"
  add_foreign_key "theatre_comments", "theatres"
  add_foreign_key "theatre_comments", "users"
  add_foreign_key "theatre_programmes", "posts"
  add_foreign_key "theatre_programmes", "theatres"
  add_foreign_key "theatre_skip_event_tags", "tags"
  add_foreign_key "theatre_skip_event_tags", "theatre_skip_events"
  add_foreign_key "theatre_skip_event_voters", "theatre_skip_events"
  add_foreign_key "theatre_skip_event_voters", "users"
  add_foreign_key "theatre_skip_events", "posts"
  add_foreign_key "theatre_skip_events", "theatres"
  add_foreign_key "theatre_skip_events", "users", column: "skipped_by_user_id"
  add_foreign_key "theatre_skip_votes", "posts"
  add_foreign_key "theatre_skip_votes", "theatres"
  add_foreign_key "theatre_skip_votes", "users"
  add_foreign_key "theatre_watching_users", "theatres"
  add_foreign_key "theatre_watching_users", "users"
  add_foreign_key "theatres", "posts", column: "current_post_id"
  add_foreign_key "theatres", "users", column: "created_by_user_id"
  add_foreign_key "theatres", "users", column: "host_user_id"
  add_foreign_key "user_ips", "ip_addresses"
  add_foreign_key "user_ips", "users"
  add_foreign_key "user_post_views", "posts"
  add_foreign_key "user_post_views", "users"
  add_foreign_key "user_theme_slots", "users"
  add_foreign_key "wiki_assets", "users", column: "created_by_user_id"
  add_foreign_key "wiki_assets", "wiki_pages"
  add_foreign_key "wiki_pages", "tag_names"
  add_foreign_key "wiki_pages", "users", column: "created_user_id"
  add_foreign_key "wiki_pages", "users", column: "updated_user_id"
  add_foreign_key "wiki_revision_lines", "wiki_lines"
  add_foreign_key "wiki_revision_lines", "wiki_revisions"
  add_foreign_key "wiki_revisions", "users", column: "created_user_id"
  add_foreign_key "wiki_revisions", "wiki_pages"
  add_foreign_key "wiki_revisions", "wiki_pages", column: "redirect_page_id"
  add_foreign_key "wiki_revisions", "wiki_revisions", column: "base_revision_id"
  add_foreign_key "wiki_versions", "users", column: "created_by_user_id"
  add_foreign_key "wiki_versions", "wiki_pages"
end
