<?php

namespace Vinteract\Payments;

use DateTime;
use DateInterval;

use \Vinteract\Client;
use \Vinteract\Utils;

use \Vinteract\Resources\User;

use \Illuminate\Database\Eloquent\Model;

class RecurringTransactionRecord extends Model {
	
	/*
		Properties
	*/
	
	public $timestamps = false;
	
	protected $fillable = [
		"card_token_id",
		"interval",
		"amount",
		"currency",
		"description",
		"first_name",
		"last_name",
		"email",
		"phone",
		"street_1",
		"street_2",
		"city",
		"state",
		"postcode",
		"country",
		"start_date",
		"last_charge_date",
		"next_charge_date",
		"retry_failed_charge_date",
		"active",
		"cancelled",
		"user_paid_fees",
		"subscription_id",
		"product_id",
		"user_id",
		"team_id",
		"date",
	];
	
	protected $hidden = [
		"id",
		"card_token_id",
		"subscription_id",
		"product_id",
		"user_id",
	];
	
	protected $casts = [
		"active" => "boolean",
		"cancelled" => "boolean",
	];
	
	/*
		Overrides
	*/
	
	public function __construct($attributes = []) {
		
		parent::__construct($attributes);
		
		date_default_timezone_set(\Vinteract\Client::getTimezone());
		
		$this->setTable(Client::getName() . "_vpay_recurring_transactions");
		$this->setConnection(Client::getType());
		
	}
	
	protected static function boot() {
		
		parent::boot();
		
		static::setEventDispatcher(new \Illuminate\Events\Dispatcher());
		
		static::creating(function($model) {
			
			// Set default provider.
			
			$model->provider = \Vinteract\Payments\Payments::getProviderName();
			
			// Calculate charge dates.
			
			$todays_date = new DateTime();
			$todays_date->setTime(0, 0, 0);
			
			$start_date = new DateTime($model->start_date);
			
			if ($todays_date->format("Y-m-d") === $start_date->format("Y-m-d")) {
				// Set last charge date.
				$model->last_charge_date = $todays_date->format("Y-m-d");
				// Set next charge date.
				if ($model->interval == "weekly") {
					$interval = new DateInterval("P7D");
				} elseif ($model->interval == "fortnightly") {
					$interval = new DateInterval("P14D");
				} elseif ($model->interval == "monthly") {
					$interval = new DateInterval("P1M");
				} elseif ($model->interval == "annually") {
					$interval = new DateInterval("P1Y");
				}
				$model->next_charge_date = $todays_date->add($interval)->format("Y-m-d");
			} else {
				$model->next_charge_date = $start_date->format("Y-m-d");
			}
			
		});
		
	}
	
	public function newQuery($excludeDeleted = true) {
		return parent::newQuery($excludeDeleted = true)
			->orderBy("id", "desc");
	}
	
	/*
		Functions
	*/
	
	public function fail($error = "") {
		
		// Variables.
		
		$next_charge_date = new DateTime($this->next_charge_date);
		
		$retry_failed_charge_date = clone $next_charge_date;
		$retry_failed_charge_date->add(new DateInterval("P2D"));
		
		// Update model properties.
		
		//$this->next_charge_date = null; // No longer valid.
		//$this->last_charge_date = $next_charge_date->format("Y-m-d");
		$this->retry_failed_charge_date = $retry_failed_charge_date->format("Y-m-d");
		
		// Send email.
		
		if ($this->isSubscription()) {
			$emailTemplateName = "subscriptionError";
		} else {
			$emailTemplateName = "paymentError";
		}
		
		$emailTemplate = \Vinteract\Notifications\EmailTemplates::getTemplate($emailTemplateName, [
			"{FIRST_NAME}" => $this->first_name,
			"{INTERVAL}" => $this->interval,
			"{AMOUNT}" => \Vinteract\Utils::formatMoney($this->amount, true),
			"{CURRENCY}" => $this->currency,
			"{DESCRIPTION}" => $this->description,
			"{DATE}" => $next_charge_date->format("j F, Y"),
			"{RETRY_DATE}" => $retry_failed_charge_date->format("j F, Y"),
			"{ERROR}" => $error,
		]);
		
		$emailClient = new \Vinteract\Notifications\Email();
		
		$emailResponse = $emailClient->send([
			"to" => [ [ "name" => "{$this->first_name} {$this->last_name}", "email" => $this->email ] ],
			"sender" => \Vinteract\Client::getDisplayName(),
			"subject" => $emailTemplate["subject"],
			"html" => $emailTemplate["html"],
		]);
		
		// Update database.
		
		return $this->save();
		
	}
	
	public function cancel($error = "") {
		
		// Variables.
		
		$todays_date = new DateTime();
		
		// Update model properties.
		
		$this->active = 0;
		$this->cancelled = 1;
		
		$this->next_charge_date = null; // No longer valid.
		$this->last_charge_date = $todays_date->format("Y-m-d"); // So they know when the recurring transaction was cancelled.
		$this->retry_failed_charge_date = null; // No longer valid.
		
		// Send email.
		
		if ($this->isSubscription()) {
			$emailTemplateName = "subscriptionCancelled";
		} else {
			$emailTemplateName = "paymentCancelled";
		}
		
		$emailTemplate = \Vinteract\Notifications\EmailTemplates::getTemplate($emailTemplateName, [
			"{FIRST_NAME}" => $this->first_name,
			"{INTERVAL}" => $this->interval,
			"{AMOUNT}" => \Vinteract\Utils::formatMoney($this->amount, true),
			"{CURRENCY}" => $this->currency,
			"{DESCRIPTION}" => $this->description,
			"{DATE}" => $todays_date->format("j F, Y"),
			"{ERROR}" => $error,
		]);
		
		$emailClient = new \Vinteract\Notifications\Email();
		
		$emailResponse = $emailClient->send([
			"to" => [ [ "name" => "{$this->first_name} {$this->last_name}", "email" => $this->email ] ],
			"sender" => \Vinteract\Client::getDisplayName(),
			"subject" => $emailTemplate["subject"],
			"html" => $emailTemplate["html"],
		]);
		
		// Update database.
		
		return $this->save();
		
	}
	
	public function reactivate() {
		
		
		
	}
	
	public function activate() {
		
		// Update model properties.
		
		$this->active = 1;
		$this->cancelled = 0;
		
		// Update database.
		
		return $this->save();
		
	}
	
	public function charge() {
		
		// Variables.
		
		$todays_date = new DateTime();
		
		// Instantiate recurring transaction.
		
		$transaction = new \Vinteract\Payments\RecurringTransaction();
		
		// Transaction response.
		
		$transactionResponse = $transaction->process([
			"customer" => [
				"email" => $this->email,
				"ip" => $_SERVER["REMOTE_ADDR"],
				"token" => $this->card->token,
			],
			"transaction" => [
				"amount" => $this->amount,
				"currency" => $this->currency,
				"description" => $this->description,
			],
		]);
		
		// Create transaction record?
		
		if ($transactionResponse->success()) {
			
			// Create the record.
			
			$record = \Vinteract\Payments\TransactionRecord::create([
				// Transaction info.
				"transaction_id" => $transactionResponse->getTransactionId(),
				"receipt_number" => $transactionResponse->getReceiptNumber(),
				"response_code" => $transactionResponse->getResponseCode(),
				"status" => $transactionResponse->getStatus(),
				"frequency" => "recurring",
				"interval" => $this->interval,
				"amount" => $this->amount,
				"fee" => $transactionResponse->getFee(),
				"platform_fee" => \Vinteract\Payments\Payments::calculatePlatformFeeOnAmount($this->amount),
				"currency" => $this->currency,
				"description" => $this->description,
				"user_paid_fees" => $this->user_paid_fees,
				// Customer info.
				"first_name" => $this->first_name,
				"last_name" => $this->last_name,
				"email" => $this->email,
				"phone" => $this->phone,
				"street_1" => $this->street_1,
				"city" => $this->city,
				"state" => $this->state,
				"postcode" => $this->postcode,
				"country" => $this->country,
				"user_id" => $this->card->user_id,
				// Product info.
				"is_gift" => $this->product->is_gift, // TODO: POINT IN TIME!
				"includes_tax" => $this->product->includes_tax, // TODO: POINT IN TIME!
				"product_id" => $this->product->id,
				// Recurring transaction info.
				"recurring_transaction_id" => $this->id,
				// Card info.
				"card_type" => $transactionResponse->getCardType(),
				"card_last_4" => $transactionResponse->getCardNumber(),
				// Metadata.
				"date" => $todays_date->format("Y-m-d H:i:s"),
			]);
			
			// Send email.
			
			if ($this->isSubscription()) {
				$emailTemplateName = "successfulSubscriptionPayment";
			} else {
				$emailTemplateName = "successfulRecurringPayment";
			}
			
			$emailTemplate = \Vinteract\Notifications\EmailTemplates::getTemplate($emailTemplateName, [
				"{FIRST_NAME}" => $this->first_name,
				"{INTERVAL}" => $this->interval,
				"{AMOUNT}" => \Vinteract\Utils::formatMoney($this->amount, true),
				"{CURRENCY}" => $this->currency,
				"{DESCRIPTION}" => $this->description,
				"{URL}" => \Vinteract\Payments\Payments::getReceiptURL($record->receipt_number)
			]);
			
			$emailClient = new \Vinteract\Notifications\Email();
			
			$emailResponse = $emailClient->send([
				"to" => [ [ "name" => "{$this->first_name} {$this->last_name}", "email" => $this->email ] ],
				"sender" => \Vinteract\Client::getDisplayName(),
				"subject" => $emailTemplate["subject"],
				"html" => $emailTemplate["html"],
			]);
			
		}
		
		// Return transaction response.
		
		return $transactionResponse;
		
	}
	
	public function moveChargeDatesForward() {
		
		// Calculate new dates.
		
		$last_charge_date = new DateTime($this->last_charge_date);
		$next_charge_date = new DateTime($this->next_charge_date);
		
		if ($this->interval == "weekly") {
			$interval = new DateInterval("P7D");
		} elseif ($this->interval == "fortnightly") {
			$interval = new DateInterval("P14D");
		} elseif ($this->interval == "monthly") {
			$interval = new DateInterval("P1M");
		} elseif ($this->interval == "annually") {
			$interval = new DateInterval("P1Y");
		}
		
		$last_charge_date->add($interval);
		$next_charge_date->add($interval);
		
		// Update model.
		
		$this->last_charge_date = $last_charge_date->format("Y-m-d");
		$this->next_charge_date = $next_charge_date->format("Y-m-d");
		$this->retry_failed_charge_date = null;
		
		// Update database.
		
		return $this->save();
		
	}
	
	public function isAlmostReadyToBeCharged() {
		
		$todays_date = new DateTime();
		$todays_date->setTime(0, 0, 0);
		
		$next_charge_date = new DateTime($this->next_charge_date);
		$next_charge_date->setTime(0, 0, 0);
		
		if ($next_charge_date->diff($todays_date)->format("%a") === "2") {
			
			// Send email.
			
			if ($this->isSubscription()) {
				$emailTemplateName = "upcomingSubscriptionPayment";
			} else {
				$emailTemplateName = "upcomingPayment";
			}
			
			$emailTemplate = \Vinteract\Notifications\EmailTemplates::getTemplate($emailTemplateName, [
				"{FIRST_NAME}" => $this->first_name,
				"{INTERVAL}" => $this->interval,
				"{AMOUNT}" => \Vinteract\Utils::formatMoney($this->amount, true),
				"{CURRENCY}" => $this->currency,
				"{DESCRIPTION}" => $this->description,
				"{DATE}" => $next_charge_date->format("j F, Y"),
			]);
			
			$emailClient = new \Vinteract\Notifications\Email();
			
			$emailResponse = $emailClient->send([
				"to" => [ [ "name" => "{$this->first_name} {$this->last_name}", "email" => $this->email ] ],
				"sender" => \Vinteract\Client::getDisplayName(),
				"subject" => $emailTemplate["subject"],
				"html" => $emailTemplate["html"],
			]);
			
			// Return true.
			
			return true;
			
		}
		
		return false;
		
	}
	
	public function isReadyToRetryCharge() {
		
		$todays_date = new DateTime();
		$todays_date->setTime(0, 0, 0);
		
		$retry_failed_charge_date = new DateTime($this->retry_failed_charge_date);
		
		if (isset($this->retry_failed_charge_date)) {
			return $todays_date >= $retry_failed_charge_date;
		}
		
		return false;
		
	}
	
	public function isReadyToBeCharged() {
		
		$todays_date = new DateTime();
		$next_charge_date = new DateTime($this->next_charge_date);
		
		if (isset($this->next_charge_date)) {
			return $todays_date >= $next_charge_date;
		}
		
		return false;
		
	}
	
	public function isFailedTransaction() {
		return isset($this->retry_failed_charge_date);
	}
	
	public function isProduct() {
		return isset($this->product_id);
	}
	
	public function isSubscription() {
		return isset($this->subscription_id);
	}
	
	public function scopeSubscriptions($query) {
		return $query->where("subscription_id", "!=", null);
	}
	
	public function scopeActive($query) {
		return $query->where(["active" => 1, "cancelled" => 0]);
	}
	
	public static function fetch($id = null) {
		
		// Get query instance.
		
		$query = self::query();
		
		// Select by id?
		
		if (isset($id)) {
			$query->where("id", $id);
		}
		
		// Relationships.
		
		$query->with(["card", "card.user"]);
		
		// Return results.
		
		return $query->get();
		
	}
	
	/*
		Relationships
	*/
	
	public function subscription() {
		return $this->hasOne("\Vinteract\Payments\Subscription", "id", "subscription_id");
	}
	
	public function product() {
		return $this->hasOne("\Vinteract\Payments\Product", "id", "product_id");
	}
	
	public function card() {
        return $this->hasOne("\Vinteract\Payments\CardTokenRecord", "id", "card_token_id");
    }
	
}
