<?php

namespace DVCNotifier;

require __DIR__ . '/class.Prices.php';
require __DIR__ . '/class.Log.php';
require 'vendor/autoload.php';

use SparkPost\SparkPost;
use GuzzleHttp\Client;
use Ivory\HttpAdapter\Guzzle6HttpAdapter;

/**
 * Class Notifier
 *
 * @package DVCNotifier
 */
class Notifier {

	/**
	 * Price class
	 *
	 * @var Prices
	 */
	private $prices;

	/**
	 * Log Class
	 *
	 * @var Log Log class.
	 */
	private $log;

	/**
	 * Default settings
	 *
	 * @var array Default settings
	 */
	private $settings = array(
		'tables'        => array(
			'posts_table'    => '',
			'meta_table'     => '',
			'term_table'     => '',
			'term_tax_table' => '',
		),
		'log'           => array(
			'uri' => '',
		),
		'sparkpost_key' => '',
		'timeout'       => 10,
		'unsubscribe'   => '',
		'salt'          => '',
		'debug'         => true,
	);

	/**
	 * Notifier constructor.
	 *
	 * @param array $settings Specific settings.
	 */
	public function __construct( $settings ) {

		$this->settings = array_merge( $this->settings, $settings );
		$this->log      = new Log( $settings['log'] );
		$this->prices   = new Prices( $settings['tables'], $this );

		return;
	}

	/**
	 * Begin the notification process.
	 */
	public function start() {

		$subscribers = $this->get_subscribers();

		if ( count( $subscribers ) > 0 ) {
			$listings = $this->get_listings();
			if ( count( $listings ) > 0 ) {
				$resorts  = $this->get_resorts();
				$statuses = $this->get_statuses();

				$listing_data    = array();
				$all_subscribers = array();

				foreach ( $listings as $listing ) {
					$this->log->write( 'Loaded listings ' . $listing['ID'] . '.' );

					$meta = $this->get_metas( $listing['ID'] );

					$details = array(
						'listing_id' => $listing['post_title'],
						'date_added' => $listing['post_date'],
						'link'       => $listing['guid'],
						'status'     => $this->get_meta_details( $statuses, $meta, 'listing_status' ),
						'resort'     => $this->get_meta_details( $resorts, $meta, 'listing_resort' ),
					);

					// get each unique meta key.
					$get = array(
						'listing_points_contract',
						'listing_year',
						'listing_points_price',
					);
					foreach ( $get as $key ) {
						$details[ $key ] = $this->get_meta( $meta, $key );
					}

					$points_available = 0;
					$points_fields    = get_field( 'listing_points_available_repeater', $listing['ID'] );

					// This is the "middle" year used in the pulling of points data.
					// The following code will pull the year before and after this setting
					// which will then be used by the email that goes out via SparkPost
					$year = 2025;

					$years = array( 0, 0, 0 );

					foreach ( $points_fields as $points_field ) {
						if ( $points_field['listing_points_available_year'] == $year - 1 ) {
							$years[0] = $points_field['listing_points_available_points'];
						} elseif ( $points_field['listing_points_available_year'] == $year ) {
							$points_available = $points_field['listing_points_available_points'];
							$years[1]         = $points_field['listing_points_available_points'];
						} elseif ( $points_field['listing_points_available_year'] == $year + 1 ) {
							$years[2] = $points_field['listing_points_available_points'];
						}
					}

					$points_coming = implode( ' | ', $years );

					$details['listing_price']            = '$' . number_format( floatval( intval( $details['listing_points_price'] ) * intval( $details['listing_points_contract'] ) ), 0, '.', ',' );
					$details['listing_points_available'] = $points_available;
					$details['listing_points_coming']    = $points_coming;
					// load data.
					$listing_data[] = $details;
				}

				usort(
					$listing_data,
					function ( $a, $b ) {

						return strcasecmp( $a['listing_id'], $b['listing_id'] );
					}
				);

				// loop the subscribers and add their variables to array.
				foreach ( $subscribers as $subscriber ) {
					$all_subscribers[] = array(
						'email' => $subscriber['email_address'],
						'ulink' => $this->generate_link( $subscriber['email_address'] ),
						'name'  => $subscriber['name'],
					);
				}

				$email = $this->mail( $all_subscribers, $listing_data );

				if ( $email ) {
					$this->log->write( 'Email successfully sent.' );
					$this->prices->sync_prices();
				} else {
					$this->log->write( 'Email failed to send.' );
				}
			} else {
				$this->log->write( 'No listings found.' );
			}
		} else {
			$this->log->write( 'No subscribers found.' );
		}

		$this->log->close();
	}

	/**
	 * Send mail.
	 *
	 * @param array $subscribers List of subscribers.
	 * @param array $listings    List of listings.
	 *
	 * @return array|bool
	 */
	private function mail( $subscribers, $listings ) {

		$result = false;

		$transmission = array(
			'campaign_id'      => 'daily-listing-notifier',
			'substitutionData' => array(
				'listings' => $listings,
			),
			'recipients'       => array(),
			'template'         => 'daily-listing-notifier',
		);

		foreach ( $subscribers as $subscriber ) {

			$transmission['recipients'][] = array(
				'address'           => $subscriber['email'],
				'substitution_data' => array(
					'unsubscribe_link' => $subscriber['ulink'],
					'name'             => $subscriber['name'],
				),
			);
		}

		try {
			$http_adapter = new Guzzle6HttpAdapter( new Client() );
			$sparky       = new SparkPost(
				$http_adapter,
				array(
					'key'     => $this->settings['sparkpost_key'],
					'timeout' => $this->settings['timeout'],
				)
			);

			$result = $sparky->transmission->send( $transmission );
			$this->log->write( $result );
		} catch ( \Exception $e ) {
			$this->log->write( $e );
		}

		return $result;
	}

	/**
	 * Get Meta from array.
	 *
	 * @param array  $metas Meta array.
	 * @param string $key   Meta key.
	 *
	 * @return object|bool
	 */
	public function get_meta( $metas, $key ) {

		if ( isset( $metas[ $key ] ) ) {
			return $metas[ $key ];
		} else {
			return false;
		}
	}

	/**
	 * Get all Metas by id.
	 *
	 * @param int $id Listing id.
	 *
	 * @return array
	 */
	public function get_metas( $id ) {

		global $wpdb;

		$meta      = $this->settings['tables']['meta_table'];
		$meta_keys = array();

		$metas = $wpdb->get_results(
			"
			SELECT
				meta_key,
				meta_value
			FROM $meta
			WHERE post_id = '$id'
		",
			ARRAY_A
		);

		foreach ( $metas as $meta ) {
			if ( strpos( $meta['meta_key'], 'listing' ) === 0 ) {
				$meta_keys[ $meta['meta_key'] ] = $meta['meta_value'];
			}
		}

		$this->log->write( "Metas loaded for pid #$id." );

		return $meta_keys;
	}

	/**
	 * Get all subscribers.
	 *
	 * @return array|null|object
	 */
	public function get_subscribers() {

		global $wpdb;

		$posts = $this->settings['tables']['posts_table'];
		$meta  = $this->settings['tables']['meta_table'];

		return $wpdb->get_results(
			"
			SELECT
				$posts.ID,
				$posts.post_title AS name,
				$meta.meta_value AS email_address
			FROM $posts
			INNER JOIN $meta
				ON $posts.ID = $meta.post_id
			WHERE $posts.post_type = 'subscriber'
				AND $posts.post_status = 'publish'
				AND $meta.meta_key = 'subscriber_email'
		",
			ARRAY_A
		);
	}

	/**
	 * Get all listings.
	 *
	 * @return array|null|object
	 */
	public function get_listings() {

		global $wpdb;

		$yesterday        = date( 'Y-m-d H:i:s', strtotime( date( 'Y-m-d', time() ) . ' 00:00:00' ) - 86400 );
		$posts            = $this->settings['tables']['posts_table'];
		$meta             = $this->settings['tables']['meta_table'];
		$reduced_listings = $this->prices->get_reduced_listings();

		$status_arr            = array();
		$status_offer_accepted = get_term_by( 'slug', 'offer-accepted', 'statuses' );
		$status_sale_pending   = get_term_by( 'slug', 'sale-pending', 'statuses' );

		if ( false !== $status_offer_accepted ) {
			$status_arr[] = $status_offer_accepted->term_id;
		}

		if ( false !== $status_sale_pending ) {
			$status_arr[] = $status_sale_pending->term_id;
		}

		$listings_query = '';

		if ( ! empty( $status_arr ) ) {
			$statuses = implode( ',', $status_arr );

			$listings_query .= "SELECT DISTINCT
					ID,
					post_title,
					post_date,
					guid
				FROM $posts
				INNER JOIN $meta
					ON $posts.ID = $meta.post_id
				WHERE $posts.post_type = 'listing'
					AND $posts.post_status = 'publish'
					AND $meta.meta_key = 'listing_status'
					AND $meta.meta_value NOT IN ($statuses)
			";
		} else {
			$listings_query .= "SELECT DISTINCT
					ID,
					post_title,
					post_date,
					guid
				FROM $posts
				WHERE $posts.post_type = 'listing'
				      AND $posts.post_status = 'publish'
          	";
		}

		if ( count( $reduced_listings ) > 0 ) {
			$reduced_listing_ids = implode( ',', $reduced_listings );
			$listings_query     .= " AND (post_date >= '$yesterday' OR ID IN ($reduced_listing_ids))";
		} else {
			$listings_query .= " AND post_date >= '$yesterday'";
		}

		return $wpdb->get_results( $listings_query, ARRAY_A );
	}

	/**
	 * Get all resorts.
	 *
	 * @return array
	 */
	public function get_resorts() {

		global $wpdb;
		$posts        = $this->settings['tables']['posts_table'];
		$resort_names = array();

		$resorts = $wpdb->get_results(
			"
			SELECT
				ID,
				post_title
			FROM $posts
			WHERE post_type = 'resort'
				AND post_status = 'publish'
		",
			ARRAY_A
		);

		foreach ( $resorts as $resort ) {
			$resort_names[ $resort['ID'] ] = $resort['post_title'];
		}
		$this->log->write( 'Resorts loaded.' );

		return $resort_names;
	}

	/**
	 * Get all statuses.
	 *
	 * @return array
	 */
	public function get_statuses() {

		global $wpdb;

		$terms        = $this->settings['tables']['term_table'];
		$tax          = $this->settings['tables']['term_tax_table'];
		$status_names = array();

		$statuses = $wpdb->get_results(
			"
			SELECT
				$terms.term_id,
				$terms.slug,
				$terms.name
			FROM $terms
			INNER JOIN $tax
				ON $terms.term_id = $tax.term_id
			WHERE $tax.taxonomy = 'statuses'
		",
			ARRAY_A
		);

		foreach ( $statuses as $status ) {
			$status_names[ $status['term_id'] ] = $status['name'];
		}

		$this->log->write( 'Statuses loaded.' );

		return $status_names;
	}

	/**
	 * Get Meta details.
	 *
	 * @param object $details Details.
	 * @param array  $metas   Metas.
	 * @param string $key     Meta key.
	 *
	 * @return string
	 */
	public function get_meta_details( $details, $metas, $key ) {

		$value = '';
		if ( isset( $metas[ $key ] ) && isset( $details[ $metas[ $key ] ] ) ) {
			$value = $details[ $metas[ $key ] ];
		}

		return $value;
	}

	/**
	 * Generate unsubscribe link.
	 *
	 * @param string $email User email.
	 *
	 * @return string
	 */
	public function generate_link( $email ) {

		return '<a href="' . $this->settings['unsubscribe'] . '?email=' . $email . '&key=' . crypt( $email, $this->settings['salt'] ) . '">please click here</a>';
	}
}
