<?php

/**
 * @file
 */

include_once('commerce_pr.features.inc');

DEFINE('COMMERCE_PR_QUEUE_NAME', 'commerce_pr_queue');


/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Adds parameters for paypaly recurring billing.
 */
function commerce_pr_form_commerce_paypal_wps_redirect_form_alter(&$form, &$form_state) {
  $valid_skus = array(
    #'Standard_Abo_25', // Testsystem
    #'Premium_Abo_50',	// Testsystem
    '3 Standard Abo',
    '4 Premium Abo',
  );

  $order = $form_state['build_info']['args'][0];
  // A recurrent order only has one recurrent product.
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
  foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
    if ($line_item_wrapper->type->value() == 'commerce_node_checkout') {
      $product_wrapper = $line_item_wrapper->commerce_product;
      if (in_array($product_wrapper->sku->value(), $valid_skus)) {
        $paypal_fields = array(
          'cmd' =>'_xclick-subscriptions',
          'item_name' => check_plain($product_wrapper->label()),
          'a3' => commerce_currency_amount_to_decimal($order_wrapper->commerce_order_total->amount->value(), $order_wrapper->commerce_order_total->currency_code->value()),
          'p3' => 1, // interval
          't3' => 'M', // unit: D = day, W = week, M = month, Y = year
          'src' => 1, // Set recurring payments until canceled.
          'sra' => TRUE,
          'no_note' => TRUE,
          'modify' => FALSE,
        );
        foreach ($paypal_fields as $name => $value) {
          if (!empty($value)) {
            $form[$name] = array('#type' => 'hidden', '#value' => $value);
          }
        }

        _commerce_pr_log_var('Filling form', $form);
      }
    }
  }
}

/**
 * Implements hook_cron_queue_info().
 */
function commerce_pr_cron_queue_info() {
  $queues = array();
  $queues[COMMERCE_PR_QUEUE_NAME] = array(
    'worker callback' => 'commerce_pr_renew_subscription_queue_execute',
    'time' => 120,
  );
  return $queues;
}

/**
 * Renews a subscription.
 *
 * Operation is queued for each subscription as PayPal might send a lot of
 * renewal request at the same time, leading to concurrency problems.
 */
function commerce_pr_renew_subscription($order, $ipn) {
  _commerce_pr_log_var('Renew subs. ORDER', $order);
  _commerce_pr_log_var('Renew subs. IPN', $ipn);

  $queue = DrupalQueue::get(COMMERCE_PR_QUEUE_NAME);
  $queue->createItem(array('order' => $order, 'ipn' => $ipn));
}

/**
 * Queue callback for actually renewing a subscription.
 */
function commerce_pr_renew_subscription_queue_execute($queue_item) {
  _commerce_pr_log_var('Exec. queue', $queue_item);

  $order = $queue_item['order'];
  $ipn = $queue_item['ipn'];

  $initial_checkout_node = commerce_pr_extract_checkout_node($order);

  _commerce_pr_log_var('Exec. queue initial_checkout_node', $initial_checkout_node);

  if($initial_checkout_node) {
    $initial_checkout_node->field_kaufdatum[LANGUAGE_NONE][0]['value']=date("Y-m-d H:i:s");
    $initial_checkout_node->field_tokens_verbraucht[LANGUAGE_NONE][0]['value']=0;
    $initial_checkout_node->field_tokens_uebrig[LANGUAGE_NONE][0]['value'] = $initial_checkout_node->field_anzahl_tokens[LANGUAGE_NONE][0]['value'];
    $initial_checkout_node->field_startdatum[LANGUAGE_NONE][0]['value']=date("Y-m-d 00:00:00");
    $timestamp=strtotime(date("Y-m-d H:i:s"));
    $ablaufdatum=date("Y-m-d H:i:s", strtotime("+1 month 5 days"));
    $initial_checkout_node->field_ablaufdatum[LANGUAGE_NONE][0]['value']=$ablaufdatum;
    $initial_checkout_node->field_cancelled='';


    if($new_checkout_node_nid = replicate_entity('node', $initial_checkout_node)) {
      if($new_checkout_node = node_load($new_checkout_node_nid)) {
        $new_order = commerce_pr_clone_order($order, $new_checkout_node, $ipn);
        _commerce_pr_log_var('Exec. queue new_order', $new_order);
        return $new_order;
      }
    }
  }
}

/**
 * Extracts the checkout node from the order's line item.
 */
function commerce_pr_extract_checkout_node($order) {
  $order_wrapper = entity_metadata_wrapper('commerce_order', $order);
  foreach ($order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
    if ($line_item_wrapper->type->value() == 'commerce_node_checkout') {
      return $line_item_wrapper->commerce_node_checkout_node->value();
    }
  }
  return FALSE;
}


/**
 * Implements hook_commerce_paypal_ipn_process().
 */
function commerce_pr_commerce_paypal_ipn_process($order, $payment_method, $ipn) {
  // Allways log the IPN, maybe something goes wrong within Drupal and we need
  // the full history.
  commerce_pr_log_ipn($order, $ipn);

  _commerce_pr_log_var('Paypal ipn ORDER', $order);
  _commerce_pr_log_var('Paypal ipn IPN', $ipn);

  if (!$order) {
    return;
  }

  switch ($ipn['txn_type']) {
    case 'subscr_signup': // Subscription started.
    case 'recurring_payment_profile_created': // Recurring payment profile created.
      break;

    case 'subscr_cancel': // subscription account cancellation:
    case 'subscr_eot': // subscription's end-of-term.
      break;

    case 'subscr_failed': // Subscription payment failed.
      break;

    case 'subscr_payment': // subscription payment.
      // Renew subscription only if it's not the first payment and the original
      // order is not in the payment checkout process anymore (it can happen
      // that subscr_signup gets called before subscr_payment and we do not want
      // to duplicate the initial order).
      $account = user_load($order->uid);
      if ($account->uid && $order->status != "checkout_payment" && $ipn['payment_status'] == "Completed" && !commerce_pr_is_first_payment($order)) {
        commerce_pr_renew_subscription($order, $ipn);
      }
      break;
  }
}

/**
 * Logs IPN to the system using the message module.
 */
function commerce_pr_log_ipn($order, $ipn) {
  $message = message_create('commerce_pr_ipn_log', array('uid' => $order->uid));
  $wrapper = entity_metadata_wrapper('message', $message);
  if ($order) {
    $wrapper->field_commerce_pr_order->set($order);
  }
  $wrapper->field_commerce_pr_ipn_type->set($ipn['txn_type']);
  $wrapper->field_commerce_pr_subscr_id->set($ipn['subscr_id']);

  // If we do not escape the values, the system breaks.
  $ipn_string = "";
  foreach ($ipn as $key => $value) {
    $ipn_string .= $key . ": " . check_plain($value) . "\n";
  }
  $wrapper->field_commerce_pr_ipn->set($ipn_string);
  $wrapper->save();
}

/**
 * Checks whether this is the initial payment )
 *
 * If so, no subscription renewal should be performed.
 */
function commerce_pr_is_first_payment($order) {
  $query = new EntityFieldQuery();
  $query->entityCondition('entity_type', 'message')
    ->entityCondition('bundle', 'commerce_pr_ipn_log')
    ->propertyCondition('uid', $order->uid)
    ->fieldCondition('field_commerce_pr_order', 'target_id', $order->order_id)
    ->fieldCondition('field_commerce_pr_ipn_type', 'value', 'subscr_payment')
    ->propertyOrderBy('timestamp', 'DESC')
    ->range(0, 2);
  $result = $query->execute();
  if (!empty($result['message']) && count($result['message']) == 1) {
    return TRUE;
  }
  return FALSE;
}


/**
 * Creates a new order based on the information given in $master_order.
 *
 * Billing address and line items get cloned. Furthermore the order completion
 * is invoked so that Rules action can react upon it (e.g. add subscription
 * time).
 *
 * @param $order
 *   The commerce order to clone.
 * @param $ipn
 *   The PayPal IPN used to set the correct transaction.
 *
 * @return
 *   The cloned commerce order.
 */
function commerce_pr_clone_order($master_order, $checkout_node, $ipn = "") {
  // Create a new order
  $new_order = commerce_order_new($master_order->uid, 'pending');

  // Clone the billing address.
  $new_order->commerce_customer_billing = $master_order->commerce_customer_billing;

  // Save it so it gets an order ID and return the full object.
  commerce_order_save($new_order);

  // Wrap the order for easy access to field data.
  $new_order_wrapper = entity_metadata_wrapper('commerce_order', $new_order);

  // Wrap the master order for easy access to field data.
  $master_order_wrapper = entity_metadata_wrapper('commerce_order', $master_order);
  foreach ($master_order_wrapper->commerce_line_items as $delta => $line_item_wrapper) {
    if ($line_item_wrapper->type->value() == 'commerce_node_checkout') {
      $product = $line_item_wrapper->commerce_product->value();
      // Create our new line item.
      $new_line_item = commerce_product_line_item_new($product, 1, $new_order->order_id, array(), 'commerce_node_checkout');

      // Set the node_reference field value.
      $new_line_item->commerce_node_checkout_node[LANGUAGE_NONE][0]['nid'] = $checkout_node->nid;

      // Let other modules have a go.
      drupal_alter('commerce_node_checkout_line_item', $new_line_item, $product, $checkout_node);
    }
    elseif ($line_item_wrapper->type->value() == 'coupon' && function_exists("commerce_coupon_line_item_new")) {
      // We can programatically reuse the coupon and thus ensure the same price.
      $coupon = entity_load_single('commerce_coupon', $line_item_wrapper->commerce_coupon_reference->value());
      if ($coupon) {
        $new_line_item = commerce_coupon_line_item_new($coupon, $new_order->order_id);
      }
    }
    if (isset($new_line_item)) {
      // Ensure the same price on the line items. The order total will always
      // be calculated base on this information.
      $new_line_item->commerce_unit_price = $line_item_wrapper->value()->commerce_unit_price;
      commerce_line_item_save($new_line_item);
      $new_order_wrapper->commerce_line_items[] = $new_line_item;
      unset($new_line_item);
    }
  }
  // Save the updated order.
  commerce_order_save($new_order);

  // Move the transaction from the master order to the new order so that we have
  // a correct order balance.
  if (isset($ipn['transaction_id'])) {
    $transaction = commerce_payment_transaction_load($ipn['transaction_id']);
    if ($transaction) {
      $transaction->order_id = $new_order->order_id;
      commerce_payment_transaction_save($transaction);
    }
  }

  // Invoke the commerce_checkout_complete Rules event.
  commerce_checkout_complete($new_order);

  return $new_order;
}

/**
 * Cancels a subscription directly via the PayPal Express Checkout system.
 *
 * Requires the Rule 'commerce_payment_paypal_ec' to be configured, but it needs
 * not be be activated.
 *
 * @param $subscr_id
 *   The PayPal subscription id.
 * @param $order
 *   The correspondig Drupal order entity (used by the PayPal system for
 *   altering the EC request).
 *
 * @return
 *   TRUE if cancellation was successful, else FALSE.
 */
function commerce_pr_cancel_subscription($subscr_id, $order) {
  // Name-value pairs for cancelling a subscription.
  $nvp = array(
   'METHOD' => 'ManageRecurringPaymentsProfileStatus',
   'PROFILEID' => $subscr_id,
   'ACTION' => 'Cancel',
  );
  $ec_payment_method = commerce_payment_method_instance_load('paypal_ec|commerce_payment_paypal_ec');

	#dpm($ec_payment_method);

  $response = commerce_paypal_api_request($ec_payment_method, $nvp, $order);

  #dpm($response);

//  if (isset($response['ACK']) && $response['ACK'] == 'ACK') {
  if (isset($response['ACK']) && $response['ACK'] == 'Success') {
    return TRUE;
  }
  return FALSE;
}

/**
 * Returns the subscription id and the order for the last subscriptio signup.
 */
function commerce_pr_get_last_subscription_data($user) {
  $query = new EntityFieldQuery();
  $query->entityCondition('entity_type', 'message')
    ->entityCondition('bundle', 'commerce_pr_ipn_log')
    ->propertyCondition('uid', $user->uid)
    ->fieldCondition('field_commerce_pr_ipn_type', 'value', 'subscr_signup')
    ->propertyOrderBy('timestamp', 'DESC')
    ->range(0, 1);
  $result = $query->execute();

  #dpm($result);

  if (!empty($result['message']) && count($result['message']) == 1) {
    $messages = entity_load('message', array_keys($result['message']));
    if ($messages) {
      $message = reset($messages);
      $wrapper = entity_metadata_wrapper('message', $message);
      $subsrc_id = $wrapper->field_commerce_pr_subscr_id->value();
      $order = $wrapper->field_commerce_pr_order->value();
      return array(
        'subscr_id' => $subsrc_id,
        'order' => $order,
      );
    }
  }
  return FALSE;
}

/**
 * Callback for cancelling the last subscription of the given user.
 */
function commerce_pr_cancel_callback($user) {
  $last_subscription_data = commerce_pr_get_last_subscription_data($user);

  #dpm($last_subscription_data);

  if (!count($last_subscription_data)) {
    drupal_set_message(t('No subscription found'), 'error');
    return 'error_notfound';
  }

  $success = commerce_pr_cancel_subscription($last_subscription_data['subscr_id'], $last_subscription_data['order']);

  #dpm($success);

  if ($success) {
    drupal_set_message(t('Your subscription has successfully been cancelled.'));
  }
  else {
    drupal_set_message(t('Your subscription could not be cancelled.'), 'error');
    return 'error_notcancelled';

  }
  return 'success';
}

function _commerce_pr_log_var($what, $var) {
  watchdog('commerce_pr_debug', $what . '<br />' . '<pre>'. check_plain(print_r($var, true)) .'</pre>');
}