<?php
/**
 * @file
 *   Module file for moneybird module.
 *   Written by Daniel Beeke, GoalGorilla, Bram tne Hove
 *   Updated by Kevin and Cedric (E-nova)
 */



/**
 * Menu Hook; Defines all entry points
 *
 */
function moneybird_menu() {
  $items = array();

  // To uncomment if we want a top menu pointing to the settings
  /* $items['admin/settings/moneybird'] = array(
    'title' => 'Moneybird settings',
    'description' => 'The settings of Moneybird.',
    'page callback' => 'drupal_get_form',
    'page arguments' => array('moneybird_settings_form'),
    'access callback' => array('administer_moneybird_access'),
    'type' => MENU_NORMAL_ITEM,
  ); */

  $items['moneybird/retrieve'] = array(
    'page callback' => 'moneybird_retrieve',
    'access callback' => 'push_moneybird_access',
    'type' => MENU_CALLBACK,
  );

  return $items;
}


/**
 * Push callback from moneybird
 *
 * This function is called when moneybird is configured to push
 * the status changes to our application.
 */
function moneybird_retrieve() {
  // TODO : test it with curl -X POST -d "invoice_id=1&state=paid" -H "MoneyBird (Push API)" http://yourwebsite.com/script_for_push.php
  if ($_POST['state'] == 'paid') {
    $ubercart_order_id = db_query("SELECT id FROM {moneybird_orders} WHERE mid = :mid", array(':mid' => $_POST['invoice_id']))->fetchField();
    uc_order_update_status($ubercart_order_id, 'payment_received');
    watchdog('moneybird', 'Updated order ' . $ubercart_order_id . ' to state ' . '"payment_received"' . ' by push callback');
  }
  exit;
}

/**
 * Hook init : Initalize the application
 */
function moneybird_init() {
  module_load_include('php', 'moneybird', 'moneybird_php_api/ApiConnector');
  spl_autoload_register('Moneybird\ApiConnector::autoload');
}

/**
 * Hook perm : defines the module permissions
 */
function moneybird_permission() {
  return array(
    'administer moneybird' => array(
      'title' => 'Administer Moneybird',
      'description' => 'whether or not the user can access the moneybird settings page',
      'restricted access' => TRUE,
    ),
    'push moneybird' => array(
      'title' => 'Push Moneybird',
      'description' => 'whether or not the user can push content in moneybird',
      'restricted access' => TRUE,
    ),
  );
}

/**
 * access righths definition for administer moneybird
 */
function administer_moneybird_node_access() {
  return user_access('administer moneybird');
}

/**
 * access righths definition for push moneybird
 */
function push_moneybird_node_access() {
  return user_access('push moneybird');
}

/**
 * Get the settings form for the module
 */
function moneybird_settings_form_content() {
  $form['moneybird'] = array(
    '#type' => 'fieldset',
    '#collapsible' => FALSE,
    '#collapsed' => FALSE,
  );

  $form['moneybird']['moneybird_client'] = array(
    '#type' => 'textfield',
    '#title' => t('Subdomain'),
    '#default_value' => variable_get('moneybird_client', ''),
    '#size' => 60,
    '#maxlength' => 64,
    '#description' => t('Please enter your moneybird subdomain.'),
  );

  $form['moneybird']['moneybird_emailaddress'] = array(
    '#type' => 'textfield',
    '#title' => t('Email address'),
    '#default_value' => variable_get('moneybird_emailaddress', ''),
    '#size' => 60,
    '#maxlength' => 64,
    '#description' => t('Please enter your moneybird account emailaddress'),
  );

  $form['moneybird']['moneybird_password'] = array(
    '#type' => 'textfield',
    '#title' => t('Password'),
    '#default_value' => variable_get('moneybird_password', ''),
    '#size' => 60,
    '#maxlength' => 64,
    '#description' => t('Please enter your Moneybird password'),
  );

  $form['moneybird']['moneybird_template'] = array(
    '#type' => 'textfield',
    '#title' => t('Which invoice template do you want to use'),
    '#default_value' => variable_get('moneybird_template', 1),
    '#size' => 60,
    '#maxlength' => 64,
    '#description' => t('Enter a number. The default is 1.'),
  );

  $form['moneybird_sync'] = array(
    '#type' => 'fieldset',
    '#collabsible' => FALSE,
    '#collapsed' => FALSE,
  );

  $form['moneybird_sync']['moneybird_cron'] = array(
    '#type' => 'checkbox',
    '#title' => t('Cron should be used for syncing the database.'),
    '#description' => 'If not checked, you may always use the button below to sync the database.',
    '#default_value' => variable_get('moneybird_cron', ''),
  );

  $form['moneybird_sync']['submit'] = array(
    '#type' => 'submit',
    '#value' => t('Sync the databases manually'),
    '#submit' => array('moneybird_sync_form_submit'),
  );

  return $form;
}

/**
 * Callback for hook_menu; Module settings form.
 */
function moneybird_settings_form($form_state) {
  $form = moneybird_settings_form_content();
  return system_settings_form($form);
}

/**
 * Implementation of additional form submit.
 */
function moneybird_sync_form_submit($form, &$form_state) {
  // Manually call the sync function.
  sync_moneybird_to_drupal();

  $form['moneybird']['moneybird_password'] = array(
    '#type' => 'textfield',
    '#title' => t('Password'),
    '#default_value' => variable_get('moneybird_password', ''),
    '#size' => 60,
    '#maxlength' => 64,
    '#description' => t('Please enter your Moneybird password'),
  );

  return system_settings_form($form);
}

/**
 * Ubercart hook, here we declare our payment method
 */
function moneybird_uc_payment_method() {
  $path = base_path() . drupal_get_path('module', 'moneybird');
  $title = '<br /><img src="' . $path . '/images/moneybird.png" />';

  $methods['moneybird'] = array(
    'name' => t('Moneybird'),
    'title' => $title,
    'desc' => t('Payment through Moneybird'),
    'callback' => 'uc_payment_method_moneybird',
    'weight' => 3,
    'checkout' => TRUE,
    'no_gateway' => TRUE,
  );

  return $methods;
}

/**
 * Hook order; Ubercart hook used as some sort of form_validation
 *
 * This function will take care of saving the invoice inside moneybird, and update the appropriate tables.
 */
function moneybird_uc_order($op, &$arg) {
  switch ($op) {
    case 'submit':
      try{
        //
        // First we check if there is a moneybird ID saved for the user of the order.
        //

        // Create a Transport
        $transport = new Moneybird\HttpClient();
        $transport->setAuth(
          variable_get('moneybird_emailaddress', ''), // put your username here
          variable_get('moneybird_password', '')  // put your password here
        );
        $connector = new Moneybird\ApiConnector(
          variable_get('moneybird_client', ''), // put your client name here (<...>.moneybird.nl)
          $transport,
          new Moneybird\XmlMapper() // create a mapper
        );

        // Ubercart tries to tie anonymous orders to an existing account by matching email-adresses.
        // We are mimicking this behaviour
        $uid_db = db_query("SELECT uid FROM {users} WHERE LOWER(mail) = LOWER(:email)", array(':email' => $arg->primary_email))->fetchField();
        if ($arg->uid == 0) {
          $uid = $uid_db ? $uid_db : 0;
        }
        else {
          $uid = $arg->uid;
        }

        // get the latest
        $muid = db_query("SELECT mid FROM {moneybird_users} WHERE uid = :uid", array(':uid' => $uid))->fetchField();
        $contact_service = $connector->getService('Contact');
        if ( $uid != 0 && $muid ) {
          // There is already a moneybird ID try to load it!
          try {
            $contact = $contact_service->getById($muid);
          } catch (Exception $e) {
            // Failed to load the contact, this means it is deleted in moneybird
            watchdog('moneybird', 'Ghost account for ' . $uid . ' overwritten.');
            $contact = new Moneybird\Contact();
          }
        }
        else {
          // There is no moneybird ID
          $contact = new Moneybird\Contact();
        }

        $contact->setData(array(
          'company_name' => $arg->billing_company,
          'firstname' => $arg->billing_first_name,
          'lastname' => $arg->billing_last_name,
          'address1' => $arg->billing_street1,
          'address2' => $arg->billing_street2,
          'zipcode' => $arg->billing_postal_code,
          'city' => $arg->billing_city,
          'country' => 'Nederland',
          'email' => $arg->primary_email,
          'phone' => $arg->billing_phone,
        ));
        $contact->save($contact_service);

        if ( $uid != 0 && !$muid ) { // there was no current moneybird user for the logged in user (or anonymous order from existing customer)
          $data = array('uid' => $uid, 'mid' => $contact->id);
          drupal_write_record('moneybird_users', $data);
        }
        elseif ( !$muid ) { // anonymous order, persist data for processing when order is finished and account has been created.
          $_SESSION['moneybird_data'] = array('order_id' => $arg->order_id, 'contact_id' => $contact->id);
        }

        //
        // Now we have the moneybird ID so we can make the invoice
        //
        $details = new Moneybird\Invoice_Detail_Array();
        $tax = NULL;
        foreach ($arg->line_items as $possible_tax) {
          if ($possible_tax['type'] == 'tax') {
            $tax = $possible_tax['data']['tax_rate'];
            break;
          }
        }
        foreach ($arg->products as $product) {
          $details->append(
            new Moneybird\Invoice_Detail(
              array(
                'amount' => $product->qty,
                'description' => $product->title,
                'price' => $product->price,
              )
            )
          );
          // TODO Taxes management.
        }

        $invoice_service = $connector->getService('Invoice');
        $invoice = new Moneybird\Invoice(
          array(
            'details' => $details,
            'lastname' => $arg->billing_last_name,
            'profileId' => variable_get('moneybird_template', 1),
            ),
          $contact
        );
        $invoice->save($invoice_service);

        $data = array('id' => $arg->order_id, 'mid' => $invoice->id);
        drupal_write_record('moneybird_orders', $data);

        //
        // Now we will send the invoice
        //
        $invoice->send($invoice_service, 'email', $arg->primary_email);
        $_SESSION['moneybird_invoice_url'] = $invoice->url;
      } catch (Exception $e) {
        if ($e->getMessage() == 'The entity or action is not found in the API') {
          return array(array('pass' => FALSE, 'message' => t('This function is not available. This can be because you are using a free account with max 3 invoices a month.')));
        }
        else {
          return array(array('pass' => FALSE, 'message' => $e->getMessage()));
        }
      }
      break;
  }
}

/**
 * Add moneybird settings to the payment method settings form.
 */
function uc_payment_method_moneybird($op, &$order, $form = NULL) {
  switch ($op) {
    case 'settings':
      $form = array_merge($form, moneybird_settings_form_content());
    return $form;
  }
}

/**
 * Hook checkout complete. Make an invoice popup display also a link if the popup get's blocked.
 * We also save the moneybird-contact data to the database. In this step of the process the new user (for anonymous checkout) is created.
 */
function moneybird_uc_checkout_complete($order, $account) {
  drupal_add_js(array('moneybird' => array('moneybirdInvoiceUrl' => $_SESSION['moneybird_invoice_url'])), 'setting');
  drupal_add_js('jQuery(document).ready(function () { window.open( Drupal.settings.moneybird.moneybirdInvoiceUrl ); });', 'inline');
  drupal_set_message(l(t('Click here to see your invoice'), $_SESSION['moneybird_invoice_url'], array('attributes' => array('target' => '_blank'))));

  // see if there is a moneybird Id to be persisted in the database:
  if ( isset( $_SESSION['moneybird_data'] ) && $_SESSION['moneybird_data']['order_id'] == $order->order_id  ) {
    if ( $order->uid != 0 ) {
      $data = array('uid' => $order->uid, 'mid' => $_SESSION['moneybird_data']['contact_id'] );
      drupal_write_record('moneybird_users', $data);
    }
    unset( $_SESSION['moneybird_data'] );
  }

}

/**
 * Hook cron. Is called by cron task.
 */
function moneybird_cron() {
  // Check if user
  if (variable_get('moneybird_cron', FALSE)) {
    // Sync moneybird database with Drupal database.
    sync_moneybird_to_drupal();
  }
}

/**
 * Function that syncs the Moneybird database with the Drupal database, checking for cancelled orders.
 */
function sync_moneybird_to_drupal() {
  $mbstatus2ucstatus = array( 'paid' => 'payment_received', 'open' => 'pending', 'late' => 'pending' );

  // Fetch all invoices.
  // $mbapi = new MoneybirdApi(variable_get('moneybird_client', ''), variable_get('moneybird_emailaddress', ''), variable_get('moneybird_password', ''));
  $transport = new Moneybird\HttpClient();
  $transport->setAuth(
    variable_get('moneybird_emailaddress', ''), // put your username here
    variable_get('moneybird_password', '')  // put your password here
  );
  $connector = new Moneybird\ApiConnector(
    variable_get('moneybird_client', ''), // put your client name here (<...>.moneybird.nl)
    $transport,
    new Moneybird\XmlMapper() // create a mapper
  );
  $invoice_service = $connector->getService('Invoice');
  $invoices = $invoice_service->getAll();
  $i = 0;
  $cancelled = 0;
  $updated = 0;
  foreach ($invoices as $invoice) {
    // if there is an invoice linked to an already existing invoice we assume it is a credit-invoice to cancel the original invoice and order.
    if ($invoice->originalInvoiceId !== NULL) {
      $result = db_query(
        'SELECT order_id FROM {moneybird_orders} mo LEFT JOIN {uc_orders} uo ON uo.order_id = mo.id WHERE mo.mid = :mid AND uo.order_status <> :order_status',
        array(
          ':mid' => $invoice->originalInvoiceId,
          ':order_status' => 'canceled',
        )
      )->fetchField();
      if ( $result !== FALSE ) {
        // the state of the moneybird-invoice is different from the one in the uc_order table, update the order!
        uc_order_update_status($result, 'canceled');
        watchdog('moneybird', 'Updated order ' . $result . ' to state ' . '"canceled"');
        $cancelled ++;
      }
    }
    elseif (isset( $mbstatus2ucstatus[$invoice->state])) { // Check if any of the invoices changed to a meaningful new state and update the order status.
      $result = db_query(
        'SELECT order_id FROM {moneybird_orders} mo LEFT JOIN {uc_orders} uo ON uo.order_id = mo.id WHERE mo.mid = :mid AND uo.order_status <> :order_status  AND uo.order_status <> \'canceled\'',
        array(
          ':mid' => $invoice->id,
          ':order_status' => $mbstatus2ucstatus[$invoice->state],
        )
      )->fetchField();
      if ( $result !== FALSE ) {
        // the state of the moneybird-invoice is different from the one in the uc_order table, update the order!
        uc_order_update_status($result, $mbstatus2ucstatus[$invoice->state]);
        watchdog('moneybird', 'Updated order ' . $result . ' to state "' . $mbstatus2ucstatus[$invoice->state] . '"');
        $updated ++;
      }
    }
    $i++;
  }
  // Finally inform the user, and insert a a log in db. We want to keep track.
  drupal_set_message(t("Sync done. Parsed %nb-invoices invoices; Cancelled %nb-cancelled orders; updated $updated orders"), array("%nb-invoices" => $i, "%nb-cancelled" => $cancelled));
  watchdog('moneybird', "Sync done. Parsed $i invoices; Cancelled $cancelled orders; updated $updated orders");
}