import db from '../config/database';
import { logger } from '../utils/logger';

export interface ExperimentBucket {
  id: string;
  name: string;
  type: 'USER' | 'SHOP' | 'NODE';
  status: 'ACTIVE' | 'PAUSED' | 'ARCHIVED';
  trafficAllocation: number; // 0-100
  variations: any[]; // [ { id: 'A', value: '...' }, { id: 'B', value: '...' } ]
  metadata: any;
}

/**
 * [CORE_GOV_EXP] AB 实验与策略分桶平台 (Experiment Hub)
 * @description 核心逻辑：支持按用户、店铺或 Hub 节点进行实验隔离与流量分桶。
 * 为 AGI 策略的 A/B 测试提供基础设施，支持平滑灰度发布。
 * 遵循 Autocomplete-First (V31.5) 规范。
 */
export class ExperimentService {
  private static readonly EXPERIMENT_TABLE = 'cf_experiment_buckets';

  /**
   * 初始化表结构
   */
  static async initTable() {
    const hasTable = await db.schema.hasTable(this.EXPERIMENT_TABLE);
    if (!hasTable) {
      logger.info(`📦 Creating ${this.EXPERIMENT_TABLE} table...`);
      await db.schema.createTable(this.EXPERIMENT_TABLE, (table) => {
        table.string('id', 64).primary();
        table.string('name', 255);
        table.string('type', 32).index();
        table.string('status', 32).defaultTo('ACTIVE').index();
        table.integer('traffic_allocation').defaultTo(0);
        table.json('variations');
        table.json('metadata');
        table.timestamps(true, true);
      });
      logger.info(`✅ Table ${this.EXPERIMENT_TABLE} created`);
    }
  }

  /**
   * 获取实验分桶结果 (Experiment Bucket Assignment)
   * @param experimentId 实验 ID
   * @param targetId 目标 ID (UserId, ShopId, NodeId)
   */
  static async getVariation(experimentId: string, targetId: string): Promise<string> {
    const experiment = await db(this.EXPERIMENT_TABLE).where({ id: experimentId, status: 'ACTIVE' }).first();
    if (!experiment) return 'CONTROL';

    // 1. 基于 targetId 进行 Hash 取模分配流量
    const hash = this.getHash(targetId);
    const bucket = hash % 100;

    if (bucket >= experiment.traffic_allocation) {
      return 'CONTROL';
    }

    // 2. 在流量包内进行 Variation 分配
    const variations = JSON.parse(experiment.variations || '[]');
    if (variations.length === 0) return 'CONTROL';

    const variationIndex = hash % variations.length;
    return variations[variationIndex].id;
  }

  /**
   * 简单的哈希函数
   * @private
   */
  private static getHash(str: string): number {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash |= 0; // Convert to 32bit integer
    }
    return Math.abs(hash);
  }
}
