import { BadRequestException, Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { EntityManager, Repository } from 'typeorm';
import { UserWallets } from './user_wallets.entity';
import { WalletTransactions } from './wallet_transactions.entity';

export const DEFAULT_INITIAL_WHALE_COINS = 1200;

export interface WalletBalancePayload {
  user_id: string;
  balance: number;
  currency: 'whale_coin';
}

export interface SpendWalletResult {
  wallet: UserWallets;
  transaction: WalletTransactions;
}

export interface EarnWalletResult {
  wallet: UserWallets;
  transaction: WalletTransactions;
}

@Injectable()
export class UserWalletsService {
  constructor(
    @InjectRepository(UserWallets)
    private readonly userWalletsRepository: Repository<UserWallets>,
    @InjectRepository(WalletTransactions)
    private readonly walletTransactionsRepository: Repository<WalletTransactions>,
  ) {}

  async ensureWallet(userId: bigint): Promise<UserWallets> {
    const existing = await this.userWalletsRepository.findOne({
      where: { user_id: userId },
    });
    if (existing) {
      return existing;
    }

    const wallet = new UserWallets();
    wallet.user_id = userId;
    wallet.balance = DEFAULT_INITIAL_WHALE_COINS;
    wallet.created_at = new Date();
    wallet.updated_at = new Date();
    const savedWallet = await this.userWalletsRepository.save(wallet);
    await this.createTransaction(userId, 'grant', DEFAULT_INITIAL_WHALE_COINS, savedWallet.balance, 'registration', 'initial_wallet', '新用户初始鲸币');
    return savedWallet;
  }

  async getBalance(userId: bigint): Promise<WalletBalancePayload> {
    const wallet = await this.ensureWallet(userId);
    return {
      user_id: userId.toString(),
      balance: wallet.balance,
      currency: 'whale_coin',
    };
  }

  async spend(userId: bigint, amount: number, referenceType: string, referenceId: string, note?: string): Promise<SpendWalletResult> {
    if (!Number.isInteger(amount) || amount < 0) {
      throw new BadRequestException('鲸币消费数量不正确');
    }

    const wallet = await this.ensureWallet(userId);
    if (wallet.balance < amount) {
      throw new BadRequestException('鲸币余额不足');
    }

    wallet.balance -= amount;
    wallet.updated_at = new Date();
    const savedWallet = await this.userWalletsRepository.save(wallet);
    const transaction = await this.createTransaction(userId, 'spend', -amount, savedWallet.balance, referenceType, referenceId, note || '');

    return {
      wallet: savedWallet,
      transaction,
    };
  }

  async earn(userId: bigint, amount: number, referenceType: string, referenceId: string, note?: string): Promise<EarnWalletResult> {
    if (!Number.isInteger(amount) || amount < 0) {
      throw new BadRequestException('鲸币收入数量不正确');
    }

    const wallet = await this.ensureWallet(userId);
    wallet.balance += amount;
    wallet.updated_at = new Date();
    const savedWallet = await this.userWalletsRepository.save(wallet);
    const transaction = await this.createTransaction(userId, 'earn', amount, savedWallet.balance, referenceType, referenceId, note || '');

    return {
      wallet: savedWallet,
      transaction,
    };
  }

  async earnInTransaction(
    manager: EntityManager,
    userId: bigint,
    amount: number,
    referenceType: string,
    referenceId: string,
    note?: string,
  ): Promise<EarnWalletResult> {
    if (!Number.isInteger(amount) || amount < 0) {
      throw new BadRequestException('鲸币收入数量不正确');
    }

    const walletRepository = manager.getRepository(UserWallets);
    const transactionRepository = manager.getRepository(WalletTransactions);
    let wallet = await walletRepository.findOne({
      where: { user_id: userId },
      lock: { mode: 'pessimistic_write' },
    });
    if (!wallet) {
      wallet = walletRepository.create({
        user_id: userId,
        balance: DEFAULT_INITIAL_WHALE_COINS,
        created_at: new Date(),
        updated_at: new Date(),
      });
      wallet = await walletRepository.save(wallet);
      await transactionRepository.save(transactionRepository.create({
        user_id: userId,
        type: 'grant',
        amount: DEFAULT_INITIAL_WHALE_COINS,
        balance_after: wallet.balance,
        reference_type: 'registration',
        reference_id: 'initial_wallet',
        note: '新用户初始鲸币',
        created_at: new Date(),
      }));
    }

    wallet.balance += amount;
    wallet.updated_at = new Date();
    const savedWallet = await walletRepository.save(wallet);
    const transaction = await transactionRepository.save(transactionRepository.create({
      user_id: userId,
      type: 'earn',
      amount,
      balance_after: savedWallet.balance,
      reference_type: referenceType,
      reference_id: referenceId,
      note: note || null,
      created_at: new Date(),
    }));
    return { wallet: savedWallet, transaction };
  }

  private async createTransaction(
    userId: bigint,
    type: string,
    amount: number,
    balanceAfter: number,
    referenceType: string,
    referenceId: string,
    note: string,
  ): Promise<WalletTransactions> {
    const transaction = new WalletTransactions();
    transaction.user_id = userId;
    transaction.type = type;
    transaction.amount = amount;
    transaction.balance_after = balanceAfter;
    transaction.reference_type = referenceType;
    transaction.reference_id = referenceId;
    transaction.note = note || null;
    transaction.created_at = new Date();
    return await this.walletTransactionsRepository.save(transaction);
  }
}
