import { Injectable } from '@nestjs/common';
import { PrismaService } from '../prisma/prisma.service';
import {
  UserRepository,
  UserAggregate,
  UserStatus,
  KycStatus,
  AccountSequence,
  Phone,
} from '@/domain';

@Injectable()
export class PrismaUserRepository implements UserRepository {
  constructor(private readonly prisma: PrismaService) {}

  async findById(id: bigint): Promise<UserAggregate | null> {
    const user = await this.prisma.user.findUnique({
      where: { id },
    });

    if (!user) return null;
    return this.toDomain(user);
  }

  async findByPhone(phone: Phone): Promise<UserAggregate | null> {
    const user = await this.prisma.user.findUnique({
      where: { phone: phone.value },
    });

    if (!user) return null;
    return this.toDomain(user);
  }

  async findByAccountSequence(accountSequence: AccountSequence): Promise<UserAggregate | null> {
    const user = await this.prisma.user.findUnique({
      where: { accountSequence: accountSequence.value },
    });

    if (!user) return null;
    return this.toDomain(user);
  }

  async save(user: UserAggregate): Promise<UserAggregate> {
    const snapshot = user.toSnapshot();
    const data = {
      phone: snapshot.phone.value,
      passwordHash: snapshot.passwordHash,
      tradePasswordHash: snapshot.tradePasswordHash,
      accountSequence: snapshot.accountSequence.value,
      status: snapshot.status,
      kycStatus: snapshot.kycStatus,
      realName: snapshot.realName,
      idCardNo: snapshot.idCardNo,
      idCardFront: snapshot.idCardFront,
      idCardBack: snapshot.idCardBack,
      kycSubmittedAt: snapshot.kycSubmittedAt,
      kycVerifiedAt: snapshot.kycVerifiedAt,
      kycRejectReason: snapshot.kycRejectReason,
      loginFailCount: snapshot.loginFailCount,
      lockedUntil: snapshot.lockedUntil,
      lastLoginAt: snapshot.lastLoginAt,
      lastLoginIp: snapshot.lastLoginIp,
    };

    if (snapshot.id) {
      const updated = await this.prisma.user.update({
        where: { id: snapshot.id },
        data,
      });
      return this.toDomain(updated);
    } else {
      const created = await this.prisma.user.create({
        data,
      });
      return this.toDomain(created);
    }
  }

  async existsByPhone(phone: Phone): Promise<boolean> {
    const count = await this.prisma.user.count({
      where: { phone: phone.value },
    });
    return count > 0;
  }

  async existsByAccountSequence(accountSequence: AccountSequence): Promise<boolean> {
    const count = await this.prisma.user.count({
      where: { accountSequence: accountSequence.value },
    });
    return count > 0;
  }

  async getNextAccountSequence(): Promise<AccountSequence> {
    const now = new Date();
    const dateKey = this.getDateKey(now);

    // 使用事务保证原子性
    const result = await this.prisma.$transaction(async (tx) => {
      // 更新或创建计数器
      const counter = await tx.dailySequenceCounter.upsert({
        where: { dateKey },
        update: { lastSeq: { increment: 1 } },
        create: { dateKey, lastSeq: 1 },
      });

      return counter.lastSeq;
    });

    return AccountSequence.generateV2(now, result);
  }

  private getDateKey(date: Date): string {
    const year = String(date.getFullYear()).slice(-2);
    const month = String(date.getMonth() + 1).padStart(2, '0');
    const day = String(date.getDate()).padStart(2, '0');
    return `${year}${month}${day}`;
  }

  private toDomain(user: any): UserAggregate {
    return UserAggregate.reconstitute({
      id: user.id,
      phone: Phone.create(user.phone),
      passwordHash: user.passwordHash,
      tradePasswordHash: user.tradePasswordHash,
      accountSequence: AccountSequence.create(user.accountSequence),
      status: user.status as UserStatus,
      kycStatus: user.kycStatus as KycStatus,
      realName: user.realName,
      idCardNo: user.idCardNo,
      idCardFront: user.idCardFront,
      idCardBack: user.idCardBack,
      kycSubmittedAt: user.kycSubmittedAt,
      kycVerifiedAt: user.kycVerifiedAt,
      kycRejectReason: user.kycRejectReason,
      loginFailCount: user.loginFailCount,
      lockedUntil: user.lockedUntil,
      lastLoginAt: user.lastLoginAt,
      lastLoginIp: user.lastLoginIp,
      createdAt: user.createdAt,
      updatedAt: user.updatedAt,
    });
  }
}
