import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Payment } from '../entities/payment.entity';
import { Order } from '../entities/order.entity';
import { ConfigService } from '@nestjs/config';
import axios from 'axios';

@Injectable()
export class PaymentService {
  constructor(
    @InjectRepository(Payment)
    private paymentRepository: Repository<Payment>,
    @InjectRepository(Order)
    private orderRepository: Repository<Order>,
    private configService: ConfigService,
  ) {}

  async initiateMpesaPayment(
    orderId: string,
    amount: number,
    phoneNumber: string,
  ): Promise<any> {
    try {
      const order = await this.orderRepository.findOne({
        where: { id: orderId },
      });

      if (!order) {
        throw new Error('Order not found');
      }

      const { MERCHANT_ID, PASSKEY, CONSUMER_KEY, CONSUMER_SECRET } = this.configService.get('mpesa');

      const timestamp = new Date().toISOString();
      const password = Buffer.from(`${MERCHANT_ID}${PASSKEY}${timestamp}`).toString('base64');

      const response = await axios.post(
        'https://api.safaricom.co.ke/mpesa/stkpush/v1/processrequest',
        {
          BusinessShortCode: MERCHANT_ID,
          Password: password,
          Timestamp: timestamp,
          TransactionType: 'CustomerPayBillOnline',
          Amount: amount,
          PartyA: phoneNumber,
          PartyB: MERCHANT_ID,
          PhoneNumber: phoneNumber,
          CallBackURL: `${this.configService.get('API_URL')}/payment/callback`,
          AccountReference: `ORDER_${orderId}`,
          TransactionDesc: `Payment for order ${orderId}`,
        },
        {
          headers: {
            'Content-Type': 'application/json',
            Authorization: `Bearer ${await this.getMpesaAccessToken()}`,
          },
        }
      );

      // Type guard for CheckoutRequestID
      let transactionId = '';
      if (response.data && typeof response.data === 'object' && 'CheckoutRequestID' in response.data) {
        transactionId = (response.data as { CheckoutRequestID: string }).CheckoutRequestID;
      }

      const payment = this.paymentRepository.create({
        orderId,
        amount,
        paymentMethod: 'mpesa',
        status: 'pending',
        transactionId,
        reference: `ORDER_${orderId}`,
        transactionTime: new Date(),
        transactionDescription: `Payment for order ${orderId}`,
        metadata: { phoneNumber },
      });

      return this.paymentRepository.save(payment);
    } catch (error) {
      throw new Error('Failed to initiate MPESA payment');
    }
  }

  async handleMpesaCallback(data: any): Promise<void> {
    const { CheckoutRequestID, ResultCode, ResultDesc } = data.Body.stkCallback;

    const payment = await this.paymentRepository.findOne({
      where: { transactionId: CheckoutRequestID },
    });

    if (!payment) {
      throw new Error('Payment not found');
    }

    if (ResultCode === 0) {
      payment.status = 'completed';
      payment.transactionTime = new Date();
      payment.transactionDescription = ResultDesc;
    } else {
      payment.status = 'failed';
      payment.transactionDescription = ResultDesc;
    }

    await this.paymentRepository.save(payment);

    // Update order status
    const order = await this.orderRepository.findOne({
      where: { id: payment.orderId },
    });

    if (order && payment.status === 'completed') {
      order.paymentStatus = 'paid';
      await this.orderRepository.save(order);
    }
  }

  private async getMpesaAccessToken(): Promise<string> {
    const { CONSUMER_KEY, CONSUMER_SECRET } = this.configService.get('mpesa');

    const response = await axios.get(
      'https://api.safaricom.co.ke/oauth/v1/generate?grant_type=client_credentials',
      {
        auth: {
          username: CONSUMER_KEY,
          password: CONSUMER_SECRET,
        },
      }
    );

    // Type guard for access_token
    if (response.data && typeof response.data === 'object' && 'access_token' in response.data) {
      return (response.data as { access_token: string }).access_token;
    }
    throw new Error('Failed to retrieve access token from M-Pesa response');
  }

  async generateInvoice(orderId: string): Promise<any> {
    const order = await this.orderRepository.findOne({
      where: { id: orderId },
      relations: ['customer', 'items'],
    });

    if (!order) {
      throw new Error('Order not found');
    }

    const invoice = {
      invoiceNumber: `INV-${orderId}`,
      date: new Date().toISOString(),
      customer: {
        name: order.customer.name,
        email: order.customer.email,
        phone: order.customer.phone,
      },
      items: order.items.map((item) => ({
        name: item.product.name,
        quantity: item.quantity,
        unitPrice: item.unitPrice,
        total: item.quantity * item.unitPrice,
      })),
      subtotal: order.items.reduce(
        (sum, item) => sum + item.quantity * item.unitPrice,
        0
      ),
      tax: 16, // VAT
      total: order.subtotal + order.tax,
      paymentStatus: order.paymentStatus,
    };

    return invoice;
  }
}
