<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\DB;
use Leenooks\Traits\ScopeActive;

use App\Interfaces\IDs;
use App\Traits\{ProviderRef,PushNew,ScopeAccountUserAuthorised,SiteID};

/**
 * Class Payment
 * Payments that belong to an account
 *
 * Attributes for payments:
 * + lid                    : Local ID for payment
 * + paid_at				: Date payment received
 * + sid                    : System ID for payment
 * + total                  : Payment total
 * + balance                : Remaining credit on payment
 *
 * @package App\Models
 */
class Payment extends Model
{
	use ProviderRef,PushNew,ScopeActive,ScopeAccountUserAuthorised,SiteID;

	protected $casts = [
		'paid_at'=>'datetime:Y-m-d',
	];

	// Array of items that can be updated with PushNew
	protected $pushable = ['items'];

	// Any balance below this we'll assume its all used.
	private const threshold = 0.05;

	public function __get($key): mixed
	{
		return match ($key) {
			'lid' => sprintf('%06s',$this->id),
			'sid' => sprintf('%02s-%04s#%s',$this->site_id,$this->account_id,$this->lid),

			default => parent::__get($key),
		};
	}

	/* RELATIONS */

	public function account()
	{
		return $this->belongsTo(Account::class);
	}

	public function checkout()
	{
		return $this->belongsTo(Checkout::class);
	}

	public function items()
	{
		return $this->hasMany(PaymentItem::class);
	}

	public function providers()
	{
		return $this->belongsToMany(ProviderOauth::class,'payment__provider')
			->where('payment__provider.site_id',$this->site_id)
			->withPivot('ref','synctoken','created_at','updated_at');
	}

	/* SCOPES */

	/**
	 * Search for a record
	 *
	 * @param        $query
	 * @param string $term
	 * @return mixed
	 */
	public function scopeSearch($query,string $term)
	{
		// Build our where clause
		$query->where('id','like','%'.$term.'%');

		return $query;
	}

	public function scopeUnapplied($query)
	{
		return $query
			->select(['payments.id','paid_at','account_id','checkout_id','total_amt',DB::raw("SUM(ABS(amount)) as allocated")])
			->leftJoin('payment_items',['payment_items.payment_id'=>'payments.id'])
			->groupBy(['payments.id','paid_at','total_amt','account_id','checkout_id'])
			->having(DB::raw('ROUND(CAST(total_amt-COALESCE(SUM(ABS(amount)),0) AS NUMERIC),2)'),'>',self::threshold);
	}

	/* ATTRIBUTES */

	/**
	 * Work out the remaining amount from this payment that has not been allocated.
	 *
	 * @return float
	 */
	public function getBalanceAttribute(): float
	{
		$balance = $this->getTotalAttribute()-$this->items->sum('amount');

		return ($balance < self::threshold) ? 0 : $balance;
	}

	/**
	 * The total of the payment.
	 *
	 * @return float
	 * @todo Change the DB column to total
	 */
	public function getTotalAttribute(): float
	{
		return round($this->total_amt,2);
	}
}