import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import { RewardLedgerEntryMapper } from '../mappers/reward-ledger-entry.mapper';
import { RewardLedgerEntry } from '../../../domain/aggregates/reward-ledger-entry/reward-ledger-entry.aggregate';
import { IRewardLedgerEntryRepository } from '../../../domain/repositories/reward-ledger-entry.repository.interface';
import { RewardStatus } from '../../../domain/value-objects/reward-status.enum';
import { RightType } from '../../../domain/value-objects/right-type.enum';

@Injectable()
export class RewardLedgerEntryRepositoryImpl implements IRewardLedgerEntryRepository {
  constructor(private readonly prisma: PrismaService) {}

  async save(entry: RewardLedgerEntry): Promise<void> {
    const data = RewardLedgerEntryMapper.toPersistence(entry);

    if (entry.id) {
      await this.prisma.rewardLedgerEntry.update({
        where: { id: entry.id },
        data: {
          rewardStatus: data.rewardStatus,
          expireAt: data.expireAt,
          claimedAt: data.claimedAt,
          settledAt: data.settledAt,
          expiredAt: data.expiredAt,
          memo: data.memo,
        },
      });
    } else {
      const created = await this.prisma.rewardLedgerEntry.create({
        data: {
          userId: data.userId,
          accountSequence: data.accountSequence,
          sourceOrderNo: data.sourceOrderNo,
          sourceUserId: data.sourceUserId,
          rightType: data.rightType,
          usdtAmount: data.usdtAmount,
          hashpowerAmount: data.hashpowerAmount,
          rewardStatus: data.rewardStatus,
          expireAt: data.expireAt,
          claimedAt: data.claimedAt,
          settledAt: data.settledAt,
          expiredAt: data.expiredAt,
          memo: data.memo,
        },
      });
      entry.setId(created.id);
    }
  }

  async saveAll(entries: RewardLedgerEntry[]): Promise<void> {
    for (const entry of entries) {
      await this.save(entry);
    }
  }

  async findById(entryId: bigint): Promise<RewardLedgerEntry | null> {
    const raw = await this.prisma.rewardLedgerEntry.findUnique({
      where: { id: entryId },
    });
    return raw ? RewardLedgerEntryMapper.toDomain(raw) : null;
  }

  async findByUserId(
    userId: bigint,
    filters?: {
      status?: RewardStatus;
      rightType?: RightType;
      startDate?: Date;
      endDate?: Date;
    },
    pagination?: { page: number; pageSize: number },
  ): Promise<RewardLedgerEntry[]> {
    const where: any = { userId };

    if (filters?.status) {
      where.rewardStatus = filters.status;
    }
    if (filters?.rightType) {
      where.rightType = filters.rightType;
    }
    if (filters?.startDate || filters?.endDate) {
      where.createdAt = {};
      if (filters.startDate) {
        where.createdAt.gte = filters.startDate;
      }
      if (filters.endDate) {
        where.createdAt.lte = filters.endDate;
      }
    }

    const skip = pagination ? (pagination.page - 1) * pagination.pageSize : undefined;
    const take = pagination?.pageSize;

    const rawList = await this.prisma.rewardLedgerEntry.findMany({
      where,
      orderBy: { createdAt: 'desc' },
      skip,
      take,
    });

    return rawList.map(RewardLedgerEntryMapper.toDomain);
  }

  async findPendingByUserId(userId: bigint): Promise<RewardLedgerEntry[]> {
    const rawList = await this.prisma.rewardLedgerEntry.findMany({
      where: {
        userId,
        rewardStatus: RewardStatus.PENDING,
      },
      orderBy: { createdAt: 'desc' },
    });

    return rawList.map(RewardLedgerEntryMapper.toDomain);
  }

  async findSettleableByUserId(userId: bigint): Promise<RewardLedgerEntry[]> {
    const rawList = await this.prisma.rewardLedgerEntry.findMany({
      where: {
        userId,
        rewardStatus: RewardStatus.SETTLEABLE,
      },
      orderBy: { createdAt: 'desc' },
    });

    return rawList.map(RewardLedgerEntryMapper.toDomain);
  }

  async findExpiredPending(beforeDate: Date): Promise<RewardLedgerEntry[]> {
    const rawList = await this.prisma.rewardLedgerEntry.findMany({
      where: {
        rewardStatus: RewardStatus.PENDING,
        expireAt: {
          lte: beforeDate,
        },
      },
    });

    return rawList.map(RewardLedgerEntryMapper.toDomain);
  }

  async findBySourceOrderNo(sourceOrderNo: string): Promise<RewardLedgerEntry[]> {
    const rawList = await this.prisma.rewardLedgerEntry.findMany({
      where: { sourceOrderNo },
    });

    return rawList.map(RewardLedgerEntryMapper.toDomain);
  }

  async countByUserId(userId: bigint, status?: RewardStatus): Promise<number> {
    const where: any = { userId };
    if (status) {
      where.rewardStatus = status;
    }
    return this.prisma.rewardLedgerEntry.count({ where });
  }

  async findByAccountSequence(
    accountSequence: string,
    filters?: {
      status?: RewardStatus;
      rightType?: RightType;
      startDate?: Date;
      endDate?: Date;
    },
    pagination?: { page: number; pageSize: number },
  ): Promise<RewardLedgerEntry[]> {
    const where: any = { accountSequence };

    if (filters?.status) {
      where.rewardStatus = filters.status;
    }
    if (filters?.rightType) {
      where.rightType = filters.rightType;
    }
    if (filters?.startDate || filters?.endDate) {
      where.createdAt = {};
      if (filters.startDate) {
        where.createdAt.gte = filters.startDate;
      }
      if (filters.endDate) {
        where.createdAt.lte = filters.endDate;
      }
    }

    const skip = pagination ? (pagination.page - 1) * pagination.pageSize : undefined;
    const take = pagination?.pageSize;

    const rawList = await this.prisma.rewardLedgerEntry.findMany({
      where,
      orderBy: { createdAt: 'desc' },
      skip,
      take,
    });

    return rawList.map(RewardLedgerEntryMapper.toDomain);
  }

  async findPendingByAccountSequence(accountSequence: string): Promise<RewardLedgerEntry[]> {
    const rawList = await this.prisma.rewardLedgerEntry.findMany({
      where: {
        accountSequence,
        rewardStatus: RewardStatus.PENDING,
      },
      orderBy: { createdAt: 'desc' },
    });

    return rawList.map(RewardLedgerEntryMapper.toDomain);
  }

  async findSettleableByAccountSequence(accountSequence: string): Promise<RewardLedgerEntry[]> {
    const rawList = await this.prisma.rewardLedgerEntry.findMany({
      where: {
        accountSequence,
        rewardStatus: RewardStatus.SETTLEABLE,
      },
      orderBy: { createdAt: 'desc' },
    });

    return rawList.map(RewardLedgerEntryMapper.toDomain);
  }

  async countByAccountSequence(accountSequence: string, status?: RewardStatus): Promise<number> {
    const where: any = { accountSequence };
    if (status) {
      where.rewardStatus = status;
    }
    return this.prisma.rewardLedgerEntry.count({ where });
  }
}
