import { Injectable, NotFoundException, BadRequestException } from '@nestjs/common';
import { FilterQuery } from '@mikro-orm/core';
import { EntityManager } from '@mikro-orm/postgresql';
import { WalletTransaction } from '../entities/wallet-transaction.entity';
import { WalletTransactionRepository } from '../repositories/wallet-transaction.repository';
import { FindWalletTransactionsDto } from '../dto/find-wallet-transactions.dto';
import { CreateWalletTransactionDto } from '../dto/create-wallet-transaction.dto';
import { PaginatedResult } from 'src/common/interfaces/pagination.interface';
import { WalletTransactionType } from '../interface/wallet';
import { PointTransactionRepository } from '../repositories/point-transaction.repository';

@Injectable()
export class WalletService {
  constructor(
    private readonly walletTransactionRepository: WalletTransactionRepository,
    private readonly pointTransactionRepository: PointTransactionRepository,
  ) {}

  async getUserWalletTransactions(
    userId: string,
    restId: string,
    dto: FindWalletTransactionsDto,
  ): Promise<PaginatedResult<WalletTransaction>> {
    const {
      page = 1,
      limit = 10,
      type,
      reason,
      startDate,
      endDate,
      minAmount,
      maxAmount,
      orderBy = 'createdAt',
      order = 'desc',
    } = dto;

    // Calculate pagination
    const offset = (page - 1) * limit;

    // Find the user's wallet for this restaurant
    const walletTransaction = await this.walletTransactionRepository.findOne({
      user: { id: userId },
      restaurant: { id: restId },
    });

    if (!walletTransaction) {
      throw new NotFoundException(`User wallet not found for user ${userId} and restaurant ${restId}.`);
    }

    // Build the 'where' filter query
    const where: FilterQuery<WalletTransaction> = {
      user: { id: userId },
      restaurant: { id: restId },
    };

    // Add type filter
    if (type) {
      where.type = type;
    }

    // Add reason filter
    if (reason) {
      where.reason = reason;
    }

    // Add date range filters
    if (startDate || endDate) {
      where.createdAt = {};
      if (startDate) {
        where.createdAt.$gte = new Date(startDate);
      }
      if (endDate) {
        where.createdAt.$lte = new Date(endDate);
      }
    }

    // Add amount range filters
    if (minAmount !== undefined || maxAmount !== undefined) {
      where.amount = {};
      if (minAmount !== undefined) {
        where.amount.$gte = minAmount;
      }
      if (maxAmount !== undefined) {
        where.amount.$lte = maxAmount;
      }
    }

    // Execute the query using findAndCount
    const [transactions, total] = await this.walletTransactionRepository.findAndCount(where, {
      limit,
      offset,
      orderBy: { [orderBy]: order.toLowerCase() as 'asc' | 'desc' },
    });

    // Calculate total pages
    const totalPages = Math.ceil(total / limit);

    // Return the paginated result
    return {
      data: transactions,
      meta: {
        total,
        page,
        limit,
        totalPages,
      },
    };
  }

  getUserWallet(userId: string, restId: string): Promise<WalletTransaction | null> {
    return this.walletTransactionRepository.findOne(
      {
        user: { id: userId },
        restaurant: { id: restId },
      },
      { orderBy: { createdAt: 'DESC' } },
    );
  }

  async createTransaction(
    em: EntityManager,
    userId: string,
    restId: string,
    dto: CreateWalletTransactionDto,
  ): Promise<WalletTransaction> {
    const { amount, type, reason } = dto;

    // Validate amount
    if (amount <= 0) {
      throw new BadRequestException('Transaction amount must be greater than 0.');
    }

    // Find the user's wallet for this restaurant
    const walletTransaction = await em.findOne(WalletTransaction, {
      user: { id: userId },
      restaurant: { id: restId },
    });

    if (!walletTransaction) {
      throw new NotFoundException(`User wallet not found for user ${userId} and restaurant ${restId}.`);
    }

    // Calculate new balance
    const currentBalance = walletTransaction.balance || 0;
    let newBalance: number;

    if (type === WalletTransactionType.CREDIT) {
      newBalance = currentBalance + amount;
    } else if (type === WalletTransactionType.DEBIT) {
      if (currentBalance < amount) {
        throw new BadRequestException(`Insufficient wallet balance. Current: ${currentBalance}, Required: ${amount}.`);
      }
      newBalance = currentBalance - amount;
    } else {
      throw new BadRequestException(`Invalid transaction type: ${type}`);
    }

    // Update wallet balance
    walletTransaction.balance = newBalance;
    await em.persistAndFlush(walletTransaction);

    // Create transaction record
    const transaction = em.create(WalletTransaction, {
      user: walletTransaction.user,
      restaurant: walletTransaction.restaurant,
      amount,
      type,
      reason,
      balance: newBalance,
    });
    await em.persistAndFlush([walletTransaction, transaction]);
    return transaction;
  }

  async getUserCurrentWalletBalance(userId: string, restuarantId: string) {
    const balance = await this.walletTransactionRepository.getCurrentWalletBalance(userId, restuarantId);
    return { balance };
  }
  async getUserCurrentPoinrBalance(userId: string, restuarantId: string) {
    const balance = await this.pointTransactionRepository.getcurrentPointBalance(userId, restuarantId);
    return { balance };
  }
}
