import { Injectable } from '@nestjs/common';
import { Prisma } from '@prisma/client';
import { PrismaService } from '../prisma/prisma.service';
import {
  IPlantingOrderRepository,
  GlobalPlantingStats,
  DailyPlantingStats,
  StatusDistribution,
  TrendPeriod,
  TrendDataPoint,
} from '../../../domain/repositories/planting-order.repository.interface';
import { PlantingOrder } from '../../../domain/aggregates/planting-order.aggregate';
import { PlantingOrderStatus } from '../../../domain/value-objects/planting-order-status.enum';
import { PlantingOrderMapper } from '../mappers/planting-order.mapper';

@Injectable()
export class PlantingOrderRepositoryImpl implements IPlantingOrderRepository {
  constructor(private readonly prisma: PrismaService) {}

  async save(order: PlantingOrder): Promise<void> {
    const { orderData, allocations } =
      PlantingOrderMapper.toPersistence(order);

    if (order.id) {
      // 更新
      await this.prisma.plantingOrder.update({
        where: { id: order.id },
        data: {
          status: orderData.status,
          selectedProvince: orderData.selectedProvince,
          selectedCity: orderData.selectedCity,
          provinceCitySelectedAt: orderData.provinceCitySelectedAt,
          provinceCityConfirmedAt: orderData.provinceCityConfirmedAt,
          poolInjectionBatchId: orderData.poolInjectionBatchId,
          poolInjectionScheduledTime: orderData.poolInjectionScheduledTime,
          poolInjectionActualTime: orderData.poolInjectionActualTime,
          poolInjectionTxHash: orderData.poolInjectionTxHash,
          miningEnabledAt: orderData.miningEnabledAt,
          paidAt: orderData.paidAt,
          fundAllocatedAt: orderData.fundAllocatedAt,
        },
      });

      // 如果有新的资金分配，插入
      if (allocations.length > 0) {
        const existingAllocations = await this.prisma.fundAllocation.count({
          where: { orderId: order.id },
        });

        if (existingAllocations === 0) {
          await this.prisma.fundAllocation.createMany({
            data: allocations.map((a) => ({
              orderId: order.id!,
              targetType: a.targetType,
              amount: a.amount,
              targetAccountId: a.targetAccountId,
              metadata: a.metadata ?? Prisma.DbNull,
            })),
          });
        }
      }
    } else {
      // 创建
      const created = await this.prisma.plantingOrder.create({
        data: {
          orderNo: orderData.orderNo,
          userId: orderData.userId,
          accountSequence: orderData.accountSequence,
          treeCount: orderData.treeCount,
          totalAmount: orderData.totalAmount,
          status: orderData.status,
          selectedProvince: orderData.selectedProvince,
          selectedCity: orderData.selectedCity,
          provinceCitySelectedAt: orderData.provinceCitySelectedAt,
          provinceCityConfirmedAt: orderData.provinceCityConfirmedAt,
          poolInjectionBatchId: orderData.poolInjectionBatchId,
          poolInjectionScheduledTime: orderData.poolInjectionScheduledTime,
          poolInjectionActualTime: orderData.poolInjectionActualTime,
          poolInjectionTxHash: orderData.poolInjectionTxHash,
          miningEnabledAt: orderData.miningEnabledAt,
          paidAt: orderData.paidAt,
          fundAllocatedAt: orderData.fundAllocatedAt,
        },
      });

      order.setId(created.id);
    }
  }

  async findById(orderId: bigint): Promise<PlantingOrder | null> {
    const order = await this.prisma.plantingOrder.findUnique({
      where: { id: orderId },
      include: { fundAllocations: true },
    });

    return order ? PlantingOrderMapper.toDomain(order) : null;
  }

  async findByOrderNo(orderNo: string): Promise<PlantingOrder | null> {
    const order = await this.prisma.plantingOrder.findUnique({
      where: { orderNo },
      include: { fundAllocations: true },
    });

    return order ? PlantingOrderMapper.toDomain(order) : null;
  }

  async findByUserId(
    userId: bigint,
    page: number = 1,
    pageSize: number = 10,
  ): Promise<PlantingOrder[]> {
    const orders = await this.prisma.plantingOrder.findMany({
      where: { userId },
      include: { fundAllocations: true },
      orderBy: { createdAt: 'desc' },
      skip: (page - 1) * pageSize,
      take: pageSize,
    });

    return orders.map(PlantingOrderMapper.toDomain);
  }

  async findByStatus(
    status: PlantingOrderStatus,
    limit: number = 100,
  ): Promise<PlantingOrder[]> {
    const orders = await this.prisma.plantingOrder.findMany({
      where: { status },
      include: { fundAllocations: true },
      orderBy: { createdAt: 'asc' },
      take: limit,
    });

    return orders.map(PlantingOrderMapper.toDomain);
  }

  async findPendingPoolScheduling(): Promise<PlantingOrder[]> {
    const orders = await this.prisma.plantingOrder.findMany({
      where: {
        status: PlantingOrderStatus.FUND_ALLOCATED,
        poolInjectionBatchId: null,
      },
      include: { fundAllocations: true },
      orderBy: { createdAt: 'asc' },
    });

    return orders.map(PlantingOrderMapper.toDomain);
  }

  async findByBatchId(batchId: bigint): Promise<PlantingOrder[]> {
    const orders = await this.prisma.plantingOrder.findMany({
      where: { poolInjectionBatchId: batchId },
      include: { fundAllocations: true },
      orderBy: { createdAt: 'asc' },
    });

    return orders.map(PlantingOrderMapper.toDomain);
  }

  async findReadyForMining(): Promise<PlantingOrder[]> {
    const orders = await this.prisma.plantingOrder.findMany({
      where: {
        status: PlantingOrderStatus.POOL_INJECTED,
        miningEnabledAt: null,
      },
      include: { fundAllocations: true },
      orderBy: { createdAt: 'asc' },
    });

    return orders.map(PlantingOrderMapper.toDomain);
  }

  async countTreesByUserId(userId: bigint): Promise<number> {
    const result = await this.prisma.plantingOrder.aggregate({
      where: {
        userId,
        status: {
          notIn: [PlantingOrderStatus.CANCELLED],
        },
      },
      _sum: {
        treeCount: true,
      },
    });

    return result._sum.treeCount || 0;
  }

  async countByUserId(userId: bigint): Promise<number> {
    return this.prisma.plantingOrder.count({
      where: {
        userId,
        status: {
          notIn: [PlantingOrderStatus.CANCELLED],
        },
      },
    });
  }

  async findPaidOrdersWithoutContract(userId: bigint): Promise<PlantingOrder[]> {
    // 已支付及之后状态（不包括 CREATED, PROVINCE_CITY_CONFIRMED, CANCELLED）
    const paidStatuses = [
      PlantingOrderStatus.PAID,
      PlantingOrderStatus.FUND_ALLOCATED,
      PlantingOrderStatus.POOL_SCHEDULED,
      PlantingOrderStatus.POOL_INJECTED,
      PlantingOrderStatus.MINING_ENABLED,
    ];

    // 查找已支付但没有对应合同签署任务的订单
    const orders = await this.prisma.plantingOrder.findMany({
      where: {
        userId,
        status: { in: paidStatuses },
        // 使用子查询排除已有合同的订单
        NOT: {
          orderNo: {
            in: await this.prisma.contractSigningTask
              .findMany({
                where: { userId },
                select: { orderNo: true },
              })
              .then((tasks) => tasks.map((t) => t.orderNo)),
          },
        },
      },
      include: { fundAllocations: true },
      orderBy: { createdAt: 'asc' },
    });

    return orders.map(PlantingOrderMapper.toDomain);
  }

  /**
   * 获取全局统计数据（从订单表实时聚合）
   * 只统计已支付（PAID）及之后状态的订单
   */
  async getGlobalStats(): Promise<GlobalPlantingStats> {
    const paidStatuses = [
      PlantingOrderStatus.PAID,
      PlantingOrderStatus.FUND_ALLOCATED,
      PlantingOrderStatus.POOL_SCHEDULED,
      PlantingOrderStatus.POOL_INJECTED,
      PlantingOrderStatus.MINING_ENABLED,
    ];

    const result = await this.prisma.plantingOrder.aggregate({
      where: {
        status: { in: paidStatuses },
      },
      _sum: {
        treeCount: true,
        totalAmount: true,
      },
      _count: {
        id: true,
      },
    });

    return {
      totalTreeCount: result._sum.treeCount || 0,
      totalOrderCount: result._count.id || 0,
      totalAmount: result._sum.totalAmount?.toString() || '0',
    };
  }

  /**
   * 获取今日统计数据
   */
  async getTodayStats(): Promise<DailyPlantingStats> {
    const today = new Date();
    today.setHours(0, 0, 0, 0);

    const tomorrow = new Date(today);
    tomorrow.setDate(tomorrow.getDate() + 1);

    const paidStatuses = [
      PlantingOrderStatus.PAID,
      PlantingOrderStatus.FUND_ALLOCATED,
      PlantingOrderStatus.POOL_SCHEDULED,
      PlantingOrderStatus.POOL_INJECTED,
      PlantingOrderStatus.MINING_ENABLED,
    ];

    const result = await this.prisma.plantingOrder.aggregate({
      where: {
        status: { in: paidStatuses },
        paidAt: {
          gte: today,
          lt: tomorrow,
        },
      },
      _sum: {
        treeCount: true,
        totalAmount: true,
      },
      _count: {
        id: true,
      },
    });

    return {
      treeCount: result._sum.treeCount || 0,
      orderCount: result._count.id || 0,
      amount: result._sum.totalAmount?.toString() || '0',
    };
  }

  /**
   * 获取本月统计数据
   * [2026-01-06] 新增
   */
  async getMonthStats(): Promise<DailyPlantingStats> {
    const now = new Date();
    const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
    const monthEnd = new Date(now.getFullYear(), now.getMonth() + 1, 1);

    const paidStatuses = [
      PlantingOrderStatus.PAID,
      PlantingOrderStatus.FUND_ALLOCATED,
      PlantingOrderStatus.POOL_SCHEDULED,
      PlantingOrderStatus.POOL_INJECTED,
      PlantingOrderStatus.MINING_ENABLED,
    ];

    const result = await this.prisma.plantingOrder.aggregate({
      where: {
        status: { in: paidStatuses },
        paidAt: {
          gte: monthStart,
          lt: monthEnd,
        },
      },
      _sum: {
        treeCount: true,
        totalAmount: true,
      },
      _count: {
        id: true,
      },
    });

    return {
      treeCount: result._sum.treeCount || 0,
      orderCount: result._count.id || 0,
      amount: result._sum.totalAmount?.toString() || '0',
    };
  }

  /**
   * 按状态获取订单分布统计
   */
  async getStatusDistribution(): Promise<StatusDistribution> {
    const statusCounts = await this.prisma.plantingOrder.groupBy({
      by: ['status'],
      _sum: {
        treeCount: true,
      },
      where: {
        status: {
          in: [
            PlantingOrderStatus.PAID,
            PlantingOrderStatus.FUND_ALLOCATED,
            PlantingOrderStatus.POOL_SCHEDULED,
            PlantingOrderStatus.POOL_INJECTED,
            PlantingOrderStatus.MINING_ENABLED,
          ],
        },
      },
    });

    const distribution: StatusDistribution = {
      paid: 0,
      fundAllocated: 0,
      poolScheduled: 0,
      poolInjected: 0,
      miningEnabled: 0,
    };

    for (const item of statusCounts) {
      const count = item._sum.treeCount || 0;
      switch (item.status) {
        case PlantingOrderStatus.PAID:
          distribution.paid = count;
          break;
        case PlantingOrderStatus.FUND_ALLOCATED:
          distribution.fundAllocated = count;
          break;
        case PlantingOrderStatus.POOL_SCHEDULED:
          distribution.poolScheduled = count;
          break;
        case PlantingOrderStatus.POOL_INJECTED:
          distribution.poolInjected = count;
          break;
        case PlantingOrderStatus.MINING_ENABLED:
          distribution.miningEnabled = count;
          break;
      }
    }

    return distribution;
  }

  /**
   * 获取趋势数据
   * [2026-01-06] 新增
   */
  async getTrendData(period: TrendPeriod): Promise<TrendDataPoint[]> {
    const paidStatuses = [
      PlantingOrderStatus.PAID,
      PlantingOrderStatus.FUND_ALLOCATED,
      PlantingOrderStatus.POOL_SCHEDULED,
      PlantingOrderStatus.POOL_INJECTED,
      PlantingOrderStatus.MINING_ENABLED,
    ];

    const now = new Date();
    let startDate: Date;
    let dateFormat: string;
    let groupByExpression: string;

    // 根据不同维度设置时间范围和分组方式
    switch (period) {
      case 'day':
        // 最近30天，按天分组
        startDate = new Date(now);
        startDate.setDate(startDate.getDate() - 29);
        startDate.setHours(0, 0, 0, 0);
        dateFormat = 'YYYY-MM-DD';
        groupByExpression = `TO_CHAR("paid_at", 'YYYY-MM-DD')`;
        break;
      case 'week':
        // 最近12周，按周分组
        startDate = new Date(now);
        startDate.setDate(startDate.getDate() - 83); // ~12周
        startDate.setHours(0, 0, 0, 0);
        dateFormat = 'IYYY-IW';
        groupByExpression = `TO_CHAR("paid_at", 'IYYY-"W"IW')`;
        break;
      case 'month':
        // 最近12个月，按月分组
        startDate = new Date(now.getFullYear(), now.getMonth() - 11, 1);
        dateFormat = 'YYYY-MM';
        groupByExpression = `TO_CHAR("paid_at", 'YYYY-MM')`;
        break;
      case 'quarter':
        // 最近8季度，按季度分组
        startDate = new Date(now.getFullYear() - 2, 0, 1);
        dateFormat = 'YYYY-QQ';
        groupByExpression = `TO_CHAR("paid_at", 'YYYY-"Q"') || EXTRACT(QUARTER FROM "paid_at")`;
        break;
      case 'year':
        // 最近5年，按年分组
        startDate = new Date(now.getFullYear() - 4, 0, 1);
        dateFormat = 'YYYY';
        groupByExpression = `TO_CHAR("paid_at", 'YYYY')`;
        break;
      default:
        throw new Error(`Invalid period: ${period}`);
    }

    // 使用原生SQL进行分组查询
    // 注意：表名是 planting_orders（复数），参照 @@map("planting_orders")
    const results = await this.prisma.$queryRawUnsafe<
      Array<{
        label: string;
        tree_count: bigint;
        order_count: bigint;
        total_amount: bigint | null;
      }>
    >(`
      SELECT
        ${groupByExpression} as label,
        COALESCE(SUM(tree_count), 0) as tree_count,
        COUNT(*) as order_count,
        COALESCE(SUM(total_amount), 0) as total_amount
      FROM planting_orders
      WHERE status IN (${paidStatuses.map(s => `'${s}'`).join(', ')})
        AND paid_at >= $1
        AND paid_at IS NOT NULL
      GROUP BY ${groupByExpression}
      ORDER BY label ASC
    `, startDate);

    // 转换结果
    return results.map(r => ({
      label: r.label,
      treeCount: Number(r.tree_count),
      orderCount: Number(r.order_count),
      amount: (r.total_amount ?? 0).toString(),
    }));
  }
}
