CREATE TABLE IF NOT EXISTS h5_users (
  id CHAR(36) PRIMARY KEY,
  username VARCHAR(64) NOT NULL UNIQUE,
  slug VARCHAR(64) NULL,
  email VARCHAR(255) NULL,
  display_name VARCHAR(128) NOT NULL,
  salt VARCHAR(64) NOT NULL,
  password_hash VARCHAR(128) NOT NULL,
  password_algorithm VARCHAR(32) NOT NULL DEFAULT 'pbkdf2-sha512',
  role ENUM('user', 'admin') NOT NULL DEFAULT 'user',
  status ENUM('active', 'suspended', 'disabled') NOT NULL DEFAULT 'active',
  plan_type VARCHAR(32) NOT NULL DEFAULT 'free',
  workspace_root VARCHAR(512) NOT NULL,
  low_balance_gift_eligible TINYINT(1) NOT NULL DEFAULT 0,
  low_balance_gift_granted_at BIGINT NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_users_slug (slug),
  UNIQUE KEY uq_h5_users_email (email)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_user_spaces (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  space_name VARCHAR(128) NOT NULL DEFAULT '我的空间',
  quota_bytes BIGINT NOT NULL DEFAULT 5242880,
  used_bytes BIGINT NOT NULL DEFAULT 0,
  reserved_bytes BIGINT NOT NULL DEFAULT 0,
  status ENUM('active', 'locked', 'deleted') NOT NULL DEFAULT 'active',
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_user_space_user (user_id),
  CONSTRAINT fk_h5_space_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT chk_h5_space_quota CHECK (
    quota_bytes >= 0 AND used_bytes >= 0 AND reserved_bytes >= 0
  )
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS mindspace_config (
  `key` VARCHAR(64) PRIMARY KEY,
  value TEXT NOT NULL,
  description VARCHAR(255) NULL,
  updated_at BIGINT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_space_categories (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  space_id CHAR(36) NOT NULL,
  category_code VARCHAR(32) NOT NULL,
  category_name VARCHAR(128) NOT NULL,
  visibility_policy VARCHAR(64) NOT NULL,
  ai_access_policy VARCHAR(64) NOT NULL,
  publish_policy VARCHAR(64) NOT NULL,
  is_system TINYINT(1) NOT NULL DEFAULT 0,
  sort_order INT NOT NULL DEFAULT 0,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_space_category_code (space_id, category_code),
  KEY idx_h5_space_category_user (user_id, sort_order),
  CONSTRAINT fk_h5_category_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_category_space FOREIGN KEY (space_id) REFERENCES h5_user_spaces(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_assets (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  space_id CHAR(36) NOT NULL,
  category_id CHAR(36) NOT NULL,
  parent_id CHAR(36) NULL,
  asset_type VARCHAR(32) NOT NULL,
  mime_type VARCHAR(128) NOT NULL,
  original_filename VARCHAR(255) NOT NULL,
  display_name VARCHAR(255) NOT NULL,
  workspace_relative_path VARCHAR(512) NULL,
  current_version_id CHAR(36) NULL,
  size_bytes BIGINT NOT NULL DEFAULT 0,
  checksum CHAR(64) NOT NULL,
  risk_level ENUM('none', 'low', 'medium', 'high', 'critical') NOT NULL DEFAULT 'none',
  visibility ENUM('private', 'internal', 'public_candidate') NOT NULL DEFAULT 'private',
  status ENUM('uploaded', 'processing', 'ready', 'quarantined', 'archived', 'deleted') NOT NULL DEFAULT 'ready',
  source_type ENUM('upload', 'chat', 'agent', 'template', 'generated', 'workspace') NOT NULL DEFAULT 'upload',
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  deleted_at BIGINT NULL,
  KEY idx_h5_assets_category (user_id, category_id, updated_at),
  KEY idx_h5_assets_parent (user_id, parent_id),
  KEY idx_h5_assets_checksum (user_id, checksum),
  KEY idx_h5_assets_workspace_path (user_id, workspace_relative_path, updated_at),
  CONSTRAINT fk_h5_asset_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_asset_space FOREIGN KEY (space_id) REFERENCES h5_user_spaces(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_asset_category FOREIGN KEY (category_id) REFERENCES h5_space_categories(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_asset_parent FOREIGN KEY (parent_id) REFERENCES h5_assets(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_asset_versions (
  id CHAR(36) PRIMARY KEY,
  asset_id CHAR(36) NOT NULL,
  version_no INT NOT NULL,
  storage_key VARCHAR(512) NOT NULL,
  size_bytes BIGINT NOT NULL,
  checksum CHAR(64) NOT NULL,
  mime_type VARCHAR(128) NOT NULL,
  created_by CHAR(36) NOT NULL,
  change_note VARCHAR(512) NULL,
  scan_status ENUM('pending', 'passed', 'warned', 'blocked') NOT NULL DEFAULT 'pending',
  created_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_asset_version (asset_id, version_no),
  UNIQUE KEY uq_h5_asset_storage_key (storage_key),
  CONSTRAINT fk_h5_asset_version_asset FOREIGN KEY (asset_id) REFERENCES h5_assets(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_asset_version_user FOREIGN KEY (created_by) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_upload_sessions (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  space_id CHAR(36) NOT NULL,
  category_id CHAR(36) NOT NULL,
  filename VARCHAR(255) NOT NULL,
  expected_size BIGINT NOT NULL,
  actual_size BIGINT NULL,
  declared_mime_type VARCHAR(128) NULL,
  detected_mime_type VARCHAR(128) NULL,
  reserved_bytes BIGINT NOT NULL,
  temporary_storage_key VARCHAR(512) NOT NULL,
  checksum CHAR(64) NULL,
  completed_asset_id CHAR(36) NULL,
  source_session_id VARCHAR(128) NULL,
  source_message_id VARCHAR(128) NULL,
  status ENUM('reserved', 'uploaded', 'completed', 'cancelled', 'expired', 'failed') NOT NULL DEFAULT 'reserved',
  expires_at BIGINT NOT NULL,
  created_at BIGINT NOT NULL,
  completed_at BIGINT NULL,
  KEY idx_h5_upload_user_status (user_id, status, expires_at),
  KEY idx_h5_upload_source_session (user_id, source_session_id),
  CONSTRAINT fk_h5_upload_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_upload_space FOREIGN KEY (space_id) REFERENCES h5_user_spaces(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_upload_category FOREIGN KEY (category_id) REFERENCES h5_space_categories(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_page_records (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  space_id CHAR(36) NOT NULL,
  category_id CHAR(36) NOT NULL,
  source_session_id VARCHAR(128) NULL,
  source_message_id VARCHAR(128) NULL,
  source_asset_id CHAR(36) NULL,
  title VARCHAR(255) NOT NULL,
  summary VARCHAR(1000) NULL,
  cover_image_asset_id CHAR(36) NULL,
  page_type VARCHAR(32) NOT NULL DEFAULT 'article',
  template_id VARCHAR(64) NOT NULL DEFAULT 'editorial',
  draft_content_ref VARCHAR(512) NULL,
  workspace_relative_path VARCHAR(512) NULL,
  current_version_id CHAR(36) NULL,
  current_publish_id CHAR(36) NULL,
  status ENUM('draft', 'reviewing', 'risk_found', 'ready', 'published', 'protected', 'expired', 'offline', 'deleted') NOT NULL DEFAULT 'draft',
  visibility ENUM('private', 'internal', 'public') NOT NULL DEFAULT 'private',
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  deleted_at BIGINT NULL,
  KEY idx_h5_pages_user_status (user_id, status, updated_at),
  KEY idx_h5_pages_category (user_id, category_id, updated_at),
  KEY idx_h5_pages_source_session (user_id, source_session_id),
  KEY idx_h5_pages_workspace_path (user_id, workspace_relative_path, updated_at),
  CONSTRAINT fk_h5_page_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_page_space FOREIGN KEY (space_id) REFERENCES h5_user_spaces(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_page_category FOREIGN KEY (category_id) REFERENCES h5_space_categories(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_page_source_asset FOREIGN KEY (source_asset_id) REFERENCES h5_assets(id) ON DELETE SET NULL,
  CONSTRAINT fk_h5_page_cover_asset FOREIGN KEY (cover_image_asset_id) REFERENCES h5_assets(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Delivery contracts are separate from page/publication lifecycle records.
-- A contract gates only new agent-delivered workspace files; historical public
-- files without a contract remain accessible for backward compatibility.
CREATE TABLE IF NOT EXISTS h5_page_delivery_contracts (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  request_id VARCHAR(128) NOT NULL,
  workspace_relative_path VARCHAR(512) NOT NULL,
  data_mode ENUM('static', 'pg_required') NOT NULL DEFAULT 'static',
  status ENUM('preparing', 'ready', 'failed') NOT NULL DEFAULT 'preparing',
  failure_reason VARCHAR(1000) NULL,
  created_at BIGINT NOT NULL,
  ready_at BIGINT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_delivery_contract_request_path (user_id, request_id, workspace_relative_path),
  KEY idx_h5_delivery_contract_route (user_id, workspace_relative_path, updated_at),
  CONSTRAINT fk_h5_delivery_contract_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_page_versions (
  id CHAR(36) PRIMARY KEY,
  page_id CHAR(36) NOT NULL,
  version_no INT NOT NULL,
  content_asset_id CHAR(36) NOT NULL,
  bundle_asset_id CHAR(36) NULL,
  source_snapshot_json JSON NULL,
  security_scan_id CHAR(36) NULL,
  created_by CHAR(36) NOT NULL,
  change_note VARCHAR(512) NULL,
  immutable TINYINT(1) NOT NULL DEFAULT 0,
  created_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_page_version (page_id, version_no),
  CONSTRAINT fk_h5_page_version_page FOREIGN KEY (page_id) REFERENCES h5_page_records(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_page_version_content FOREIGN KEY (content_asset_id) REFERENCES h5_assets(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_page_version_bundle FOREIGN KEY (bundle_asset_id) REFERENCES h5_assets(id) ON DELETE SET NULL,
  CONSTRAINT fk_h5_page_version_user FOREIGN KEY (created_by) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_security_scans (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  target_type ENUM('asset', 'page_version', 'publication_bundle') NOT NULL,
  target_id CHAR(36) NOT NULL,
  scanner_version VARCHAR(64) NOT NULL,
  status ENUM('queued', 'running', 'passed', 'warned', 'blocked', 'failed') NOT NULL,
  risk_level ENUM('none', 'low', 'medium', 'high', 'critical') NOT NULL DEFAULT 'none',
  findings_count INT NOT NULL DEFAULT 0,
  summary_json JSON NULL,
  started_at BIGINT NOT NULL,
  completed_at BIGINT NULL,
  KEY idx_h5_security_scan_target (user_id, target_type, target_id, completed_at),
  CONSTRAINT fk_h5_security_scan_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_security_findings (
  id CHAR(36) PRIMARY KEY,
  scan_id CHAR(36) NOT NULL,
  finding_type VARCHAR(64) NOT NULL,
  risk_level ENUM('low', 'medium', 'high', 'critical') NOT NULL,
  occurrence_count INT NOT NULL DEFAULT 1,
  sample_masked VARCHAR(255) NULL,
  blocking TINYINT(1) NOT NULL DEFAULT 0,
  created_at BIGINT NOT NULL,
  KEY idx_h5_security_finding_scan (scan_id, risk_level),
  CONSTRAINT fk_h5_security_finding_scan FOREIGN KEY (scan_id) REFERENCES h5_security_scans(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_publish_records (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  page_id CHAR(36) NOT NULL,
  page_version_id CHAR(36) NOT NULL,
  publish_type ENUM('page', 'share') NOT NULL DEFAULT 'page',
  url_slug VARCHAR(64) NOT NULL,
  public_url VARCHAR(512) NOT NULL,
  access_mode ENUM('public', 'password', 'private_link', 'time_limited', 'login_required', 'owner_only') NOT NULL,
  password_hash VARCHAR(255) NULL,
  token_hash CHAR(64) NULL,
  token_prefix VARCHAR(16) NULL,
  expires_at BIGINT NULL,
  user_confirmed_at BIGINT NULL,
  published_at BIGINT NOT NULL,
  offline_at BIGINT NULL,
  status ENUM('draft', 'online', 'expired', 'offline', 'blocked') NOT NULL DEFAULT 'online',
  view_count BIGINT NOT NULL DEFAULT 0,
  security_scan_id CHAR(36) NOT NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  KEY idx_h5_publish_route (url_slug, status, published_at),
  KEY idx_h5_publish_page (user_id, page_id, published_at),
  KEY idx_h5_publish_auto_private (access_mode, status, user_confirmed_at, expires_at),
  UNIQUE KEY uq_h5_publish_token_hash (token_hash),
  CONSTRAINT fk_h5_publish_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_publish_page FOREIGN KEY (page_id) REFERENCES h5_page_records(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_publish_page_version FOREIGN KEY (page_version_id) REFERENCES h5_page_versions(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_publish_scan FOREIGN KEY (security_scan_id) REFERENCES h5_security_scans(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_page_data_policy_index (
  page_id CHAR(36) PRIMARY KEY,
  owner_user_id CHAR(36) NOT NULL,
  access_mode VARCHAR(32) NOT NULL,
  dataset_count INT UNSIGNED NOT NULL DEFAULT 0,
  scope_hash CHAR(16) NOT NULL,
  updated_at BIGINT NOT NULL,
  KEY idx_h5_page_data_policy_owner (owner_user_id, updated_at),
  CONSTRAINT fk_h5_page_data_policy_owner FOREIGN KEY (owner_user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_publication_asset_refs (
  publication_id CHAR(36) NOT NULL,
  asset_id CHAR(36) NOT NULL,
  created_at BIGINT NOT NULL,
  PRIMARY KEY (publication_id, asset_id),
  KEY idx_asset_id (asset_id),
  KEY idx_created_at (created_at),
  CONSTRAINT fk_h5_pub_asset_ref_pub FOREIGN KEY (publication_id) REFERENCES h5_publish_records(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_pub_asset_ref_asset FOREIGN KEY (asset_id) REFERENCES h5_assets(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_publication_events (
  id CHAR(36) PRIMARY KEY,
  publish_id CHAR(36) NOT NULL,
  event_type ENUM('published', 'republished', 'settings_changed', 'expired', 'offlined', 'blocked') NOT NULL,
  actor_id CHAR(36) NOT NULL,
  old_page_version_id CHAR(36) NULL,
  new_page_version_id CHAR(36) NULL,
  access_mode VARCHAR(32) NOT NULL,
  detail_json JSON NULL,
  created_at BIGINT NOT NULL,
  KEY idx_h5_publication_event (publish_id, created_at),
  CONSTRAINT fk_h5_publication_event_publish FOREIGN KEY (publish_id) REFERENCES h5_publish_records(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_publication_event_actor FOREIGN KEY (actor_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_publication_views (
  id CHAR(36) PRIMARY KEY,
  publish_id CHAR(36) NOT NULL,
  viewer_user_id CHAR(36) NULL,
  referrer_host VARCHAR(255) NULL,
  device_type ENUM('desktop', 'mobile', 'tablet', 'bot', 'unknown') NOT NULL,
  viewed_at BIGINT NOT NULL,
  KEY idx_h5_publication_view_time (publish_id, viewed_at),
  KEY idx_h5_publication_viewer (viewer_user_id, viewed_at),
  CONSTRAINT fk_h5_publication_view_publish FOREIGN KEY (publish_id) REFERENCES h5_publish_records(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_publication_view_user FOREIGN KEY (viewer_user_id) REFERENCES h5_users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_agent_jobs (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  session_id VARCHAR(128) NULL,
  job_type VARCHAR(64) NOT NULL,
  instruction TEXT NOT NULL,
  permission_scope JSON NULL,
  user_context_json JSON NULL,
  output_category_id CHAR(36) NOT NULL,
  output_type VARCHAR(32) NOT NULL,
  max_output_bytes BIGINT NOT NULL DEFAULT 2097152,
  status ENUM('queued', 'running', 'completed', 'failed', 'cancelled', 'timed_out') NOT NULL DEFAULT 'queued',
  idempotency_key VARCHAR(128) NULL,
  progress_json JSON NULL,
  job_token_hash CHAR(64) NULL,
  result_page_id CHAR(36) NULL,
  result_asset_id CHAR(36) NULL,
  error_code VARCHAR(64) NULL,
  error_message VARCHAR(1000) NULL,
  queued_at BIGINT NOT NULL,
  started_at BIGINT NULL,
  heartbeat_at BIGINT NULL,
  completed_at BIGINT NULL,
  expires_at BIGINT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_agent_job_idempotency (user_id, idempotency_key),
  KEY idx_h5_agent_job_user_status (user_id, status, queued_at),
  KEY idx_h5_agent_job_token (job_token_hash),
  CONSTRAINT fk_h5_agent_job_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_agent_job_output_category FOREIGN KEY (output_category_id) REFERENCES h5_space_categories(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_agent_job_result_page FOREIGN KEY (result_page_id) REFERENCES h5_page_records(id) ON DELETE SET NULL,
  CONSTRAINT fk_h5_agent_job_result_asset FOREIGN KEY (result_asset_id) REFERENCES h5_assets(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_agent_job_assets (
  id CHAR(36) PRIMARY KEY,
  job_id CHAR(36) NOT NULL,
  asset_id CHAR(36) NOT NULL,
  asset_version_id CHAR(36) NOT NULL,
  permission ENUM('read', 'write', 'create_derivative') NOT NULL DEFAULT 'read',
  created_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_agent_job_asset (job_id, asset_id, asset_version_id),
  KEY idx_h5_agent_job_asset_job (job_id, created_at),
  CONSTRAINT fk_h5_agent_job_asset_job FOREIGN KEY (job_id) REFERENCES h5_agent_jobs(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_agent_job_asset_asset FOREIGN KEY (asset_id) REFERENCES h5_assets(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_agent_job_asset_version FOREIGN KEY (asset_version_id) REFERENCES h5_asset_versions(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_user_wallets (
  user_id CHAR(36) PRIMARY KEY,
  balance_cents BIGINT NOT NULL DEFAULT 0,
  tokens_used BIGINT NOT NULL DEFAULT 0,
  updated_at BIGINT NOT NULL,
  CONSTRAINT fk_h5_wallet_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_user_path_grants (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  path VARCHAR(512) NOT NULL,
  mode ENUM('read', 'readwrite') NOT NULL DEFAULT 'readwrite',
  UNIQUE KEY uq_h5_user_path (user_id, path),
  CONSTRAINT fk_h5_path_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_user_sessions (
  agent_session_id VARCHAR(128) NOT NULL PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  goosed_node TINYINT UNSIGNED NOT NULL DEFAULT 0,
  goosed_target VARCHAR(255) NULL,
  origin ENUM('h5', 'wechat') NOT NULL DEFAULT 'h5',
  created_at BIGINT NOT NULL,
  KEY idx_h5_user_sessions_user (user_id),
  CONSTRAINT fk_h5_session_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_agent_runs (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  agent_session_id VARCHAR(128) NULL,
  request_id VARCHAR(128) NOT NULL,
  status ENUM('queued', 'running', 'retryable', 'succeeded', 'failed') NOT NULL DEFAULT 'queued',
  attempts INT NOT NULL DEFAULT 0,
  user_message_json LONGTEXT NOT NULL,
  error_message TEXT NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  started_at BIGINT NULL,
  completed_at BIGINT NULL,
  UNIQUE KEY uq_h5_agent_run_request (user_id, request_id),
  KEY idx_h5_agent_run_user_status (user_id, status, updated_at),
  KEY idx_h5_agent_run_session (agent_session_id, updated_at),
  CONSTRAINT fk_h5_agent_run_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_agent_run_session FOREIGN KEY (agent_session_id) REFERENCES h5_user_sessions(agent_session_id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_agent_run_events (
  id CHAR(36) PRIMARY KEY,
  run_id CHAR(36) NOT NULL,
  event_type VARCHAR(64) NOT NULL,
  data_json JSON NULL,
  created_at BIGINT NOT NULL,
  KEY idx_h5_agent_run_event_run (run_id, created_at),
  CONSTRAINT fk_h5_agent_run_event_run FOREIGN KEY (run_id) REFERENCES h5_agent_runs(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Patch 4c session SSE replay cursor store (rollback: DROP TABLE h5_session_stream_events).
CREATE TABLE IF NOT EXISTS h5_session_stream_events (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  agent_session_id VARCHAR(128) NOT NULL,
  event_type VARCHAR(64) NOT NULL,
  payload_json LONGTEXT NOT NULL,
  upstream_event_id VARCHAR(255) NULL,
  created_at BIGINT NOT NULL,
  KEY idx_h5_session_stream_session (agent_session_id, created_at, id),
  KEY idx_h5_session_stream_user (user_id, created_at),
  CONSTRAINT fk_h5_session_stream_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_conversation_packages (
  id VARCHAR(64) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  session_id VARCHAR(128) NOT NULL,
  title VARCHAR(255) NULL,
  status ENUM('active', 'archived', 'deleted') NOT NULL DEFAULT 'active',
  storage_prefix VARCHAR(512) NULL,
  manifest_asset_id CHAR(36) NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_conversation_package_session (user_id, session_id),
  KEY idx_h5_conversation_package_user_updated (user_id, updated_at),
  CONSTRAINT fk_h5_conversation_package_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_conversation_package_manifest FOREIGN KEY (manifest_asset_id) REFERENCES h5_assets(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_conversation_artifacts (
  id VARCHAR(64) PRIMARY KEY,
  package_id VARCHAR(64) NOT NULL,
  asset_id CHAR(36) NULL,
  page_id CHAR(36) NULL,
  publication_id CHAR(36) NULL,
  agent_run_id CHAR(36) NULL,
  message_id VARCHAR(128) NULL,
  role VARCHAR(32) NULL,
  artifact_kind VARCHAR(64) NOT NULL,
  display_name VARCHAR(255) NULL,
  mime_type VARCHAR(128) NULL,
  size_bytes BIGINT NULL,
  storage_key VARCHAR(512) NULL,
  canonical_url VARCHAR(512) NULL,
  sort_order INT NOT NULL DEFAULT 0,
  created_at BIGINT NOT NULL,
  KEY idx_h5_conversation_artifact_package (package_id, sort_order, created_at),
  KEY idx_h5_conversation_artifact_asset (asset_id),
  KEY idx_h5_conversation_artifact_page (page_id),
  KEY idx_h5_conversation_artifact_publication (publication_id),
  KEY idx_h5_conversation_artifact_run (agent_run_id),
  KEY idx_h5_conversation_artifact_message (message_id),
  CONSTRAINT fk_h5_conversation_artifact_package FOREIGN KEY (package_id) REFERENCES h5_conversation_packages(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_conversation_artifact_asset FOREIGN KEY (asset_id) REFERENCES h5_assets(id) ON DELETE SET NULL,
  CONSTRAINT fk_h5_conversation_artifact_page FOREIGN KEY (page_id) REFERENCES h5_page_records(id) ON DELETE SET NULL,
  CONSTRAINT fk_h5_conversation_artifact_publication FOREIGN KEY (publication_id) REFERENCES h5_publish_records(id) ON DELETE SET NULL,
  CONSTRAINT fk_h5_conversation_artifact_run FOREIGN KEY (agent_run_id) REFERENCES h5_agent_runs(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_session_billing_state (
  agent_session_id VARCHAR(128) NOT NULL PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  last_accumulated_cost DOUBLE NULL,
  last_input_tokens BIGINT NOT NULL DEFAULT 0,
  last_output_tokens BIGINT NOT NULL DEFAULT 0,
  updated_at BIGINT NOT NULL,
  CONSTRAINT fk_h5_billing_state_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_usage_records (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  agent_session_id VARCHAR(128) NOT NULL,
  request_id VARCHAR(128) NULL,
  input_tokens INT NOT NULL DEFAULT 0,
  output_tokens INT NOT NULL DEFAULT 0,
  cost_cents BIGINT NOT NULL,
  balance_after_cents BIGINT NOT NULL,
  created_at BIGINT NOT NULL,
  UNIQUE KEY uniq_h5_usage_request_id (request_id),
  KEY idx_h5_usage_user_time (user_id, created_at),
  CONSTRAINT fk_h5_usage_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_user_policies (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  subject_type ENUM('role', 'user') NOT NULL,
  subject_id VARCHAR(64) NOT NULL,
  policy_key VARCHAR(64) NOT NULL,
  policy_value VARCHAR(512) NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_user_policy (subject_type, subject_id, policy_key),
  KEY idx_h5_policy_subject (subject_type, subject_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_user_skill_grants (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  subject_type ENUM('role', 'user') NOT NULL,
  subject_id VARCHAR(64) NOT NULL,
  skill_name VARCHAR(64) NOT NULL,
  enabled TINYINT(1) NOT NULL DEFAULT 0,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_user_skill_grant (subject_type, subject_id, skill_name),
  KEY idx_h5_skill_subject (subject_type, subject_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_capability_grants (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  subject_type ENUM('role', 'user') NOT NULL,
  subject_id VARCHAR(64) NOT NULL,
  capability_key VARCHAR(64) NOT NULL,
  allowed TINYINT(1) NOT NULL DEFAULT 0,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_capability_grant (subject_type, subject_id, capability_key),
  KEY idx_h5_capability_subject (subject_type, subject_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_login_sessions (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  token_hash CHAR(64) NOT NULL,
  expires_at BIGINT NOT NULL,
  created_at BIGINT NOT NULL,
  revoked_at BIGINT NULL,
  UNIQUE KEY uq_h5_login_token_hash (token_hash),
  KEY idx_h5_login_session_user (user_id),
  KEY idx_h5_login_session_expires (expires_at),
  CONSTRAINT fk_h5_login_session_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_mindspace_audit_logs (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  action VARCHAR(64) NOT NULL,
  object_type VARCHAR(32) NOT NULL,
  object_id CHAR(36) NOT NULL,
  ip VARCHAR(64) NULL,
  result VARCHAR(32) NOT NULL,
  risk_level VARCHAR(16) NULL,
  created_at BIGINT NOT NULL,
  KEY idx_h5_mindspace_audit_user_time (user_id, created_at),
  CONSTRAINT fk_h5_mindspace_audit_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_billing_ledger (
  id BIGINT AUTO_INCREMENT PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  type ENUM('recharge', 'deduct', 'refund', 'adjust') NOT NULL,
  amount_cents BIGINT NOT NULL,
  tokens BIGINT NOT NULL DEFAULT 0,
  session_id VARCHAR(128) NULL,
  note VARCHAR(512) NULL,
  operator_id CHAR(36) NULL,
  created_at BIGINT NOT NULL,
  KEY idx_h5_ledger_user (user_id, created_at),
  CONSTRAINT fk_h5_ledger_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_llm_provider_keys (
  id CHAR(36) PRIMARY KEY,
  provider_id VARCHAR(64) NOT NULL,
  provider_kind ENUM('builtin', 'custom') NOT NULL DEFAULT 'builtin',
  api_url VARCHAR(512) NULL,
  base_path VARCHAR(256) NULL,
  models_json TEXT NULL,
  goosed_provider_id VARCHAR(64) NULL,
  engine VARCHAR(32) NOT NULL DEFAULT 'openai',
  relay_provider VARCHAR(64) NULL,
  name VARCHAR(128) NOT NULL,
  api_key_ciphertext TEXT NOT NULL,
  api_key_iv VARCHAR(24) NOT NULL,
  api_key_tag VARCHAR(24) NOT NULL,
  default_model VARCHAR(128) NOT NULL,
  status ENUM('active', 'disabled') NOT NULL DEFAULT 'active',
  is_selected TINYINT(1) NOT NULL DEFAULT 0,
  is_vision_selected TINYINT(1) NOT NULL DEFAULT 0,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_llm_key_name (name),
  KEY idx_h5_llm_key_provider (provider_id),
  KEY idx_h5_llm_key_selected (is_selected),
  KEY idx_h5_llm_key_vision (is_vision_selected)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_llm_executor_bindings (
  id CHAR(36) PRIMARY KEY,
  executor ENUM('goose', 'aider', 'openhands') NOT NULL,
  purpose VARCHAR(32) NOT NULL DEFAULT 'default',
  provider_key_id CHAR(36) NULL,
  model VARCHAR(128) NOT NULL,
  enabled TINYINT(1) NOT NULL DEFAULT 1,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_llm_executor_binding (executor, purpose),
  KEY idx_h5_llm_executor_provider (provider_key_id),
  CONSTRAINT fk_h5_llm_executor_provider FOREIGN KEY (provider_key_id) REFERENCES h5_llm_provider_keys(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_asset_gateway_config (
  config_key VARCHAR(32) PRIMARY KEY,
  enabled TINYINT(1) NOT NULL DEFAULT 0,
  updated_by CHAR(36) NULL,
  updated_at BIGINT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_asset_plugin_configs (
  id CHAR(36) PRIMARY KEY,
  plugin_id VARCHAR(64) NOT NULL,
  enabled TINYINT(1) NOT NULL DEFAULT 0,
  provider VARCHAR(64) NULL,
  llm_provider_key_id CHAR(36) NULL,
  llm_model VARCHAR(128) NULL,
  config_json JSON NULL,
  updated_by CHAR(36) NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_asset_plugin (plugin_id),
  KEY idx_h5_asset_plugin_llm_provider (llm_provider_key_id),
  CONSTRAINT fk_h5_asset_plugin_llm_provider FOREIGN KEY (llm_provider_key_id)
    REFERENCES h5_llm_provider_keys(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_payment_orders (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  amount_cents BIGINT NOT NULL,
  channel ENUM('wechat') NOT NULL DEFAULT 'wechat',
  status ENUM('pending', 'paid', 'failed', 'expired', 'refunded') NOT NULL DEFAULT 'pending',
  pay_mode ENUM('native', 'h5', 'jsapi') NOT NULL DEFAULT 'native',
  out_trade_no VARCHAR(64) NOT NULL,
  provider_txn VARCHAR(128) NULL,
  code_url VARCHAR(512) NULL,
  h5_url VARCHAR(1024) NULL,
  client_ip VARCHAR(64) NULL,
  expire_at BIGINT NOT NULL,
  paid_at BIGINT NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_payment_out_trade_no (out_trade_no),
  KEY idx_h5_payment_user_time (user_id, created_at),
  KEY idx_h5_payment_status_expire (status, expire_at),
  CONSTRAINT fk_h5_payment_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS plaza_categories (
  id CHAR(36) PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  slug VARCHAR(50) NOT NULL,
  icon VARCHAR(10) NOT NULL DEFAULT '',
  description VARCHAR(200) NOT NULL DEFAULT '',
  sort_order INT NOT NULL DEFAULT 0,
  is_active TINYINT(1) NOT NULL DEFAULT 1,
  created_at BIGINT NOT NULL,
  UNIQUE KEY uq_plaza_category_slug (slug),
  KEY idx_plaza_category_sort (is_active, sort_order)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS plaza_posts (
  id CHAR(36) PRIMARY KEY,
  publication_id CHAR(36) NOT NULL,
  user_id CHAR(36) NOT NULL,
  title VARCHAR(200) NOT NULL,
  summary VARCHAR(500) NOT NULL DEFAULT '',
  cover_url VARCHAR(500) NOT NULL DEFAULT '',
  user_slug VARCHAR(100) NOT NULL,
  user_display_name VARCHAR(100) NOT NULL,
  user_avatar_url VARCHAR(500) NOT NULL DEFAULT '',
  category_id CHAR(36) NOT NULL,
  tags JSON NOT NULL,
  status ENUM('pending_review', 'published', 'hidden', 'rejected') NOT NULL DEFAULT 'pending_review',
  view_count INT UNSIGNED NOT NULL DEFAULT 0,
  like_count INT UNSIGNED NOT NULL DEFAULT 0,
  collect_count INT UNSIGNED NOT NULL DEFAULT 0,
  comment_count INT UNSIGNED NOT NULL DEFAULT 0,
  share_count INT UNSIGNED NOT NULL DEFAULT 0,
  hot_score DOUBLE NOT NULL DEFAULT 0,
  hot_updated_at BIGINT NULL,
  allow_comment TINYINT(1) NOT NULL DEFAULT 1,
  published_at BIGINT NOT NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_plaza_post_publication (publication_id),
  KEY idx_plaza_post_user_status (user_id, status),
  KEY idx_plaza_post_category_hot (category_id, status, hot_score),
  KEY idx_plaza_post_published_at (published_at),
  KEY idx_plaza_post_status_hot (status, hot_score),
  CONSTRAINT fk_plaza_post_publication FOREIGN KEY (publication_id) REFERENCES h5_publish_records(id) ON DELETE CASCADE,
  CONSTRAINT fk_plaza_post_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_plaza_post_category FOREIGN KEY (category_id) REFERENCES plaza_categories(id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS plaza_reactions (
  id CHAR(36) PRIMARY KEY,
  post_id CHAR(36) NOT NULL,
  user_id CHAR(36) NOT NULL,
  type ENUM('like', 'collect', 'share') NOT NULL,
  created_at BIGINT NOT NULL,
  UNIQUE KEY uq_plaza_reaction (post_id, user_id, type),
  KEY idx_plaza_reaction_post (post_id, type),
  KEY idx_plaza_reaction_user (user_id, type),
  CONSTRAINT fk_plaza_reaction_post FOREIGN KEY (post_id) REFERENCES plaza_posts(id) ON DELETE CASCADE,
  CONSTRAINT fk_plaza_reaction_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS plaza_comments (
  id CHAR(36) PRIMARY KEY,
  post_id CHAR(36) NOT NULL,
  user_id CHAR(36) NOT NULL,
  parent_id CHAR(36) NULL,
  content VARCHAR(500) NOT NULL,
  status ENUM('visible', 'deleted', 'flagged', 'hidden') NOT NULL DEFAULT 'visible',
  like_count INT UNSIGNED NOT NULL DEFAULT 0,
  reply_count INT UNSIGNED NOT NULL DEFAULT 0,
  deleted_by ENUM('user', 'ops') NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  KEY idx_plaza_comment_post (post_id, status, created_at),
  KEY idx_plaza_comment_parent (parent_id),
  KEY idx_plaza_comment_user (user_id, created_at),
  CONSTRAINT fk_plaza_comment_post FOREIGN KEY (post_id) REFERENCES plaza_posts(id) ON DELETE CASCADE,
  CONSTRAINT fk_plaza_comment_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS plaza_comment_reactions (
  id CHAR(36) PRIMARY KEY,
  comment_id CHAR(36) NOT NULL,
  user_id CHAR(36) NOT NULL,
  created_at BIGINT NOT NULL,
  UNIQUE KEY uq_plaza_comment_reaction (comment_id, user_id),
  KEY idx_plaza_comment_reaction_comment (comment_id),
  CONSTRAINT fk_plaza_comment_reaction_comment FOREIGN KEY (comment_id) REFERENCES plaza_comments(id) ON DELETE CASCADE,
  CONSTRAINT fk_plaza_comment_reaction_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS plaza_follows (
  follower_id CHAR(36) NOT NULL,
  followee_id CHAR(36) NOT NULL,
  created_at BIGINT NOT NULL,
  PRIMARY KEY (follower_id, followee_id),
  KEY idx_plaza_follow_followee (followee_id, created_at),
  CONSTRAINT fk_plaza_follow_follower FOREIGN KEY (follower_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_plaza_follow_followee FOREIGN KEY (followee_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS plaza_algorithm_config (
  `key` VARCHAR(100) NOT NULL,
  value DOUBLE NOT NULL,
  description VARCHAR(200) NOT NULL DEFAULT '',
  updated_at BIGINT NOT NULL,
  PRIMARY KEY (`key`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS plaza_attribution_events (
  id CHAR(36) PRIMARY KEY,
  event_type ENUM('landing', 'signup') NOT NULL,
  utm_source VARCHAR(100) NOT NULL,
  utm_medium VARCHAR(100) NOT NULL DEFAULT '',
  utm_campaign VARCHAR(100) NOT NULL DEFAULT '',
  ref_id VARCHAR(200) NOT NULL DEFAULT '',
  user_id CHAR(36) NULL,
  ip_hash VARCHAR(64) NOT NULL DEFAULT '',
  created_at BIGINT NOT NULL,
  KEY idx_plaza_attr_created (created_at),
  KEY idx_plaza_attr_campaign (utm_source, utm_campaign, created_at),
  CONSTRAINT fk_plaza_attr_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS plaza_reports (
  id CHAR(36) PRIMARY KEY,
  target_type ENUM('post', 'comment') NOT NULL,
  target_id CHAR(36) NOT NULL,
  reporter_id CHAR(36) NOT NULL,
  reason ENUM('spam', 'violence', 'porn', 'political', 'privacy', 'other') NOT NULL,
  detail VARCHAR(500) NOT NULL DEFAULT '',
  status ENUM('pending', 'processed', 'dismissed') NOT NULL DEFAULT 'pending',
  processed_by CHAR(36) NULL,
  processed_at BIGINT NULL,
  action_taken VARCHAR(200) NOT NULL DEFAULT '',
  created_at BIGINT NOT NULL,
  KEY idx_plaza_report_status (status, target_type, created_at),
  KEY idx_plaza_report_target (target_type, target_id),
  CONSTRAINT fk_plaza_report_reporter FOREIGN KEY (reporter_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_plaza_report_processor FOREIGN KEY (processed_by) REFERENCES h5_users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Session snapshot cache: stores userVisible messages for fast history reload.
-- Rollback: DROP TABLE h5_session_snapshots (data-only, no dependants).
CREATE TABLE IF NOT EXISTS h5_session_snapshots (
  agent_session_id VARCHAR(128) NOT NULL PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  name VARCHAR(512) NOT NULL DEFAULT '',
  display_title VARCHAR(512) NOT NULL DEFAULT '',
  working_dir VARCHAR(1024) NOT NULL DEFAULT '',
  created_at_str VARCHAR(64) NOT NULL DEFAULT '',
  updated_at_str VARCHAR(64) NOT NULL DEFAULT '',
  user_set_name TINYINT(1) NOT NULL DEFAULT 0,
  recipe_json TEXT NULL,
  synced_msg_count INT NOT NULL DEFAULT 0,
  source_updated_at VARCHAR(64) NOT NULL DEFAULT '',
  messages_json LONGTEXT NOT NULL,
  synced_at BIGINT NOT NULL,
  KEY idx_h5_snapshots_user (user_id),
  KEY idx_h5_snapshots_synced (synced_at),
  CONSTRAINT fk_h5_snapshot_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS plaza_featured (
  id CHAR(36) PRIMARY KEY,
  post_id CHAR(36) NOT NULL,
  position VARCHAR(50) NOT NULL,
  sort_order INT NOT NULL DEFAULT 0,
  starts_at BIGINT NOT NULL,
  expires_at BIGINT NULL,
  created_by CHAR(36) NOT NULL,
  created_at BIGINT NOT NULL,
  KEY idx_plaza_featured_position (position, starts_at, expires_at),
  CONSTRAINT fk_plaza_featured_post FOREIGN KEY (post_id) REFERENCES plaza_posts(id) ON DELETE CASCADE,
  CONSTRAINT fk_plaza_featured_creator FOREIGN KEY (created_by) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS plaza_user_events (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NULL,
  session_id VARCHAR(64) NOT NULL,
  post_id CHAR(36) NOT NULL,
  event_type ENUM(
    'impression', 'click', 'view', 'dwell',
    'like', 'collect', 'comment', 'share',
    'dislike', 'hide'
  ) NOT NULL,
  dwell_ms INT UNSIGNED NULL,
  feed_sort VARCHAR(32) NOT NULL DEFAULT '',
  feed_category VARCHAR(64) NOT NULL DEFAULT '',
  position INT UNSIGNED NULL,
  created_at BIGINT NOT NULL,
  KEY idx_plaza_event_user_time (user_id, created_at),
  KEY idx_plaza_event_session_time (session_id, created_at),
  KEY idx_plaza_event_post (post_id, event_type, created_at),
  CONSTRAINT fk_plaza_event_post FOREIGN KEY (post_id) REFERENCES plaza_posts(id) ON DELETE CASCADE,
  CONSTRAINT fk_plaza_event_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS ops_audit_log (
  id CHAR(36) PRIMARY KEY,
  operator_id CHAR(36) NOT NULL,
  action VARCHAR(100) NOT NULL,
  target_type VARCHAR(50) NOT NULL,
  target_id CHAR(36) NOT NULL,
  detail JSON NOT NULL,
  created_at BIGINT NOT NULL,
  KEY idx_ops_audit_operator (operator_id, created_at),
  KEY idx_ops_audit_target (target_type, target_id, created_at),
  CONSTRAINT fk_ops_audit_operator FOREIGN KEY (operator_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_user_wechat_identities (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  app_id VARCHAR(32) NOT NULL,
  openid VARCHAR(64) NOT NULL,
  unionid VARCHAR(64) NULL,
  nickname VARCHAR(128) NULL,
  avatar_url VARCHAR(512) NULL,
  last_login_at BIGINT NOT NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_wechat_openid (app_id, openid),
  UNIQUE KEY uq_wechat_user (user_id, app_id),
  KEY idx_wechat_unionid (unionid),
  CONSTRAINT fk_wechat_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_wechat_oauth_states (
  state VARCHAR(64) PRIMARY KEY,
  return_to VARCHAR(512) NULL,
  utm_source VARCHAR(64) NULL,
  utm_medium VARCHAR(64) NULL,
  utm_campaign VARCHAR(64) NULL,
  intent VARCHAR(16) NOT NULL DEFAULT 'login',
  bind_user_id CHAR(36) NULL,
  auth_mode VARCHAR(8) NOT NULL DEFAULT 'mp',
  status VARCHAR(16) NOT NULL DEFAULT 'pending',
  result_kind VARCHAR(32) NULL,
  result_token VARCHAR(512) NULL,
  result_message VARCHAR(255) NULL,
  expires_at BIGINT NOT NULL,
  created_at BIGINT NOT NULL,
  KEY idx_wechat_oauth_state_expires (expires_at),
  KEY idx_wechat_oauth_state_status (status, expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_wechat_pending_binds (
  token VARCHAR(64) PRIMARY KEY,
  app_id VARCHAR(32) NOT NULL,
  openid VARCHAR(64) NOT NULL,
  unionid VARCHAR(64) NULL,
  nickname VARCHAR(128) NULL,
  avatar_url VARCHAR(512) NULL,
  return_to VARCHAR(512) NULL,
  utm_source VARCHAR(64) NULL,
  utm_medium VARCHAR(64) NULL,
  utm_campaign VARCHAR(64) NULL,
  expires_at BIGINT NOT NULL,
  created_at BIGINT NOT NULL,
  KEY idx_wechat_pending_expires (expires_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_wechat_agent_routes (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  app_id VARCHAR(32) NOT NULL,
  openid VARCHAR(64) NOT NULL,
  agent_session_id VARCHAR(128) NOT NULL,
  status ENUM('active', 'disabled') NOT NULL DEFAULT 'active',
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_wechat_agent_route (app_id, openid),
  KEY idx_wechat_agent_route_user (user_id, updated_at),
  CONSTRAINT fk_wechat_agent_route_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_wechat_mp_messages (
  app_id VARCHAR(32) NOT NULL,
  openid VARCHAR(64) NOT NULL,
  msg_id VARCHAR(128) NOT NULL,
  status ENUM('processing', 'done', 'failed') NOT NULL DEFAULT 'processing',
  agent_session_id VARCHAR(128) NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  PRIMARY KEY (app_id, openid, msg_id),
  KEY idx_wechat_mp_messages_updated (updated_at),
  KEY idx_wechat_mp_messages_session (agent_session_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_wechat_mp_message_details (
  id CHAR(36) PRIMARY KEY,
  app_id VARCHAR(32) NOT NULL,
  openid VARCHAR(64) NOT NULL,
  user_id CHAR(36) NULL,
  msg_id VARCHAR(128) NULL,
  msg_type VARCHAR(32) NOT NULL,
  display_text TEXT NULL,
  agent_text TEXT NULL,
  media_id VARCHAR(256) NULL,
  media_url TEXT NULL,
  media_public_url TEXT NULL,
  media_format VARCHAR(64) NULL,
  location_lat DECIMAL(10,7) NULL,
  location_lng DECIMAL(10,7) NULL,
  location_label VARCHAR(255) NULL,
  link_url TEXT NULL,
  link_title VARCHAR(255) NULL,
  raw_xml_hash CHAR(40) NULL,
  raw_json JSON NULL,
  created_at BIGINT NOT NULL,
  KEY idx_wechat_mp_message_details_user (app_id, openid, created_at),
  KEY idx_wechat_mp_message_details_msg (msg_id),
  KEY idx_wechat_mp_message_details_type (msg_type, created_at),
  CONSTRAINT fk_wechat_mp_message_details_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_schedule_items (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  kind ENUM('task', 'event') NOT NULL,
  title VARCHAR(255) NOT NULL,
  description TEXT NULL,
  status ENUM('active', 'completed', 'cancelled', 'deleted') NOT NULL DEFAULT 'active',
  start_at BIGINT NULL,
  end_at BIGINT NULL,
  due_at BIGINT NULL,
  all_day TINYINT(1) NOT NULL DEFAULT 0,
  timezone VARCHAR(64) NOT NULL DEFAULT 'Asia/Shanghai',
  location VARCHAR(255) NULL,
  source_channel ENUM('h5', 'wechat', 'agent', 'api') NOT NULL DEFAULT 'agent',
  source_session_id VARCHAR(128) NULL,
  source_message_id VARCHAR(128) NULL,
  source_text TEXT NULL,
  metadata_json JSON NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  deleted_at BIGINT NULL,
  KEY idx_schedule_user_time (user_id, status, start_at, due_at),
  KEY idx_schedule_user_updated (user_id, updated_at),
  CONSTRAINT fk_schedule_item_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_schedule_reminders (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  item_id CHAR(36) NOT NULL,
  remind_at BIGINT NOT NULL,
  offset_minutes INT NULL,
  channel ENUM('wechat', 'in_app') NOT NULL DEFAULT 'wechat',
  status ENUM('pending', 'locked', 'sent', 'failed', 'cancelled') NOT NULL DEFAULT 'pending',
  attempts INT NOT NULL DEFAULT 0,
  last_error VARCHAR(500) NULL,
  locked_until BIGINT NULL,
  sent_at BIGINT NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_schedule_item_remind_at (item_id, remind_at, channel),
  KEY idx_reminder_due (status, remind_at),
  KEY idx_reminder_user (user_id, status, remind_at),
  CONSTRAINT fk_schedule_reminder_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_schedule_reminder_item FOREIGN KEY (item_id) REFERENCES h5_schedule_items(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_schedule_digest_subscriptions (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  digest_type ENUM('todo_day') NOT NULL DEFAULT 'todo_day',
  hour TINYINT UNSIGNED NOT NULL,
  minute TINYINT UNSIGNED NOT NULL DEFAULT 0,
  timezone VARCHAR(64) NOT NULL DEFAULT 'Asia/Shanghai',
  channel ENUM('wechat', 'in_app') NOT NULL DEFAULT 'wechat',
  status ENUM('active', 'locked', 'failed', 'cancelled') NOT NULL DEFAULT 'active',
  next_run_at BIGINT NOT NULL,
  last_run_at BIGINT NULL,
  attempts INT NOT NULL DEFAULT 0,
  locked_until BIGINT NULL,
  last_error VARCHAR(500) NULL,
  source_channel ENUM('h5', 'wechat', 'agent', 'api') NOT NULL DEFAULT 'agent',
  source_session_id VARCHAR(128) NULL,
  source_message_id VARCHAR(128) NULL,
  source_text TEXT NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_schedule_digest_user_type_channel (user_id, digest_type, channel),
  KEY idx_schedule_digest_due (status, next_run_at),
  CONSTRAINT fk_schedule_digest_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_balance_alert_subscriptions (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  threshold_cents BIGINT NOT NULL,
  channel ENUM('wechat', 'in_app') NOT NULL DEFAULT 'wechat',
  status ENUM('active', 'locked', 'failed', 'cancelled') NOT NULL DEFAULT 'active',
  next_run_at BIGINT NOT NULL,
  last_run_at BIGINT NULL,
  last_notified_balance_cents BIGINT NULL,
  attempts INT NOT NULL DEFAULT 0,
  locked_until BIGINT NULL,
  last_error VARCHAR(500) NULL,
  source_channel ENUM('h5', 'wechat', 'agent', 'api') NOT NULL DEFAULT 'agent',
  source_session_id VARCHAR(128) NULL,
  source_message_id VARCHAR(128) NULL,
  source_text TEXT NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_balance_alert_user_channel (user_id, channel),
  KEY idx_balance_alert_due (status, next_run_at),
  KEY idx_balance_alert_user (user_id, status, next_run_at),
  CONSTRAINT fk_balance_alert_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_schedule_delivery_logs (
  id CHAR(36) PRIMARY KEY,
  reminder_id CHAR(36) NULL,
  subscription_id CHAR(36) NULL,
  user_id CHAR(36) NOT NULL,
  channel ENUM('wechat', 'in_app') NOT NULL,
  status ENUM('success', 'failed') NOT NULL,
  provider_message_id VARCHAR(128) NULL,
  error_code VARCHAR(64) NULL,
  error_message VARCHAR(500) NULL,
  created_at BIGINT NOT NULL,
  KEY idx_delivery_reminder (reminder_id, created_at),
  KEY idx_delivery_subscription (subscription_id, created_at),
  KEY idx_delivery_user (user_id, created_at),
  CONSTRAINT fk_schedule_delivery_reminder FOREIGN KEY (reminder_id) REFERENCES h5_schedule_reminders(id) ON DELETE CASCADE,
  CONSTRAINT fk_schedule_delivery_subscription FOREIGN KEY (subscription_id) REFERENCES h5_schedule_digest_subscriptions(id) ON DELETE CASCADE,
  CONSTRAINT fk_schedule_delivery_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_user_notifications (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  channel ENUM('web', 'wechat') NOT NULL DEFAULT 'web',
  notification_type VARCHAR(64) NOT NULL,
  title VARCHAR(255) NOT NULL,
  body TEXT NOT NULL,
  data_json JSON NULL,
  status ENUM('unread', 'read') NOT NULL DEFAULT 'unread',
  read_at BIGINT NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  KEY idx_user_notifications_user_status_created (user_id, status, created_at),
  KEY idx_user_notifications_user_created (user_id, created_at),
  CONSTRAINT fk_user_notifications_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_plan_catalog (
  plan_type    VARCHAR(32) PRIMARY KEY,
  name         VARCHAR(64) NOT NULL,
  price_cents  INT NOT NULL DEFAULT 0,
  period_days  INT NOT NULL DEFAULT 30,
  period_tokens BIGINT NOT NULL DEFAULT 0,
  period_images INT NOT NULL DEFAULT 0,
  model_tier   VARCHAR(32) NOT NULL DEFAULT 'basic',
  overage_rate DECIMAL(4,2) NOT NULL DEFAULT 1.00,
  sort_order   INT NOT NULL DEFAULT 0,
  is_active    TINYINT(1) NOT NULL DEFAULT 1,
  description  VARCHAR(512) NULL,
  created_at   BIGINT NOT NULL,
  updated_at   BIGINT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_subscriptions (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  plan_type VARCHAR(32) NOT NULL DEFAULT 'free',
  status ENUM('active', 'expired', 'cancelled') NOT NULL DEFAULT 'active',
  period_tokens_limit BIGINT NOT NULL DEFAULT 0,
  period_tokens_used BIGINT NOT NULL DEFAULT 0,
  period_images_limit INT NOT NULL DEFAULT 0,
  period_images_used INT NOT NULL DEFAULT 0,
  period_start BIGINT NOT NULL,
  period_end BIGINT NOT NULL,
  expires_at BIGINT NOT NULL,
  overage_rate DECIMAL(4,2) NOT NULL DEFAULT 1.00,
  auto_renew TINYINT(1) NOT NULL DEFAULT 0,
  operator_id CHAR(36) NULL,
  note VARCHAR(512) NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  KEY idx_h5_sub_user_status (user_id, status),
  KEY idx_h5_sub_expires (expires_at, status),
  CONSTRAINT fk_h5_sub_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_blocked_words (
  id CHAR(36) PRIMARY KEY,
  word VARCHAR(200) NOT NULL,
  replacement VARCHAR(200) NOT NULL DEFAULT '***',
  note VARCHAR(500) NULL,
  status ENUM('active', 'disabled') NOT NULL DEFAULT 'active',
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uk_blocked_word (word)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Shared experience store (etat C): all goosed Worker instances read/write the
-- same learned experience so capability is not siloed per instance. Built on
-- MySQL first with a keyword + recency retrieval; `embedding` is reserved for a
-- later move to PostgreSQL + pgvector without changing the calling contract.
CREATE TABLE IF NOT EXISTS h5_experience (
  id CHAR(36) PRIMARY KEY,
  scope VARCHAR(64) NOT NULL DEFAULT 'global',
  kind VARCHAR(32) NOT NULL DEFAULT 'lesson',
  title VARCHAR(255) NOT NULL,
  body MEDIUMTEXT NOT NULL,
  tags_json JSON NULL,
  source_session_id VARCHAR(128) NULL,
  source_user_id CHAR(36) NULL,
  use_count BIGINT NOT NULL DEFAULT 0,
  embedding JSON NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  KEY idx_h5_experience_scope_updated (scope, updated_at),
  FULLTEXT KEY ftx_h5_experience (title, body)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_conversation_messages (
  id CHAR(64) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  agent_session_id VARCHAR(128) NOT NULL,
  message_key VARCHAR(128) NOT NULL,
  sequence_no INT NOT NULL DEFAULT 0,
  role VARCHAR(32) NOT NULL,
  text MEDIUMTEXT NOT NULL,
  raw_json LONGTEXT NULL,
  analyzed_at BIGINT NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_conversation_message (agent_session_id, message_key),
  KEY idx_h5_conversation_user_created (user_id, created_at),
  KEY idx_h5_conversation_user_analyzed (user_id, analyzed_at),
  CONSTRAINT fk_h5_conversation_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_conversation_session FOREIGN KEY (agent_session_id) REFERENCES h5_user_sessions(agent_session_id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_user_memory_items (
  id CHAR(64) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  label ENUM('preference', 'habit', 'interest', 'goal', 'fact', 'experience', 'knowledge') NOT NULL DEFAULT 'fact',
  memory_hash CHAR(64) NOT NULL,
  memory_text TEXT NOT NULL,
  evidence_message_id CHAR(64) NULL,
  source_session_id VARCHAR(128) NULL,
  confidence DECIMAL(4,3) NOT NULL DEFAULT 0.600,
  status ENUM('active', 'archived', 'deleted') NOT NULL DEFAULT 'active',
  raw_json JSON NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  UNIQUE KEY uq_h5_user_memory_hash (user_id, memory_hash),
  KEY idx_h5_user_memory_user_label (user_id, label, updated_at),
  KEY idx_h5_user_memory_session (source_session_id),
  CONSTRAINT fk_h5_user_memory_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE,
  CONSTRAINT fk_h5_user_memory_evidence FOREIGN KEY (evidence_message_id) REFERENCES h5_conversation_messages(id) ON DELETE SET NULL,
  CONSTRAINT fk_h5_user_memory_session FOREIGN KEY (source_session_id) REFERENCES h5_user_sessions(agent_session_id) ON DELETE SET NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS h5_user_feedback (
  id CHAR(36) PRIMARY KEY,
  user_id CHAR(36) NOT NULL,
  type ENUM('bug', 'feature', 'other') NOT NULL,
  title VARCHAR(120) NOT NULL,
  description TEXT NOT NULL,
  contact VARCHAR(120) NULL,
  status ENUM('pending', 'reviewing', 'resolved', 'closed') NOT NULL DEFAULT 'pending',
  images_json JSON NULL,
  context_json JSON NULL,
  created_at BIGINT NOT NULL,
  updated_at BIGINT NOT NULL,
  KEY idx_h5_user_feedback_user_created (user_id, created_at),
  KEY idx_h5_user_feedback_status_created (status, created_at),
  CONSTRAINT fk_h5_user_feedback_user FOREIGN KEY (user_id) REFERENCES h5_users(id) ON DELETE CASCADE
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
