<?php

class CRM_Core_Payment_Liqpay extends CRM_Core_Payment {

  /**
   * mode of operation: live or test
   *
   * @var object
   * @static
   */
  protected $_mode = NULL;

  /**
   * Constructor
   *
   * @param string $mode the mode of operation: live or test
   *
   * @return void
   */
  function __construct($mode, &$paymentProcessor) {
    $this->_mode = $mode;
    $this->_paymentProcessor = $paymentProcessor;
    $this->_processorName = ts('LiqPay');
  }

  /**
   * This function checks to see if we have the right config values
   *
   * @return string the error message if any
   * @public
   */
  function checkConfig() {
    $error = [];

    if (empty($this->_paymentProcessor['user_name'])) {
      $error[] = ts('The "Bill To ID" is not set in the Administer CiviCRM Payment Processor.');
    }

    if (!empty($error)) {
      return implode('<p>', $error);
    } else {
      return NULL;
    }
  }

  /**
   * Given a payment processor id, return details including publishable key
   *
   * @param int $paymentProcessorId
   *
   * @return array
   */
  public static function getPaymentProcessorSettings($paymentProcessorId) {
    $fields = ['user_name', 'password'];

    try {
      $paymentProcessorDetails = civicrm_api3('PaymentProcessor', 'getsingle', [
        'return' => $fields,
        'id' => $paymentProcessorId,
      ]);
    } catch (CiviCRM_API3_Exception $e) {
      $error = $e->getMessage();
      CRM_Core_Error::debug_log_message(ts('API Error %1', [
        'domain' => 'com.agiliway.liqpay',
        1 => $error,
      ]));

      return [];
    }

    // Throw an error if credential not found
    foreach ($fields as $key => $field) {
      if (!isset($paymentProcessorDetails[$field])) {
        CRM_Core_Error::statusBounce(ts('Could not find valid LiqPay Payment Processor credentials'));
        Civi::log()->debug('LiqPay Credential' . $field . 'not found.');
      }
    }

    return $paymentProcessorDetails;
  }

  /**
   * Sets appropriate parameters for checking out to LiqPay
   *
   * @param array $params name value pair of contribution data
   *
   * @param string $component
   *
   * @return void
   * @throws \CiviCRM_API3_Exception
   * @throws \Civi\Payment\Exception\PaymentProcessorException
   * @access public
   */
  function doPayment(&$params, $component = 'contribute') {
    // If amount is 0 skip pinging LiqPay and just record a completed payment.
    if ($params['amount'] == 0) {
      civicrm_api3('Contribution', 'completetransaction', ['id' => $params['contributionID']]);
      CRM_Utils_System::redirect($this->getReturnSuccessUrl($params['qfKey']));
    }

    // Get LiqPay credentials ($params come from a form)
    if (!empty($params['payment_processor_id'])) {
      $liqPayCreds = $this::getPaymentProcessorSettings($params['payment_processor_id']);
    }

    // Throw an error if no credentials found
    if (empty($liqPayCreds)) {
      $errorMessage = self::handleErrorNotification('No valid LiqPay payment processor credentials found', $params['liqpay_error_url']);
      throw new \Civi\Payment\Exception\PaymentProcessorException('Failed to create LiqPay Charge: ' . $errorMessage);
    }

    $liqPayParams = [
      'amount' => $params['amount'],
      'action' => isset($params['is_recur']) && $params['is_recur'] ? 'subscribe' : 'pay',
      'version' => '3',
      'order_id' => isset($params['is_recur']) && $params['is_recur'] ? 'recurring_' . $params['contributionRecurID'] : $params['contributionID'],
      'description' => $params['description'],
      'currency' => $params['currencyID'],
      'public_key' => $liqPayCreds['user_name'],
      'language' => substr(CRM_Utils_System::getUFLocale(), 0, 2),
      'result_url' => $this->getReturnSuccessUrl($params['qfKey']),
      'server_url' => $this->getNotifyUrl(),
    ];

    if (isset($params['is_recur']) && $params['is_recur'] == TRUE) {
      $liqPayParams['customer'] = $params['contactID'];
      $liqPayParams['subscribe'] = 1;
      $liqPayParams['subscribe_date_start'] = date('Y-m-d H:i:s');

      //TODO liqPay support only month and year frequency unit need some validation for this part
      $liqPayParams['subscribe_periodicity'] = $params['frequency_unit'];
    }

    // Allow further manipulation of the arguments via custom hooks ..
    CRM_Utils_Hook::alterPaymentProcessorParams($this, $params, $liqPayParams);

    $liqPay = new CRM_Liqpayintegration_Utils_LiqPay($liqPayCreds['user_name'], $liqPayCreds['password']);
    $liqPayRedirectLink = $liqPay->getCheckoutUrl($liqPayParams);

    // Redirect the user to the payment url.
    CRM_Utils_System::redirect($liqPayRedirectLink);

    exit();
  }

  /**
   * Process incoming notification.
   *
   * @throws \CiviCRM_API3_Exception
   */
  public function handlePaymentNotification() {
    $params = array_merge($_GET, $_REQUEST);
    $data = CRM_Liqpayintegration_Utils_LiqPay::decodeParams($params['data']);
    $liqPayCreds = self::getPaymentProcessorSettings($params['processor_id']);
    $generatedSignature = CRM_Liqpayintegration_Utils_LiqPay::strToSign($liqPayCreds['password'] . $params['data'] . $liqPayCreds['password']);

    if ($generatedSignature !== $params['signature']) {
      return;
    }

    if ($data['status'] == 'success' && $data['action'] == 'pay') {
      $contribution = civicrm_api3('Contribution', 'getsingle', [
        'return' => ['contribution_status_id'],
        'id' => $data['order_id'],
      ]);

      if ($contribution['contribution_status'] == 'Pending') {
        civicrm_api3('Contribution', 'completetransaction', ['id' => $data['order_id']]);
      }
    } elseif ($data['status'] == 'success' && $data['action'] == 'regular') {
      $data['order_id'] = str_replace('recurring_', '', $data['order_id']);
      $contributions = civicrm_api3('Contribution', 'get', [
        'sequential' => 1,
        'contribution_recur_id' => $data['order_id'],
      ]);

      if ($contributions['count'] > 1 || ($contributions['count'] === 1 && $contributions['values'][0]['contribution_status'] == 'Completed')) {
        $recurringContribution = civicrm_api3('ContributionRecur', 'getsingle', [
          'id' => $data['order_id'],
        ]);
        $contribution = civicrm_api3('Contribution', 'create', [
          'financial_type_id' => $recurringContribution['financial_type_id'],
          'receive_date' => date('Y-m-d H:i:s'),
          'total_amount' => $recurringContribution['amount'],
          'contact_id' => $recurringContribution['contact_id'],
          'payment_instrument_id' => $recurringContribution['payment_instrument_id'],
          'contribution_recur_id' => $recurringContribution['id'],
          'payment_processor' => $recurringContribution['payment_processor_id'],
          'currency' => $recurringContribution['currency'],
          'contribution_status_id' => 'Pending',
          'source' => $contributions['values'][0]['contribution_source'],
        ]);
        $membership = civicrm_api3('Membership', 'get', [
          'contribution_recur_id' => $data['order_id'],
        ]);

        if ($membership['count'] > 0) {
          civicrm_api3('MembershipPayment', 'create', [
            'membership_id' => $membership['id'],
            'contribution_id' => $contribution['id'],
          ]);
        }

        civicrm_api3('Contribution', 'completetransaction', ['id' => $contribution['id']]);
      }
      elseif ($contributions['count'] === 1 && $contributions['values'][0]['contribution_status'] == 'Pending') {
        civicrm_api3('Contribution', 'completetransaction', ['id' => $contributions['id']]);
      }
    } elseif ($data['status'] == 'failure' && $data['action'] == 'regular') {
      $recurringContribution = civicrm_api3('ContributionRecur', 'getsingle', [
        'id' => $data['order_id'],
      ]);
      civicrm_api3('ContributionRecur', 'create', [
        'id' => $data['order_id'],
        'failure_count' => $recurringContribution['failure_count'] + 1,
        'contribution_status_id' => $recurringContribution['failure_count'] + 1 > 2 ? 'Failed' : 'Failing',
      ]);
    }
  }

}
