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

@Injectable()
export class MpesaService {
  constructor(
    @InjectRepository(Payment)
    private paymentRepository: Repository<Payment>,
    @InjectRepository(Receipt)
    private receiptRepository: Repository<Receipt>,
    private configService: ConfigService,
  ) {}

  private async getAccessToken(): 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 initiateSTKPush(
    orderId: string,
    amount: number,
    phoneNumber: string,
  ): Promise<any> {
    const { MERCHANT_ID, PASSKEY } = 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.getAccessToken()}`,
        },
      }
    );

    return response.data;
  }

  async b2cPayment(
    recipient: string,
    amount: number,
    occasion: string,
  ): Promise<any> {
    const { MERCHANT_ID, PASSKEY } = this.configService.get('mpesa');
    const timestamp = new Date().toISOString();

    const response = await axios.post(
      'https://api.safaricom.co.ke/mpesa/b2c/v1/paymentrequest',
      {
        InitiatorName: 'testapi',
        SecurityCredential: 'YOUR_SECURITY_CREDENTIAL',
        CommandID: 'SalaryPayment',
        Amount: amount,
        PartyA: MERCHANT_ID,
        PartyB: recipient,
        Remarks: occasion,
        QueueTimeOutURL: `${this.configService.get('API_URL')}/payment/b2c-callback`,
        ResultURL: `${this.configService.get('API_URL')}/payment/b2c-callback`,
        Occasion: occasion,
      },
      {
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${await this.getAccessToken()}`,
        },
      }
    );

    return response.data;
  }

  async b2bPayment(
    recipient: string,
    amount: number,
    reason: string,
  ): Promise<any> {
    const { MERCHANT_ID, PASSKEY } = this.configService.get('mpesa');
    const timestamp = new Date().toISOString();

    const response = await axios.post(
      'https://api.safaricom.co.ke/mpesa/b2b/v1/paymentrequest',
      {
        Initiator: 'testapi',
        SecurityCredential: 'YOUR_SECURITY_CREDENTIAL',
        CommandID: 'BusinessPayment',
        SenderIdentifierType: '4',
        RecipientIdentifierType: '4',
        Amount: amount,
        PartyA: MERCHANT_ID,
        PartyB: recipient,
        AccountReference: 'Business Payment',
        Remarks: reason,
        QueueTimeOutURL: `${this.configService.get('API_URL')}/payment/b2b-callback`,
        ResultURL: `${this.configService.get('API_URL')}/payment/b2b-callback`,
      },
      {
        headers: {
          'Content-Type': 'application/json',
          Authorization: `Bearer ${await this.getAccessToken()}`,
        },
      }
    );

    return response.data;
  }

  async generateReceipt(paymentId: string): Promise<Receipt> {
    const payment = await this.paymentRepository.findOne({
      where: { id: paymentId },
      relations: ['order'],
    });

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

    const receiptNumber = `RCPT-${Date.now()}`;
    const receipt = this.receiptRepository.create({
      receiptNumber,
      amount: payment.amount,
      description: `Payment for order ${payment.order.id}`,
      order: payment.order,
      payment,
    });

    return this.receiptRepository.save(receipt);
  }
}