import db from '../config/database';
import { logger } from '../utils/logger';
import { UserAssetService } from './UserAssetService';
import {
  MemberLevel,
  MemberLevelRule,
  MemberLevelRuleCreateInput,
} from '../models/UserAsset';

/**
 * MemberLevelService - 会员等级系统
 * 
 * [BE-UA003] 会员等级系统
 * @description 管理会员等级规则、等级升降、权益配置
 * 
 * 功能定位：
 * - 会员等级规则管理
 * - 等级自动升降
 * - 会员权益配置
 * - 等级统计报表
 * 
 * 安全约束：
 * - 所有操作必须携带五元组追踪信息
 * - 等级变更必须记录日志
 * 
 * @author AI-Backend-7
 * @taskId BE-UA003
 */
export class MemberLevelService {
  private static readonly RULE_TABLE = 'cf_member_level_rule';
  private static readonly LOG_TABLE = 'cf_member_level_log';

  /**
   * 初始化数据库表
   * 遵循幂等性原则：使用 hasTable 前置校验
   */
  static async initTable(): Promise<void> {
    const hasRuleTable = await db.schema.hasTable(this.RULE_TABLE);
    if (!hasRuleTable) {
      logger.info(`[MemberLevelService] Creating ${this.RULE_TABLE} table...`);
      await db.schema.createTable(this.RULE_TABLE, (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 64).index();
        table.enum('level', ['BRONZE', 'SILVER', 'GOLD', 'PLATINUM', 'DIAMOND']).notNullable();
        table.integer('min_score').notNullable();
        table.integer('max_score').notNullable();
        table.decimal('discount', 5, 4).defaultTo(1.0).notNullable();
        table.decimal('points_multiplier', 5, 4).defaultTo(1.0).notNullable();
        table.json('benefits').notNullable();
        table.boolean('is_active').defaultTo(true).notNullable();
        table.timestamp('created_at').defaultTo(db.fn.now());
        table.timestamp('updated_at').defaultTo(db.fn.now());

        table.index(['tenant_id', 'level'], 'idx_rule_tenant_level');
      });
      logger.info(`[MemberLevelService] Table ${this.RULE_TABLE} created`);
    }

    const hasLogTable = await db.schema.hasTable(this.LOG_TABLE);
    if (!hasLogTable) {
      logger.info(`[MemberLevelService] Creating ${this.LOG_TABLE} table...`);
      await db.schema.createTable(this.LOG_TABLE, (table) => {
        table.string('id', 36).primary();
        table.string('tenant_id', 64).notNullable().index();
        table.string('shop_id', 64).notNullable().index();
        table.string('user_id', 64).notNullable().index();
        table.string('trace_id', 64).notNullable();
        table.enum('old_level', ['BRONZE', 'SILVER', 'GOLD', 'PLATINUM', 'DIAMOND']);
        table.enum('new_level', ['BRONZE', 'SILVER', 'GOLD', 'PLATINUM', 'DIAMOND']).notNullable();
        table.integer('old_score');
        table.integer('new_score').notNullable();
        table.string('change_type', 32).notNullable();
        table.text('reason');
        table.timestamp('created_at').defaultTo(db.fn.now());

        table.index(['tenant_id', 'user_id'], 'idx_log_user');
        table.index(['created_at'], 'idx_log_time');
      });
      logger.info(`[MemberLevelService] Table ${this.LOG_TABLE} created`);
    }
  }

  /**
   * 创建或更新等级规则
   * @param input 规则创建输入
   * @returns 规则ID
   */
  static async createOrUpdateRule(input: MemberLevelRuleCreateInput): Promise<string> {
    const existing = await db(this.RULE_TABLE)
      .where({
        tenant_id: input.tenantId || null,
        level: input.level,
      })
      .first();

    if (existing) {
      await db(this.RULE_TABLE)
        .where({ id: existing.id })
        .update({
          min_score: input.minScore,
          max_score: input.maxScore,
          discount: input.discount,
          points_multiplier: input.pointsMultiplier,
          benefits: JSON.stringify(input.benefits),
          is_active: input.isActive !== undefined ? input.isActive : true,
          updated_at: new Date(),
        });

      logger.info(`[MemberLevelService] Rule updated: level=${input.level}, tenantId=${input.tenantId || 'global'}`);
      return existing.id;
    }

    const id = this.generateId();
    const now = new Date();

    await db(this.RULE_TABLE).insert({
      id,
      tenant_id: input.tenantId || null,
      level: input.level,
      min_score: input.minScore,
      max_score: input.maxScore,
      discount: input.discount,
      points_multiplier: input.pointsMultiplier,
      benefits: JSON.stringify(input.benefits),
      is_active: input.isActive !== undefined ? input.isActive : true,
      created_at: now,
      updated_at: now,
    });

    logger.info(`[MemberLevelService] Rule created: id=${id}, level=${input.level}, tenantId=${input.tenantId || 'global'}`);

    return id;
  }

  /**
   * 获取等级规则
   * @param tenantId 租户ID（可选，为空则获取全局规则）
   * @param level 等级
   * @returns 等级规则
   */
  static async getRule(tenantId: string | undefined, level: MemberLevel): Promise<MemberLevelRule | null> {
    let query = db(this.RULE_TABLE)
      .where('level', level)
      .where('is_active', true);

    if (tenantId) {
      query = query.where(function(this: any) {
        this.whereNull('tenant_id').orWhere('tenant_id', tenantId);
      });
    } else {
      query = query.whereNull('tenant_id');
    }

    const row = await query.orderBy('tenant_id', 'desc').first();

    if (!row) return null;

    return this.mapRowToRule(row);
  }

  /**
   * 获取所有等级规则
   * @param tenantId 租户ID（可选）
   * @returns 等级规则列表
   */
  static async getAllRules(tenantId?: string): Promise<MemberLevelRule[]> {
    let query = db(this.RULE_TABLE)
      .where('is_active', true);

    if (tenantId) {
      query = query.where(function(this: any) {
        this.whereNull('tenant_id').orWhere('tenant_id', tenantId);
      });
    } else {
      query = query.whereNull('tenant_id');
    }

    const rows = await query.orderBy('min_score', 'asc');

    const rules: MemberLevelRule[] = [];
    const seenLevels = new Set<string>();

    for (const row of rows) {
      if (!seenLevels.has(row.level)) {
        rules.push(this.mapRowToRule(row));
        seenLevels.add(row.level);
      }
    }

    return rules;
  }

  /**
   * 根据积分计算等级
   * @param tenantId 租户ID
   * @param score 积分
   * @returns 等级
   */
  static async calculateLevel(tenantId: string, score: number): Promise<MemberLevel> {
    const rules = await this.getAllRules(tenantId);

    if (rules.length === 0) {
      return UserAssetService.calculateLevel(score);
    }

    for (const rule of rules) {
      if (score >= rule.minScore && score <= rule.maxScore) {
        return rule.level;
      }
    }

    const sortedRules = [...rules].sort((a, b) => b.minScore - a.minScore);
    for (const rule of sortedRules) {
      if (score >= rule.minScore) {
        return rule.level;
      }
    }

    return 'BRONZE';
  }

  /**
   * 获取等级权益
   * @param tenantId 租户ID
   * @param level 等级
   * @returns 权益列表
   */
  static async getLevelBenefits(tenantId: string, level: MemberLevel): Promise<string[]> {
    const rule = await this.getRule(tenantId, level);
    return rule?.benefits || [];
  }

  /**
   * 获取等级折扣
   * @param tenantId 租户ID
   * @param level 等级
   * @returns 折扣率（0-1）
   */
  static async getLevelDiscount(tenantId: string, level: MemberLevel): Promise<number> {
    const rule = await this.getRule(tenantId, level);
    return rule?.discount || 1.0;
  }

  /**
   * 获取等级积分倍率
   * @param tenantId 租户ID
   * @param level 等级
   * @returns 积分倍率
   */
  static async getLevelPointsMultiplier(tenantId: string, level: MemberLevel): Promise<number> {
    const rule = await this.getRule(tenantId, level);
    return rule?.pointsMultiplier || 1.0;
  }

  /**
   * 升级用户等级
   * @param tenantId 租户ID
   * @param shopId 店铺ID
   * @param userId 用户ID
   * @param newLevel 新等级
   * @param reason 原因
   * @param traceId 追踪ID
   */
  static async upgradeLevel(
    tenantId: string,
    shopId: string,
    userId: string,
    newLevel: MemberLevel,
    reason: string,
    traceId: string
  ): Promise<void> {
    const asset = await UserAssetService.getByUserId(tenantId, shopId, userId);
    if (!asset) {
      throw new Error(`User asset not found: userId=${userId}`);
    }

    const oldLevel = asset.memberLevel;

    if (oldLevel === newLevel) {
      return;
    }

    await db('cf_user_asset')
      .where({ tenant_id: tenantId, shop_id: shopId, user_id: userId })
      .update({
        member_level: newLevel,
        updated_at: new Date(),
      });

    await this.logLevelChange({
      tenantId,
      shopId,
      userId,
      oldLevel,
      newLevel,
      oldScore: asset.memberScore,
      newScore: asset.memberScore,
      changeType: this.getLevelOrder(newLevel) > this.getLevelOrder(oldLevel) ? 'UPGRADE' : 'DOWNGRADE',
      reason,
      traceId,
    });

    logger.info(`[MemberLevelService] Level changed: userId=${userId}, oldLevel=${oldLevel}, newLevel=${newLevel}, traceId=${traceId}`);
  }

  /**
   * 批量重新计算用户等级
   * @param tenantId 租户ID
   * @param shopId 店铺ID
   * @returns 更新的用户数
   */
  static async batchRecalculateLevels(
    tenantId: string,
    shopId?: string
  ): Promise<number> {
    let query = db('cf_user_asset').where('tenant_id', tenantId);
    if (shopId) {
      query = query.where('shop_id', shopId);
    }

    const users = await query;
    let updatedCount = 0;

    for (const user of users) {
      try {
        const newLevel = await this.calculateLevel(tenantId, user.member_score);

        if (newLevel !== user.member_level) {
          await this.upgradeLevel(
            tenantId,
            user.shop_id,
            user.user_id,
            newLevel,
            '系统自动计算',
            `AUTO-RECALC-${Date.now()}`
          );
          updatedCount++;
        }
      } catch (error: any) {
        logger.error(`[MemberLevelService] Failed to recalculate level: userId=${user.user_id}, error=${error.message}`);
      }
    }

    logger.info(`[MemberLevelService] Batch recalculate completed: updated=${updatedCount}`);

    return updatedCount;
  }

  /**
   * 获取等级变更历史
   * @param tenantId 租户ID
   * @param userId 用户ID
   * @param limit 限制条数
   * @returns 变更历史
   */
  static async getLevelHistory(
    tenantId: string,
    userId: string,
    limit: number = 20
  ): Promise<Array<{
    oldLevel: MemberLevel | null;
    newLevel: MemberLevel;
    changeType: string;
    reason: string;
    createdAt: Date;
  }>> {
    const rows = await db(this.LOG_TABLE)
      .where({ tenant_id: tenantId, user_id: userId })
      .orderBy('created_at', 'desc')
      .limit(limit);

    return rows.map(row => ({
      oldLevel: row.old_level,
      newLevel: row.new_level,
      changeType: row.change_type,
      reason: row.reason,
      createdAt: row.created_at,
    }));
  }

  /**
   * 获取等级分布统计
   * @param tenantId 租户ID
   * @param shopId 店铺ID（可选）
   * @returns 等级分布
   */
  static async getLevelDistribution(
    tenantId: string,
    shopId?: string
  ): Promise<Record<MemberLevel, number>> {
    let query = db('cf_user_asset').where('tenant_id', tenantId);
    if (shopId) {
      query = query.where('shop_id', shopId);
    }

    const rows = await query;

    const distribution: Record<MemberLevel, number> = {
      BRONZE: 0,
      SILVER: 0,
      GOLD: 0,
      PLATINUM: 0,
      DIAMOND: 0,
    };

    for (const row of rows) {
      distribution[row.member_level as MemberLevel]++;
    }

    return distribution;
  }

  /**
   * 初始化默认等级规则
   */
  static async initDefaultRules(): Promise<void> {
    const defaultRules: MemberLevelRuleCreateInput[] = [
      {
        level: 'BRONZE',
        minScore: 0,
        maxScore: 499,
        discount: 1.0,
        pointsMultiplier: 1.0,
        benefits: ['基础积分获取', '生日优惠'],
      },
      {
        level: 'SILVER',
        minScore: 500,
        maxScore: 1999,
        discount: 0.98,
        pointsMultiplier: 1.2,
        benefits: ['基础积分获取', '生日优惠', '专属客服', '优先发货'],
      },
      {
        level: 'GOLD',
        minScore: 2000,
        maxScore: 4999,
        discount: 0.95,
        pointsMultiplier: 1.5,
        benefits: ['基础积分获取', '生日优惠', '专属客服', '优先发货', '会员专享价', '免费退换货'],
      },
      {
        level: 'PLATINUM',
        minScore: 5000,
        maxScore: 9999,
        discount: 0.92,
        pointsMultiplier: 2.0,
        benefits: ['基础积分获取', '生日优惠', '专属客服', '优先发货', '会员专享价', '免费退换货', '专属活动', '积分双倍日'],
      },
      {
        level: 'DIAMOND',
        minScore: 10000,
        maxScore: 999999,
        discount: 0.88,
        pointsMultiplier: 3.0,
        benefits: ['基础积分获取', '生日优惠', '专属客服', '优先发货', '会员专享价', '免费退换货', '专属活动', '积分双倍日', '一对一顾问', '新品优先体验', '年度礼包'],
      },
    ];

    for (const rule of defaultRules) {
      await this.createOrUpdateRule(rule);
    }

    logger.info(`[MemberLevelService] Default rules initialized: count=${defaultRules.length}`);
  }

  /**
   * 记录等级变更日志
   */
  private static async logLevelChange(params: {
    tenantId: string;
    shopId: string;
    userId: string;
    oldLevel: MemberLevel;
    newLevel: MemberLevel;
    oldScore: number;
    newScore: number;
    changeType: string;
    reason: string;
    traceId: string;
  }): Promise<void> {
    await db(this.LOG_TABLE).insert({
      id: this.generateId(),
      tenant_id: params.tenantId,
      shop_id: params.shopId,
      user_id: params.userId,
      trace_id: params.traceId,
      old_level: params.oldLevel,
      new_level: params.newLevel,
      old_score: params.oldScore,
      new_score: params.newScore,
      change_type: params.changeType,
      reason: params.reason,
      created_at: new Date(),
    });
  }

  /**
   * 获取等级顺序
   */
  private static getLevelOrder(level: MemberLevel): number {
    const order: Record<MemberLevel, number> = {
      BRONZE: 1,
      SILVER: 2,
      GOLD: 3,
      PLATINUM: 4,
      DIAMOND: 5,
    };
    return order[level] || 0;
  }

  /**
   * 生成唯一ID
   */
  private static generateId(): string {
    return `MLR-${Date.now()}-${Math.random().toString(36).substring(2, 10)}`;
  }

  /**
   * 将数据库行映射为规则对象
   */
  private static mapRowToRule(row: Record<string, any>): MemberLevelRule {
    return {
      id: row.id,
      tenantId: row.tenant_id,
      level: row.level,
      minScore: Number(row.min_score),
      maxScore: Number(row.max_score),
      discount: Number(row.discount),
      pointsMultiplier: Number(row.points_multiplier),
      benefits: JSON.parse(row.benefits),
      isActive: Boolean(row.is_active),
      createdAt: row.created_at,
      updatedAt: row.updated_at,
    };
  }
}
