CREATE TABLE IF NOT EXISTS qipai_notification_templates (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL,
  store_id BIGINT UNSIGNED NULL,
  scope_store_id BIGINT UNSIGNED GENERATED ALWAYS AS (COALESCE(store_id, 0)) STORED,
  template_code VARCHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  event_type VARCHAR(128) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  channel VARCHAR(32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  title_template VARCHAR(256) NOT NULL,
  body_template VARCHAR(2000) NOT NULL,
  external_template_id VARCHAR(128) NOT NULL DEFAULT '',
  status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
  version INT UNSIGNED NOT NULL DEFAULT 1,
  created_by BIGINT UNSIGNED NULL,
  updated_by BIGINT UNSIGNED NULL,
  created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  CONSTRAINT fk_qipai_notification_template_tenant FOREIGN KEY (tenant_id) REFERENCES qipai_tenants(id),
  CONSTRAINT fk_qipai_notification_template_store FOREIGN KEY (tenant_id, store_id) REFERENCES qipai_stores(tenant_id, id),
  CONSTRAINT fk_qipai_notification_template_creator FOREIGN KEY (tenant_id, created_by) REFERENCES qipai_users(tenant_id, id),
  CONSTRAINT fk_qipai_notification_template_updater FOREIGN KEY (tenant_id, updated_by) REFERENCES qipai_users(tenant_id, id),
  UNIQUE KEY uq_qipai_notification_template_scope (tenant_id, scope_store_id, template_code),
  UNIQUE KEY uq_qipai_notification_template_scope_id (tenant_id, id),
  KEY idx_qipai_notification_template_event (tenant_id, event_type, channel, status),
  CONSTRAINT chk_qipai_notification_template_channel CHECK (channel IN ('IN_APP', 'WECHAT_SUBSCRIBE', 'WE_COM', 'WEBHOOK', 'CLOUD_SPEAKER')),
  CONSTRAINT chk_qipai_notification_template_status CHECK (status IN ('ACTIVE', 'DISABLED')),
  CONSTRAINT chk_qipai_notification_template_version CHECK (version >= 1)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS qipai_notification_routes (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL,
  store_id BIGINT UNSIGNED NULL,
  scope_store_id BIGINT UNSIGNED GENERATED ALWAYS AS (COALESCE(store_id, 0)) STORED,
  event_type VARCHAR(128) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  template_id BIGINT UNSIGNED NOT NULL,
  recipient_type VARCHAR(32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  recipient_value VARCHAR(191) NOT NULL,
  quiet_start TIME NULL,
  quiet_end TIME NULL,
  status VARCHAR(16) NOT NULL DEFAULT 'ACTIVE',
  version INT UNSIGNED NOT NULL DEFAULT 1,
  created_by BIGINT UNSIGNED NULL,
  updated_by BIGINT UNSIGNED NULL,
  created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  CONSTRAINT fk_qipai_notification_route_tenant FOREIGN KEY (tenant_id) REFERENCES qipai_tenants(id),
  CONSTRAINT fk_qipai_notification_route_store FOREIGN KEY (tenant_id, store_id) REFERENCES qipai_stores(tenant_id, id),
  CONSTRAINT fk_qipai_notification_route_template FOREIGN KEY (tenant_id, template_id) REFERENCES qipai_notification_templates(tenant_id, id),
  CONSTRAINT fk_qipai_notification_route_creator FOREIGN KEY (tenant_id, created_by) REFERENCES qipai_users(tenant_id, id),
  CONSTRAINT fk_qipai_notification_route_updater FOREIGN KEY (tenant_id, updated_by) REFERENCES qipai_users(tenant_id, id),
  UNIQUE KEY uq_qipai_notification_route_scope_id (tenant_id, id),
  UNIQUE KEY uq_qipai_notification_route (tenant_id, scope_store_id, event_type, template_id, recipient_type, recipient_value),
  KEY idx_qipai_notification_route_match (tenant_id, event_type, store_id, status),
  CONSTRAINT chk_qipai_notification_route_recipient CHECK (recipient_type IN ('CUSTOMER', 'USER', 'ROLE', 'STORE_WEBHOOK')),
  CONSTRAINT chk_qipai_notification_route_status CHECK (status IN ('ACTIVE', 'DISABLED')),
  CONSTRAINT chk_qipai_notification_route_quiet CHECK ((quiet_start IS NULL AND quiet_end IS NULL) OR (quiet_start IS NOT NULL AND quiet_end IS NOT NULL)),
  CONSTRAINT chk_qipai_notification_route_version CHECK (version >= 1)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS qipai_notification_subscriptions (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL,
  user_id BIGINT UNSIGNED NOT NULL,
  channel VARCHAR(32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  template_code VARCHAR(64) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  status VARCHAR(16) NOT NULL,
  authorized_at DATETIME(3) NOT NULL,
  revoked_at DATETIME(3) NULL,
  updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  CONSTRAINT fk_qipai_notification_subscription_user FOREIGN KEY (tenant_id, user_id) REFERENCES qipai_users(tenant_id, id),
  UNIQUE KEY uq_qipai_notification_subscription (tenant_id, user_id, channel, template_code),
  CONSTRAINT chk_qipai_notification_subscription_channel CHECK (channel IN ('WECHAT_SUBSCRIBE')),
  CONSTRAINT chk_qipai_notification_subscription_status CHECK (status IN ('AUTHORIZED', 'REVOKED'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS qipai_notification_deliveries (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL,
  store_id BIGINT UNSIGNED NULL,
  outbox_event_id BIGINT UNSIGNED NOT NULL,
  route_id BIGINT UNSIGNED NOT NULL,
  template_id BIGINT UNSIGNED NOT NULL,
  event_type VARCHAR(128) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  channel VARCHAR(32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  recipient_type VARCHAR(32) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  recipient_id VARCHAR(191) NOT NULL,
  recipient_masked VARCHAR(191) NOT NULL,
  title_snapshot VARCHAR(256) NOT NULL,
  body_snapshot VARCHAR(2000) NOT NULL,
  payload_snapshot JSON NOT NULL,
  status VARCHAR(32) NOT NULL DEFAULT 'PENDING',
  attempts INT UNSIGNED NOT NULL DEFAULT 0,
  max_attempts INT UNSIGNED NOT NULL DEFAULT 8,
  available_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  sent_at DATETIME(3) NULL,
  read_at DATETIME(3) NULL,
  last_error_code VARCHAR(64) NOT NULL DEFAULT '',
  last_error_message VARCHAR(512) NOT NULL DEFAULT '',
  provider_message_id VARCHAR(191) NOT NULL DEFAULT '',
  manual_retry_count INT UNSIGNED NOT NULL DEFAULT 0,
  created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  updated_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  CONSTRAINT fk_qipai_notification_delivery_tenant FOREIGN KEY (tenant_id) REFERENCES qipai_tenants(id),
  CONSTRAINT fk_qipai_notification_delivery_store FOREIGN KEY (tenant_id, store_id) REFERENCES qipai_stores(tenant_id, id),
  CONSTRAINT fk_qipai_notification_delivery_outbox FOREIGN KEY (outbox_event_id) REFERENCES qipai_outbox_events(id),
  CONSTRAINT fk_qipai_notification_delivery_route FOREIGN KEY (tenant_id, route_id) REFERENCES qipai_notification_routes(tenant_id, id),
  CONSTRAINT fk_qipai_notification_delivery_template FOREIGN KEY (tenant_id, template_id) REFERENCES qipai_notification_templates(tenant_id, id),
  UNIQUE KEY uq_qipai_notification_delivery_scope_id (tenant_id, id),
  UNIQUE KEY uq_qipai_notification_delivery_idempotency (tenant_id, outbox_event_id, channel, recipient_type, recipient_id, template_id),
  KEY idx_qipai_notification_delivery_dispatch (status, available_at, id),
  KEY idx_qipai_notification_delivery_management (tenant_id, store_id, status, created_at, id),
  KEY idx_qipai_notification_delivery_inbox (tenant_id, recipient_type, recipient_id, channel, read_at, created_at),
  CONSTRAINT chk_qipai_notification_delivery_status CHECK (status IN ('PENDING', 'PROCESSING', 'SENT', 'RETRY', 'FAILED', 'SUPPRESSED')),
  CONSTRAINT chk_qipai_notification_delivery_attempts CHECK (attempts <= max_attempts AND max_attempts BETWEEN 1 AND 20)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

CREATE TABLE IF NOT EXISTS qipai_notification_attempts (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
  tenant_id BIGINT UNSIGNED NOT NULL,
  delivery_id BIGINT UNSIGNED NOT NULL,
  attempt_no INT UNSIGNED NOT NULL,
  trigger_type VARCHAR(16) CHARACTER SET ascii COLLATE ascii_bin NOT NULL,
  status VARCHAR(16) NOT NULL,
  error_code VARCHAR(64) NOT NULL DEFAULT '',
  error_message VARCHAR(512) NOT NULL DEFAULT '',
  provider_message_id VARCHAR(191) NOT NULL DEFAULT '',
  operator_id BIGINT UNSIGNED NULL,
  created_at DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  CONSTRAINT fk_qipai_notification_attempt_delivery FOREIGN KEY (tenant_id, delivery_id) REFERENCES qipai_notification_deliveries(tenant_id, id),
  CONSTRAINT fk_qipai_notification_attempt_operator FOREIGN KEY (tenant_id, operator_id) REFERENCES qipai_users(tenant_id, id),
  UNIQUE KEY uq_qipai_notification_attempt (tenant_id, delivery_id, attempt_no),
  CONSTRAINT chk_qipai_notification_attempt_trigger CHECK (trigger_type IN ('AUTO', 'MANUAL')),
  CONSTRAINT chk_qipai_notification_attempt_status CHECK (status IN ('SENT', 'FAILED', 'SUPPRESSED'))
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

DROP TRIGGER IF EXISTS qipai_notification_attempts_no_update;
CREATE TRIGGER qipai_notification_attempts_no_update BEFORE UPDATE ON qipai_notification_attempts
FOR EACH ROW SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'NOTIFICATION_ATTEMPT_IMMUTABLE';
DROP TRIGGER IF EXISTS qipai_notification_attempts_no_delete;
CREATE TRIGGER qipai_notification_attempts_no_delete BEFORE DELETE ON qipai_notification_attempts
FOR EACH ROW SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = 'NOTIFICATION_ATTEMPT_IMMUTABLE';

INSERT IGNORE INTO qipai_permissions (code, name, category) VALUES
  ('notification.read', '查看通知中心', 'notification'),
  ('notification.manage', '管理通知与补发', 'notification');
INSERT IGNORE INTO qipai_role_permissions (tenant_id, role_id, permission_id)
SELECT r.tenant_id, r.id, p.id FROM qipai_roles r
INNER JOIN qipai_permissions p ON p.code IN ('notification.read', 'notification.manage')
WHERE r.code IN ('STORE_ADMIN', 'TENANT_ADMIN', 'PLATFORM_ADMIN')
  AND r.status = 'ACTIVE' AND r.deleted_at IS NULL;

INSERT IGNORE INTO qipai_schema_migrations (version, name)
VALUES ('2026081110', 'm10a_notification_center');
