import { Injectable, Logger, BadRequestException } from '@nestjs/common';
import axios from 'axios';
import {
  IPaymentGateway,
  IPaymentVerifyParams,
  IRequestPaymentData,
  IRequestPaymentParams,
  IPaymentVerifyData,
  IZarinpalPaymentResponse,
  IZarinpalRequestPayment,
  IZarinpalVerifyRequest,
  IZarinpalVerifyResponse,
} from '../interface/gateway';
import { ConfigService } from '@nestjs/config';
import { PaymentMessage } from 'src/common/enums/message.enum';

@Injectable()
export class ZarinpalGateway implements IPaymentGateway {
  private readonly logger = new Logger(ZarinpalGateway.name);
  private readonly zarinpalRequestUrl: string;
  private readonly zarinpalVerifyUrl: string;
  private readonly zarinpalPaymentBaseUrl: string;
  private readonly axiosConfig = {
    timeout: 10_000,
    headers: {
      'Content-Type': 'application/json',
    },
  };

  constructor(private readonly configService: ConfigService) {
    const zarinpalBaseUrl = this.configService.get<string>('ZARINPAL_BASE_URL') || 'https://sandbox.zarinpal.com';
    this.zarinpalPaymentBaseUrl = zarinpalBaseUrl;
    this.zarinpalRequestUrl = `${zarinpalBaseUrl}/pg/v4/payment/request.json`;
    this.zarinpalVerifyUrl = `${zarinpalBaseUrl}/pg/v4/payment/verify.json`;
  }

  private getAxiosErrorData(error: unknown): { status?: number; data?: unknown } | null {
    if (!axios.isAxiosError(error) || !error.response) return null;
    return { status: error.response.status, data: error.response.data };
  }

  async requestPayment({ amount, merchantId, orderId, domain }: IRequestPaymentParams): Promise<IRequestPaymentData> {
    // Transform camelCase to snake_case for Zarinpal API v4
    const callbackUrl = `${domain}/verify/${orderId}`;
    const zarinpalRequest: IZarinpalRequestPayment = {
      amount,
      merchant_id: merchantId,
      description: `Payment for order #${orderId}`,
      callback_url: callbackUrl,
      currency: 'IRT',
      metadata: {
        order_id: orderId,
      },
    };

    try {
      const res = await axios.post<IZarinpalPaymentResponse>(
        this.zarinpalRequestUrl,
        zarinpalRequest,
        this.axiosConfig,
      );
      const { data, errors } = res.data ?? {};
      const code = data?.code;
      const message = data?.message;
      const authority = data?.authority;

      if (!data) {
        this.logger.error('Invalid response from Zarinpal API (missing data)', JSON.stringify(res.data));
        throw new BadRequestException(PaymentMessage.ZARINPAL_INVALID_RESPONSE);
      }

      if ((Array.isArray(errors) && errors.length > 0) || code !== 100 || !authority) {
        this.logger.error(
          'Zarinpal payment request failed',
          JSON.stringify({
            code,
            message,
            errors,
            callbackUrl,
            amount,
            orderId,
          }),
        );
        throw new BadRequestException(message ?? PaymentMessage.ZARINPAL_PAYMENT_REQUEST_ERROR);
      }

      return { transactionId: authority };
    } catch (error) {
      if (error instanceof BadRequestException) throw error;

      const axiosErr = this.getAxiosErrorData(error);
      if (axiosErr) {
        this.logger.error(
          'Zarinpal payment request axios error',
          JSON.stringify({
            status: axiosErr.status,
            data: axiosErr.data,
            callbackUrl,
            amount,
            orderId,
          }),
        );
        throw new BadRequestException(PaymentMessage.ZARINPAL_PAYMENT_REQUEST_ERROR);
      }

      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      this.logger.error('Zarinpal payment request error', errorMessage);
      throw new BadRequestException(PaymentMessage.GATEWAY_ERROR);
    }
  }

  async verifyPayment({ merchantId, amount, transactionId }: IPaymentVerifyParams): Promise<IPaymentVerifyData> {
    try {
      // Transform camelCase to snake_case for Zarinpal API v4
      const zarinpalVerifyRequest: IZarinpalVerifyRequest = {
        merchant_id: merchantId,
        amount,
        authority: transactionId,
      };

      const res = await axios.post<IZarinpalVerifyResponse>(
        this.zarinpalVerifyUrl,
        zarinpalVerifyRequest,
        this.axiosConfig,
      );

      // Check if response data exists
      if (!res.data || !res.data.data) {
        throw new BadRequestException(PaymentMessage.ZARINPAL_INVALID_RESPONSE);
      }

      const { code, message, ref_id, card_pan } = res.data.data;

      // Check if there are errors in the response
      if (code !== 100 && code !== 0) {
        throw new BadRequestException(message ?? PaymentMessage.ZARINPAL_VERIFY_ERROR);
      }

      return {
        success: code === 100,
        referenceId: ref_id ? ref_id.toString() : '',
        cardPan: card_pan,
        raw: res.data,
      };
    } catch (error) {
      if (axios.isAxiosError(error) && error.response) {
        this.logger.error('Zarinpal verify failed', error.response.data);
        throw new BadRequestException(error.response.data);
      }
      const errorMessage = error instanceof Error ? error.message : 'Unknown error';
      throw new BadRequestException(PaymentMessage.GATEWAY_ERROR);
    }
  }

  getPaymentUrl(authority: string): string {
    return `${this.zarinpalPaymentBaseUrl}/pg/StartPay/${authority}`;
  }
}
