import { Injectable, Logger } from '@nestjs/common';
import { PrismaService } from '@/infrastructure/persistence/prisma/prisma.service';
import {
  UserAccountRepository,
  Pagination,
  ReferralLinkData,
  CreateReferralLinkParams,
} from '@/domain/repositories/user-account.repository.interface';
import { UserAccount } from '@/domain/aggregates/user-account/user-account.aggregate';
import { WalletAddress } from '@/domain/entities/wallet-address.entity';
import {
  UserId,
  AccountSequence,
  PhoneNumber,
  ReferralCode,
  ChainType,
  AccountStatus,
  KYCStatus,
  DeviceInfo,
  KYCInfo,
  AddressStatus,
} from '@/domain/value-objects';
import {
  toMpcSignatureString,
  fromMpcSignatureString,
} from '../entities/wallet-address.entity';

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

  constructor(private readonly prisma: PrismaService) {}

  async save(account: UserAccount): Promise<void> {
    const devices = account.getAllDevices();
    const isNewAccount = account.userId.value === BigInt(0);

    this.logger.log(
      `[SAVE] Starting save: sequence=${account.accountSequence.value}, isNew=${isNewAccount}, phone=${account.phoneNumber?.value || 'null'}`,
    );

    try {
      await this.prisma.$transaction(async (tx) => {
        let savedUserId: bigint;

        if (isNewAccount) {
          // 新账户，让数据库自动生成userId
          this.logger.log(`[SAVE] Creating new account in transaction...`);

          const created = await tx.userAccount.create({
            data: {
              accountSequence: account.accountSequence.value,
              phoneNumber: account.phoneNumber?.value || null,
              nickname: account.nickname,
              avatarUrl: account.avatarUrl,
              inviterSequence: account.inviterSequence?.value || null,
              referralCode: account.referralCode.value,
              kycStatus: account.kycStatus,
              realName: account.kycInfo?.realName || null,
              idCardNumber: account.kycInfo?.idCardNumber || null,
              idCardFrontUrl: account.kycInfo?.idCardFrontUrl || null,
              idCardBackUrl: account.kycInfo?.idCardBackUrl || null,
              status: account.status,
              registeredAt: account.registeredAt,
              lastLoginAt: account.lastLoginAt,
            },
          });
          savedUserId = created.userId;

          this.logger.log(
            `[SAVE] Account created: userId=${savedUserId}, sequence=${created.accountSequence}`,
          );

          // 更新聚合根的userId
          (account as any)._userId = UserId.create(savedUserId);
        } else {
          // 已存在的账户，更新
          this.logger.log(
            `[SAVE] Updating existing account: userId=${account.userId.value}`,
          );

          await tx.userAccount.update({
            where: { userId: BigInt(account.userId.value) },
            data: {
              phoneNumber: account.phoneNumber?.value || null,
              nickname: account.nickname,
              avatarUrl: account.avatarUrl,
              kycStatus: account.kycStatus,
              realName: account.kycInfo?.realName || null,
              idCardNumber: account.kycInfo?.idCardNumber || null,
              idCardFrontUrl: account.kycInfo?.idCardFrontUrl || null,
              idCardBackUrl: account.kycInfo?.idCardBackUrl || null,
              status: account.status,
              lastLoginAt: account.lastLoginAt,
            },
          });
          savedUserId = account.userId.value;

          this.logger.log(`[SAVE] Account updated: userId=${savedUserId}`);
        }

        // Sync devices
        this.logger.log(
          `[SAVE] Syncing ${devices.length} devices for userId=${savedUserId}`,
        );

        await tx.userDevice.deleteMany({ where: { userId: savedUserId } });
        if (devices.length > 0) {
          await tx.userDevice.createMany({
            data: devices.map((d) => {
              // 从 deviceInfo JSON 中提取冗余字段便于查询
              const info = d.deviceInfo || {};
              return {
                userId: savedUserId,
                deviceId: d.deviceId,
                deviceName: d.deviceName,
                deviceInfo: d.deviceInfo
                  ? JSON.parse(JSON.stringify(d.deviceInfo))
                  : null, // 100% 保存完整 JSON
                platform: (info as any).platform || null,
                deviceModel: (info as any).model || null,
                osVersion: (info as any).osVersion || null,
                appVersion: (info as any).appVersion || null,
                screenWidth: (info as any).screenWidth || null,
                screenHeight: (info as any).screenHeight || null,
                locale: (info as any).locale || null,
                timezone: (info as any).timezone || null,
                addedAt: d.addedAt,
                lastActiveAt: d.lastActiveAt,
              };
            }),
          });
        }

        this.logger.log(`[SAVE] Devices synced successfully`);
      });

      this.logger.log(
        `[SAVE] Transaction committed: sequence=${account.accountSequence.value}, userId=${account.userId.value}`,
      );
    } catch (error) {
      this.logger.error(
        `[SAVE] Transaction FAILED: sequence=${account.accountSequence.value}, error=${error.message}`,
      );
      this.logger.error(`[SAVE] Stack: ${error.stack}`);
      throw error; // 确保异常被抛出
    }
  }

  async saveWallets(userId: UserId, wallets: WalletAddress[]): Promise<void> {
    // 使用事务保护，确保所有钱包地址要么全部保存成功，要么全部回滚
    await this.prisma.$transaction(async (tx) => {
      for (const w of wallets) {
        const sig = fromMpcSignatureString(w.mpcSignature);

        // 幂等性检查：如果该链类型的地址已存在，跳过
        const existing = await tx.walletAddress.findFirst({
          where: {
            userId: BigInt(userId.value),
            chainType: w.chainType,
          },
        });

        if (existing) {
          // 已存在，跳过（幂等性）
          continue;
        }

        // 创建新的钱包地址
        await tx.walletAddress.create({
          data: {
            userId: BigInt(userId.value),
            chainType: w.chainType,
            address: w.address,
            publicKey: w.publicKey,
            addressDigest: w.addressDigest,
            mpcSignatureR: sig.r,
            mpcSignatureS: sig.s,
            mpcSignatureV: sig.v,
            status: w.status,
            boundAt: w.boundAt,
          },
        });
      }
    });
  }

  async findById(userId: UserId): Promise<UserAccount | null> {
    const data = await this.prisma.userAccount.findUnique({
      where: { userId: BigInt(userId.value) },
      include: { devices: true, walletAddresses: true },
    });
    return data ? this.toDomain(data) : null;
  }

  async findByAccountSequence(
    sequence: AccountSequence,
  ): Promise<UserAccount | null> {
    const data = await this.prisma.userAccount.findUnique({
      where: { accountSequence: sequence.value },
      include: { devices: true, walletAddresses: true },
    });
    return data ? this.toDomain(data) : null;
  }

  async findByDeviceId(deviceId: string): Promise<UserAccount | null> {
    const device = await this.prisma.userDevice.findFirst({
      where: { deviceId },
    });
    if (!device) return null;
    return this.findById(UserId.create(device.userId.toString()));
  }

  async findByPhoneNumber(
    phoneNumber: PhoneNumber,
  ): Promise<UserAccount | null> {
    const data = await this.prisma.userAccount.findUnique({
      where: { phoneNumber: phoneNumber.value },
      include: { devices: true, walletAddresses: true },
    });
    return data ? this.toDomain(data) : null;
  }

  async findByReferralCode(
    referralCode: ReferralCode,
  ): Promise<UserAccount | null> {
    const data = await this.prisma.userAccount.findUnique({
      where: { referralCode: referralCode.value },
      include: { devices: true, walletAddresses: true },
    });
    return data ? this.toDomain(data) : null;
  }

  async findByWalletAddress(
    chainType: ChainType,
    address: string,
  ): Promise<UserAccount | null> {
    const wallet = await this.prisma.walletAddress.findUnique({
      where: { uk_chain_address: { chainType, address } },
    });
    if (!wallet) return null;
    return this.findById(UserId.create(wallet.userId.toString()));
  }

  async getMaxAccountSequence(): Promise<AccountSequence | null> {
    const result = await this.prisma.userAccount.aggregate({
      _max: { accountSequence: true },
    });
    return result._max.accountSequence
      ? AccountSequence.create(result._max.accountSequence)
      : null;
  }

  async getNextAccountSequence(): Promise<AccountSequence> {
    const now = new Date();
    const year = String(now.getFullYear()).slice(-2);
    const month = String(now.getMonth() + 1).padStart(2, '0');
    const day = String(now.getDate()).padStart(2, '0');
    const dateKey = `${year}${month}${day}`;

    const result = await this.prisma.$transaction(async (tx) => {
      // 尝试更新当日记录，如果不存在则创建
      const existing = await tx.accountSequenceGenerator.findUnique({
        where: { dateKey },
      });

      if (existing) {
        const updated = await tx.accountSequenceGenerator.update({
          where: { dateKey },
          data: { currentSequence: { increment: 1 } },
        });
        return updated.currentSequence;
      } else {
        // 当日第一个用户，创建新记录
        const created = await tx.accountSequenceGenerator.create({
          data: { dateKey, currentSequence: 0 },
        });
        return created.currentSequence;
      }
    });

    return AccountSequence.generate(now, result);
  }

  async findUsers(
    filters?: {
      status?: AccountStatus;
      kycStatus?: KYCStatus;
      keyword?: string;
    },
    pagination?: Pagination,
  ): Promise<UserAccount[]> {
    const where: any = {};
    if (filters?.status) where.status = filters.status;
    if (filters?.kycStatus) where.kycStatus = filters.kycStatus;
    if (filters?.keyword) {
      where.OR = [
        { nickname: { contains: filters.keyword } },
        { phoneNumber: { contains: filters.keyword } },
      ];
    }

    const data = await this.prisma.userAccount.findMany({
      where,
      include: { devices: true, walletAddresses: true },
      skip: pagination ? (pagination.page - 1) * pagination.limit : undefined,
      take: pagination?.limit,
      orderBy: { registeredAt: 'desc' },
    });

    return data.map((d) => this.toDomain(d));
  }

  async countUsers(filters?: {
    status?: AccountStatus;
    kycStatus?: KYCStatus;
  }): Promise<number> {
    const where: any = {};
    if (filters?.status) where.status = filters.status;
    if (filters?.kycStatus) where.kycStatus = filters.kycStatus;
    return this.prisma.userAccount.count({ where });
  }

  private toDomain(data: any): UserAccount {
    const devices = data.devices.map((d: any) => {
      // 优先使用完整的 deviceInfo JSON，保持原样
      return new DeviceInfo(
        d.deviceId,
        d.deviceName || '未命名设备',
        d.addedAt,
        d.lastActiveAt,
        d.deviceInfo || undefined, // 100% 保持原样
      );
    });

    const wallets = data.walletAddresses.map((w: any) =>
      WalletAddress.reconstruct({
        addressId: w.addressId.toString(),
        userId: w.userId.toString(),
        chainType: w.chainType as ChainType,
        address: w.address,
        publicKey: w.publicKey || '',
        addressDigest: w.addressDigest || '',
        mpcSignature: toMpcSignatureString(w), // 64 bytes hex (r + s)
        status: w.status as AddressStatus,
        boundAt: w.boundAt,
      }),
    );

    const kycInfo =
      data.realName && data.idCardNumber
        ? KYCInfo.create({
            realName: data.realName,
            idCardNumber: data.idCardNumber,
            idCardFrontUrl: data.idCardFrontUrl || '',
            idCardBackUrl: data.idCardBackUrl || '',
          })
        : null;

    return UserAccount.reconstruct({
      userId: data.userId.toString(),
      accountSequence: data.accountSequence,
      devices,
      phoneNumber: data.phoneNumber,
      nickname: data.nickname,
      avatarUrl: data.avatarUrl,
      inviterSequence: data.inviterSequence || null,
      referralCode: data.referralCode,
      walletAddresses: wallets,
      kycInfo,
      kycStatus: data.kycStatus as KYCStatus,
      status: data.status as AccountStatus,
      registeredAt: data.registeredAt,
      lastLoginAt: data.lastLoginAt,
      updatedAt: data.updatedAt,
    });
  }

  // ============ 批量查询 ============

  async findByUserIds(userIds: UserId[]): Promise<UserAccount[]> {
    if (userIds.length === 0) {
      return [];
    }

    const data = await this.prisma.userAccount.findMany({
      where: {
        userId: {
          in: userIds.map((id) => BigInt(id.value)),
        },
      },
      include: { devices: true, walletAddresses: true },
    });

    return data.map((d) => this.toDomain(d));
  }

  async findByAccountSequences(sequences: AccountSequence[]): Promise<UserAccount[]> {
    if (sequences.length === 0) {
      return [];
    }

    const data = await this.prisma.userAccount.findMany({
      where: {
        accountSequence: {
          in: sequences.map((seq) => seq.value),
        },
      },
      include: { devices: true, walletAddresses: true },
    });

    return data.map((d) => this.toDomain(d));
  }

  // ============ 推荐相关 ============

  async findByInviterSequence(
    inviterSequence: AccountSequence,
  ): Promise<UserAccount[]> {
    const data = await this.prisma.userAccount.findMany({
      where: { inviterSequence: inviterSequence.value },
      include: { devices: true, walletAddresses: true },
      orderBy: { registeredAt: 'desc' },
    });
    return data.map((d) => this.toDomain(d));
  }

  async createReferralLink(
    params: CreateReferralLinkParams,
  ): Promise<ReferralLinkData> {
    const result = await this.prisma.referralLink.create({
      data: {
        userId: params.userId,
        referralCode: params.referralCode,
        shortCode: params.shortCode,
        channel: params.channel,
        campaignId: params.campaignId,
      },
    });

    return {
      linkId: result.linkId,
      userId: result.userId,
      referralCode: result.referralCode,
      shortCode: result.shortCode,
      channel: result.channel,
      campaignId: result.campaignId,
      createdAt: result.createdAt,
    };
  }

  async findReferralLinksByUserId(userId: UserId): Promise<ReferralLinkData[]> {
    const results = await this.prisma.referralLink.findMany({
      where: { userId: BigInt(userId.value) },
      orderBy: { createdAt: 'desc' },
    });

    return results.map((r) => ({
      linkId: r.linkId,
      userId: r.userId,
      referralCode: r.referralCode,
      shortCode: r.shortCode,
      channel: r.channel,
      campaignId: r.campaignId,
      createdAt: r.createdAt,
    }));
  }

  /**
   * 查找钱包地址不完整的用户
   *
   * 使用 SQL 查询找出三链地址不全的用户：
   * - 需要 KAVA、DST、BSC 三种链都有地址才算完整
   * - 只返回 ACTIVE 状态的用户
   */
  async findUsersWithIncompleteWallets(limit: number): Promise<UserId[]> {
    // 使用原生 SQL 查询，因为 Prisma 不支持 HAVING 子句的复杂聚合
    const results = await this.prisma.$queryRaw<Array<{ user_id: bigint }>>`
      SELECT u.user_id
      FROM user_accounts u
      LEFT JOIN wallet_addresses w ON u.user_id = w.user_id
      WHERE u.status = 'ACTIVE'
      GROUP BY u.user_id
      HAVING COUNT(DISTINCT w.chain_type) < 3
      ORDER BY u.registered_at ASC
      LIMIT ${limit}
    `;

    this.logger.log(
      `[QUERY] Found ${results.length} users with incomplete wallets`,
    );

    return results.map((r) => UserId.create(r.user_id));
  }
}
