import { Injectable, Logger, NotFoundException, BadRequestException } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, DataSource, Between } from 'typeorm';
import { Cron } from '@nestjs/schedule';
import { Settlement } from '@/entities/settlement.entity';
import { SettlementItem } from '@/entities/settlement-item.entity';
import { Order } from '@/entities/order.entity';
import { AccountService } from './account.service';
import { TransactionService } from './transaction.service';
import dayjs from 'dayjs';

@Injectable()
export class SettlementService {
  private readonly logger = new Logger(SettlementService.name);

  constructor(
    @InjectRepository(Settlement)
    private settlementRepo: Repository<Settlement>,
    @InjectRepository(SettlementItem)
    private settlementItemRepo: Repository<SettlementItem>,
    @InjectRepository(Order)
    private orderRepo: Repository<Order>,
    private accountService: AccountService,
    private transactionService: TransactionService,
    private dataSource: DataSource,
  ) {}

  /**
   * 每周一凌晨2点自动执行周结算
   */
  @Cron('0 2 * * 1')
  async handleWeeklySettlement() {
    this.logger.log('开始执行周结算任务');

    try {
      const lastWeekStart = dayjs().subtract(1, 'week').startOf('week').format('YYYY-MM-DD');
      const lastWeekEnd = dayjs().subtract(1, 'week').endOf('week').format('YYYY-MM-DD');

      this.logger.log(`结算周期：${lastWeekStart} ~ ${lastWeekEnd}`);

      // 检查该周期是否已经结算过
      const existingSettlements = await this.settlementRepo.count({
        where: {
          periodStart: lastWeekStart,
          periodEnd: lastWeekEnd
        }
      });

      if (existingSettlements > 0) {
        this.logger.warn(`该周期 ${lastWeekStart} ~ ${lastWeekEnd} 已经结算过，跳过`);
        return {
          successCount: 0,
          failCount: 0,
          totalOrders: 0,
          skipped: true,
          reason: `该周期 ${lastWeekStart} ~ ${lastWeekEnd} 已经结算过`
        };
      }

      // 查询所有已完成且截止到上周末的订单
      this.logger.log(`查询条件: status=completed, checkout_at <= ${lastWeekEnd} 23:59:59`);

      const allOrders = await this.orderRepo
        .createQueryBuilder('o')
        .where('o.status = :status', { status: 'completed' })
        .andWhere('o.merchant_id IS NOT NULL')
        .andWhere('o.checkout_at IS NOT NULL')
        .andWhere('o.checkout_at <= :end', {
          end: `${lastWeekEnd} 23:59:59`
        })
        .getMany();

      this.logger.log(`查询到 ${allOrders.length} 个已完成订单`);

      if (allOrders.length === 0) {
        this.logger.log('没有需要结算的订单');
        return {
          successCount: 0,
          failCount: 0,
          totalOrders: 0,
          skipped: true,
          reason: '该周期内没有需要结算的订单'
        };
      }

      // 批量查询已结算的订单ID
      const orderIds = allOrders.map(o => o.id);
      const settledItems = await this.settlementItemRepo
        .createQueryBuilder('si')
        .select('si.order_id')
        .where('si.order_id IN (:...orderIds)', { orderIds })
        .getRawMany();

      const settledOrderIds = new Set(settledItems.map(item => item.order_id));

      // 过滤出未结算的订单，并按商家分组
      const ordersByMerchant: Record<number, Order[]> = {};
      let skippedCount = 0;

      for (const order of allOrders) {
        const merchantId = order.merchantId;

        if (!merchantId || isNaN(merchantId)) {
          this.logger.warn(`订单 ${order.id} 的 merchantId 无效: ${merchantId}`);
          continue;
        }

        // 检查该订单是否已经被结算过
        if (settledOrderIds.has(order.id)) {
          skippedCount++;
          continue;
        }

        if (!ordersByMerchant[merchantId]) {
          ordersByMerchant[merchantId] = [];
        }
        ordersByMerchant[merchantId].push(order);
      }

      this.logger.log(`共查询到 ${allOrders.length} 个订单，已结算 ${skippedCount} 个，待结算 ${allOrders.length - skippedCount} 个`);

      if (Object.keys(ordersByMerchant).length === 0) {
        this.logger.log('没有需要结算的订单（所有订单都已结算）');
        return {
          successCount: 0,
          failCount: 0,
          totalOrders: 0,
          skipped: true,
          reason: '没有需要结算的订单（所有订单都已结算）'
        };
      }

      let successCount = 0;
      let failCount = 0;
      const errors: Array<{ merchantId: number; error: string }> = [];

      for (const [merchantIdStr, merchantOrders] of Object.entries(ordersByMerchant)) {
        const merchantId = Number(merchantIdStr);

        try {
          await this.createSettlement(merchantId, merchantOrders, lastWeekStart, lastWeekEnd);
          this.logger.log(`商家 ${merchantId} 结算完成，订单数：${merchantOrders.length}`);
          successCount++;
        } catch (error) {
          const errorMessage = error instanceof Error ? error.message : String(error);
          const errorStack = error instanceof Error ? error.stack : '';
          this.logger.error(`商家 ${merchantId} 结算失败：${errorMessage}`);
          this.logger.error(`错误堆栈：${errorStack}`);
          failCount++;
          errors.push({
            merchantId,
            error: errorMessage
          });
        }
      }

      this.logger.log(`周结算任务执行完成，成功：${successCount}，失败：${failCount}`);
      return {
        successCount,
        failCount,
        totalOrders: allOrders.length - skippedCount,
        skipped: false,
        errors: errors.length > 0 ? errors : undefined
      };
    } catch (error) {
      this.logger.error(`周结算任务执行失败：${error.message}`);
      throw error;
    }
  }

  /**
   * 创建结算单
   */
  async createSettlement(
    merchantId: number,
    orders: Order[],
    periodStart: string,
    periodEnd: string
  ) {
    // 检查该商家在该周期是否已经结算过
    const existingSettlement = await this.settlementRepo.findOne({
      where: {
        merchantId,
        periodStart,
        periodEnd
      }
    });

    if (existingSettlement) {
      this.logger.warn(`商家 ${merchantId} 在周期 ${periodStart} ~ ${periodEnd} 已经结算过，跳过`);
      throw new BadRequestException(`商家 ${merchantId} 在该周期已经结算过`);
    }

    const queryRunner = this.dataSource.createQueryRunner();
    await queryRunner.connect();
    await queryRunner.startTransaction();

    try {
      const orderCount = orders.length;
      const orderAmount = orders.reduce((sum, o) => sum + Number(o.payAmount || 0), 0);
      const serviceFee = orders.reduce((sum, o) => sum + Number(o.serviceFee || 0), 0);
      const settlementAmount = orderAmount - serviceFee;

      const settlementNo = this.generateSettlementNo();

      const settlement = this.settlementRepo.create({
        settlementNo,
        merchantId,
        periodStart,
        periodEnd,
        orderCount,
        orderAmount,
        serviceFee,
        settlementAmount,
        status: 'settled',
        settledAt: new Date()
      });

      await queryRunner.manager.save(settlement);

      for (const order of orders) {
        const item = this.settlementItemRepo.create({
          settlementId: settlement.id,
          orderId: order.id,
          orderNo: order.orderNo,
          orderAmount: Number(order.payAmount),
          serviceFee: Number(order.serviceFee || 0),
          settlementAmount: Number(order.payAmount) - Number(order.serviceFee || 0)
        });
        await queryRunner.manager.save(item);
      }

      const transactionNo = this.transactionService.generateTransactionNo();

      // 给商家账户增加余额（订单金额 - 服务费）
      await this.accountService.addMerchantBalance(
        merchantId,
        settlementAmount,
        transactionNo,
        'settlement',
        settlement.id,
        settlementNo,
        `商家周结算：${periodStart} ~ ${periodEnd}`
      );

      // 给平台账户增加服务费收入
      await this.accountService.addPlatformServiceFee(
        serviceFee,
        transactionNo,
        'settlement',
        settlement.id,
        settlementNo,
        `商家 ${merchantId} 周结算服务费：${periodStart} ~ ${periodEnd}`
      );

      await queryRunner.commitTransaction();

      return settlement;
    } catch (error) {
      await queryRunner.rollbackTransaction();
      throw error;
    } finally {
      await queryRunner.release();
    }
  }

  /**
   * 获取结算记录列表
   */
  async getSettlements(params: {
    merchantId?: number;
    status?: string;
    startDate?: string;
    endDate?: string;
    page?: number;
    pageSize?: number;
  }) {
    const { merchantId, status, startDate, endDate, page = 1, pageSize = 20 } = params;

    const queryBuilder = this.settlementRepo.createQueryBuilder('s');

    if (merchantId) {
      queryBuilder.andWhere('s.merchant_id = :merchantId', { merchantId });
    }

    if (status) {
      queryBuilder.andWhere('s.status = :status', { status });
    }

    if (startDate && endDate) {
      queryBuilder.andWhere('s.period_start >= :startDate', { startDate });
      queryBuilder.andWhere('s.period_end <= :endDate', { endDate });
    }

    queryBuilder.orderBy('s.created_at', 'DESC');
    queryBuilder.skip((page - 1) * pageSize).take(pageSize);

    const [list, total] = await queryBuilder.getManyAndCount();

    return {
      list: list.map(item => ({
        ...item,
        orderAmount: Number(item.orderAmount),
        serviceFee: Number(item.serviceFee),
        settlementAmount: Number(item.settlementAmount)
      })),
      total,
      page,
      pageSize,
      totalPages: Math.ceil(total / pageSize)
    };
  }

  /**
   * 获取结算单详情
   */
  async getSettlementDetail(id: number) {
    const settlement = await this.settlementRepo.findOne({ where: { id } });

    if (!settlement) {
      throw new NotFoundException('结算单不存在');
    }

    const items = await this.settlementItemRepo.find({
      where: { settlementId: id },
      order: { createdAt: 'ASC' }
    });

    return {
      ...settlement,
      orderAmount: Number(settlement.orderAmount),
      serviceFee: Number(settlement.serviceFee),
      settlementAmount: Number(settlement.settlementAmount),
      items: items.map(item => ({
        ...item,
        orderAmount: Number(item.orderAmount),
        serviceFee: Number(item.serviceFee),
        settlementAmount: Number(item.settlementAmount)
      }))
    };
  }

  /**
   * 手动生成结算单（管理员操作）
   */
  async manualSettlement(params: {
    merchantId: number;
    periodStart: string;
    periodEnd: string;
  }) {
    const { merchantId, periodStart, periodEnd } = params;

    const orders = await this.orderRepo.find({
      where: {
        merchantId,
        status: 'completed',
        checkoutAt: Between(
          new Date(`${periodStart} 00:00:00`),
          new Date(`${periodEnd} 23:59:59`)
        )
      }
    });

    if (orders.length === 0) {
      throw new NotFoundException('该周期内没有已完成的订单');
    }

    return await this.createSettlement(merchantId, orders, periodStart, periodEnd);
  }

  /**
   * 获取商家待结算金额
   */
  async getPendingSettlementAmount(merchantId: number): Promise<number> {
    const lastSettlement = await this.settlementRepo.findOne({
      where: { merchantId },
      order: { settledAt: 'DESC' }
    });

    const startDate = lastSettlement
      ? dayjs(lastSettlement.periodEnd).add(1, 'day').format('YYYY-MM-DD')
      : '2020-01-01';

    const orders = await this.orderRepo.find({
      where: {
        merchantId,
        status: 'completed',
        checkoutAt: Between(
          new Date(`${startDate} 00:00:00`),
          new Date()
        )
      }
    });

    const orderAmount = orders.reduce((sum, o) => sum + Number(o.payAmount), 0);
    const serviceFee = orders.reduce((sum, o) => sum + Number(o.serviceFee || 0), 0);

    return orderAmount - serviceFee;
  }

  /**
   * 获取商家待结算订单列表
   */
  async getPendingSettlementOrders(merchantId: number, page: number = 1, pageSize: number = 20) {
    const lastSettlement = await this.settlementRepo.findOne({
      where: { merchantId },
      order: { settledAt: 'DESC' }
    });

    const startDate = lastSettlement
      ? dayjs(lastSettlement.periodEnd).add(1, 'day').format('YYYY-MM-DD')
      : '2020-01-01';

    const queryBuilder = this.orderRepo.createQueryBuilder('order')
      .leftJoinAndSelect('order.room', 'room')
      .leftJoinAndSelect('order.user', 'user')
      .where('order.merchantId = :merchantId', { merchantId })
      .andWhere('order.status = :status', { status: 'completed' })
      .andWhere('order.checkoutAt IS NOT NULL')
      .andWhere('order.checkoutAt >= :startDate', { startDate: `${startDate} 00:00:00` })
      .andWhere('order.checkoutAt <= :endDate', { endDate: new Date() })
      .orderBy('order.checkoutAt', 'DESC');

    const skip = (page - 1) * pageSize;
    queryBuilder.skip(skip).take(pageSize);

    const [orders, total] = await queryBuilder.getManyAndCount();

    return {
      list: orders.map(order => ({
        id: order.id,
        orderNo: order.orderNo,
        roomName: order.room?.name || '',
        checkInDate: order.checkInDate,
        checkOutDate: order.checkOutDate,
        nights: order.nights,
        payAmount: Number(order.payAmount),
        serviceFee: Number(order.serviceFee || 0),
        merchantIncome: Number(order.merchantIncome || 0),
        checkoutAt: order.checkoutAt,
        contactName: order.contactName,
        contactPhone: order.contactPhone,
      })),
      total,
      page,
      pageSize,
      totalPages: Math.ceil(total / pageSize)
    };
  }

  /**
   * 获取商家待结算订单统计
   */
  async getPendingSettlementSummary(merchantId: number) {
    const lastSettlement = await this.settlementRepo.findOne({
      where: { merchantId },
      order: { settledAt: 'DESC' }
    });

    const startDate = lastSettlement
      ? dayjs(lastSettlement.periodEnd).add(1, 'day').format('YYYY-MM-DD')
      : '2020-01-01';

    const orders = await this.orderRepo
      .createQueryBuilder('o')
      .where('o.merchant_id = :merchantId', { merchantId })
      .andWhere('o.status = :status', { status: 'completed' })
      .andWhere('o.checkout_at IS NOT NULL')
      .andWhere('o.checkout_at >= :startDate', { startDate: `${startDate} 00:00:00` })
      .andWhere('o.checkout_at <= :endDate', { endDate: new Date() })
      .getMany();

    const orderCount = orders.length;
    const totalPayAmount = orders.reduce((sum, o) => sum + Number(o.payAmount), 0);
    const totalServiceFee = orders.reduce((sum, o) => sum + Number(o.serviceFee || 0), 0);
    const totalMerchantIncome = orders.reduce((sum, o) => sum + Number(o.merchantIncome || 0), 0);

    return {
      orderCount,
      totalPayAmount,
      totalServiceFee,
      totalMerchantIncome,
      startDate,
      lastSettlementDate: lastSettlement?.settledAt || null,
    };
  }

  /**
   * 预览周结算数据（不实际执行）
   */
  async previewWeeklySettlement() {
    try {
      const lastWeekStart = dayjs().subtract(1, 'week').startOf('week').format('YYYY-MM-DD');
      const lastWeekEnd = dayjs().subtract(1, 'week').endOf('week').format('YYYY-MM-DD');

      this.logger.log(`预览周结算：${lastWeekStart} ~ ${lastWeekEnd}`);

      // 检查该周期是否已经结算过
      const existingSettlements = await this.settlementRepo.count({
        where: {
          periodStart: lastWeekStart,
          periodEnd: lastWeekEnd
        }
      });

      if (existingSettlements > 0) {
        this.logger.warn(`该周期 ${lastWeekStart} ~ ${lastWeekEnd} 已经结算过`);
        throw new BadRequestException(`该周期 ${lastWeekStart} ~ ${lastWeekEnd} 已经结算过，无法重复结算`);
      }

      // 不使用 join，直接查询订单
      const queryBuilder = this.orderRepo
        .createQueryBuilder('o')
        .where('o.status = :status', { status: 'completed' })
        .andWhere('o.merchant_id IS NOT NULL')
        .andWhere('o.checkout_at BETWEEN :start AND :end', {
          start: `${lastWeekStart} 00:00:00`,
          end: `${lastWeekEnd} 23:59:59`
        });

      // 打印 SQL 语句
      const sql = queryBuilder.getSql();
      this.logger.log(`生成的 SQL: ${sql}`);
      this.logger.log(`参数: ${JSON.stringify(queryBuilder.getParameters())}`);

      const orders = await queryBuilder.getMany();

      this.logger.log(`找到 ${orders.length} 个已完成订单`);

      if (orders.length === 0) {
        throw new BadRequestException('该周期内没有需要结算的订单');
      }

      const ordersByMerchant = orders.reduce((acc, order) => {
        const merchantId = order.merchantId;
        if (!merchantId || isNaN(merchantId)) {
          this.logger.warn(`订单 ${order.id} 的 merchantId 无效: ${merchantId}`);
          return acc;
        }
        if (!acc[merchantId]) {
          acc[merchantId] = [];
        }
        acc[merchantId].push(order);
        return acc;
      }, {} as Record<number, Order[]>);

      // 获取所有商家ID
      const merchantIds = Object.keys(ordersByMerchant).map(id => Number(id));

      // 批量查询商家信息
      const merchantsData = await this.dataSource
        .getRepository('Merchant')
        .createQueryBuilder('m')
        .where('m.id IN (:...ids)', { ids: merchantIds })
        .getMany();

      const merchantMap = new Map(merchantsData.map((m: any) => [m.id, m]));

      const merchants = Object.entries(ordersByMerchant).map(([merchantIdStr, merchantOrders]) => {
        const merchantId = Number(merchantIdStr);
        const merchant = merchantMap.get(merchantId);
        const orderCount = merchantOrders.length;
        const orderAmount = merchantOrders.reduce((sum, o) => sum + Number(o.payAmount || 0), 0);
        const serviceFee = merchantOrders.reduce((sum, o) => sum + Number(o.serviceFee || 0), 0);
        const settlementAmount = orderAmount - serviceFee;

        return {
          merchantId,
          merchantName: merchant?.shopName || '未知商家',
          orderCount,
          orderAmount,
          serviceFee,
          settlementAmount
        };
      });

      const totalAmount = merchants.reduce((sum, m) => sum + m.orderAmount, 0);
      const totalServiceFee = merchants.reduce((sum, m) => sum + m.serviceFee, 0);
      const totalSettlementAmount = merchants.reduce((sum, m) => sum + m.settlementAmount, 0);

      return {
        periodStart: lastWeekStart,
        periodEnd: lastWeekEnd,
        totalOrders: orders.length,
        totalAmount,
        totalServiceFee,
        totalSettlementAmount,
        merchants
      };
    } catch (error) {
      this.logger.error(`预览周结算失败: ${error.message}`);
      this.logger.error(`错误堆栈: ${error.stack}`);
      throw error;
    }
  }

  /**
   * 生成结算单号
   */
  private generateSettlementNo(): string {
    const timestamp = Date.now();
    const random = Math.floor(Math.random() * 10000).toString().padStart(4, '0');
    return `STL${timestamp}${random}`;
  }
}
