<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\DB;
use Leenooks\Carbon as LeenooksCarbon;

use App\Traits\PushNew;

/**
 * Class Invoice Items
 * Items that belong on an invoice
 *
 * Attributes for services:
 * + sub_total          : Value of item
 * + tax                : Total of all taxes
 * + total              : Total including taxes
 */
class InvoiceItem extends Model
{
	use PushNew;

	protected $dates = [
		'start_at',
		'stop_at',
	];

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

	public const INVOICEITEM_SERVICE = 0;
	public const INVOICEITEM_SETUP = 4;

	// @todo Change these to CONSTS so it's easier to reference through out the code
	public const type = [
		self::INVOICEITEM_SERVICE => 'Service Charge',
		1 => 'Hardware',                          // *
		2 => 'Service Relocation Fee',            // * Must have corresponding SERVICE_ID
		3 => 'Service Change',                    // * Must have corresponding SERVICE_ID
		self::INVOICEITEM_SETUP => 'Service Connection',		// * Must have corresponding SERVICE_ID
		6 => 'Service Cancellation',              // * Must have corresponding SERVICE_ID
		7 => 'Extra Product/Service Charge',      // * Service Billing in advance, Must have corresponding SERVICE_ID
		8 => 'Product Addition',                  // * Additional Product Customisation, Must have corresponding SERVICE_ID
		9 => 'Usage Charge',
		120 => 'Credit/Debit Transfer',           // * SERVICE_ID is NULL, MODULE_ID is NULL, MODULE_REF is NULL : INVOICE_ID is NOT NULL
		123 => 'Shipping',
		124 => 'Late Payment Fee',                // * SERVICE_ID is NULL, MODULE_ID = CHECKOUT MODULE,
		125 => 'Payment Fee',                     // * SERVICE_ID is NULL, MODULE_ID = CHECKOUT MODULE, MODULE_REF = CHECKOUT NAME
		126 => 'Other',                           // * MODEL_ID should be a module
		127 => 'Rounding',                        // * SERVICE_ID is NULL, MODULE_ID is NULL, MODULE_REF is NULL
	];

	/* STATIC */

	public static function boot()
	{
		parent::boot();

		static::created(function($model) {
			// If this items were a charge, we'll update the charge to processed
			if (($model->module_id === 30) && $model->module_ref) {
				$o = Charge::findOrfail($model->module_ref);
				$o->processed = TRUE;
				$o->save();
			}
		});
	}

	public static function invoice_items(): Builder
	{
		// Choose your select tag and group by tag
		return (new InvoiceItem)
			->select([
				//'invoice_id',
				DB::raw('ROUND(CAST(SUM(quantity*price_base) AS NUMERIC),2) AS item'),
				DB::raw('ROUND(CAST(SUM(COALESCE(amount,0)) AS NUMERIC),2) AS tax'),
				DB::raw('ROUND(CAST(SUM(COALESCE(invoice_items.discount_amt,0)) AS NUMERIC),2) AS discount'),
				DB::raw('ROUND(CAST(SUM(ROUND(CAST(ROUND(CAST(quantity*(price_base-COALESCE(invoice_items.discount_amt,0)) AS NUMERIC),2)+COALESCE(amount,0) AS NUMERIC),2)) AS NUMERIC),2) AS item_total'),
				//DB::raw('0 as payments'),
				//DB::raw('0 as payment_fees'),
			])
			->leftjoin('invoice_item_taxes',['invoice_item_taxes.invoice_item_id'=>'invoice_items.id'])
			//->rightjoin('invoices',['invoices.id'=>'invoice_items.invoice_id'])
			->where('invoice_items.active',TRUE)
			->where(fn($query)=>$query
				->where('invoice_item_taxes.active',TRUE)
				->orWhereNull('invoice_item_taxes.active'))
			//->where('invoices.active',TRUE)
			//->groupBy(['invoice_id'])
			;
	}

	/* RELATIONS */

	public function invoice()
	{
		return $this->belongsTo(Invoice::class);
	}

	public function product()
	{
		return $this->belongsTo(Product::class);
	}

	public function service()
	{
		return $this->belongsTo(Service::class);
	}

	public function taxes()
	{
		return $this->hasMany(InvoiceItemTax::class);
	}

	/* ATTRIBUTES */

	public function getItemTypeNameAttribute()
	{
		switch ($this->item_type) {
			// * Line Charge Topic on Invoice.
			case 0:
				if ($this->start_at)
					return sprintf('%s [%s]','Product/Service',
						(($this->start_at == $this->stop_at) || (! $this->stop_at)) ? $this->start_at->format('Y-m-d') : sprintf('%s -> %s',$this->start_at->format('Y-m-d'),$this->stop_at->format('Y-m-d')));
				else
					return 'Product/Service';

			// * Excess Service Item, of item 0, must have corresponding SERVICE_ID
			case 5:
				return sprintf('%s [%s] (%s)','Excess Usage',
					$this->start_at == $this->stop_at ? $this->start_at->format('Y-m-d') : sprintf('%s -> %s',$this->start_at->format('Y-m-d'),$this->stop_at->format('Y-m-d')),
					$this->module_text()
				);

			default:
				return ($this->module_id == 30 ? 'Additional Charge: ' : '').Arr::get(self::type,$this->item_type,'Unknown');
		}
	}

	/**
	 * We need to cast some dates to LeenooksCarbon to get access to startOfHalf()/endOfHalf() methods
	 *
	 * @param $value
	 * @return LeenooksCarbon
	 */
	public function getStartAtAttribute($value): LeenooksCarbon
	{
		return LeenooksCarbon::create($value);
	}

	/**
	 * We need to cast some dates to LeenooksCarbon to get access to startOfHalf()/endOfHalf() methods
	 *
	 * @param $value
	 * @return LeenooksCarbon
	 */
	public function getStopAtAttribute($value): LeenooksCarbon
	{
		return LeenooksCarbon::create($value);
	}

	/**
	 * Sub total of item
	 *
	 * @return float
	 */
	public function getSubTotalAttribute(): float
	{
		return round($this->quantity*($this->price_base-$this->discount_amt),2);
	}

	/**
	 * Total of all taxes
	 *
	 * @return mixed
	 */
	public function getTaxAttribute(): float
	{
		return round($this->taxes->sum('amount'),2);
	}

	/**
	 * Total including taxes
	 *
	 * @return float
	 */
	public function getTotalAttribute(): float
	{
		return round($this->getSubTotalAttribute()+$this->getTaxAttribute(),2);
	}

	/* METHODS */

	/**
	 * Add taxes to this record
	 */
	public function addTaxes(Collection $taxes)
	{
		// Refuse to change an existing record
		if ($this->exists)
			throw new \Exception('Refusing to add Taxes to existing record');

		foreach($taxes as $to) {
			$iit = new InvoiceItemTax;
			$iit->tax_id = $to->id;
			$iit->amount = round($this->quantity*$this->price_base*$to->rate,3);
			$iit->site_id = 1;
			$this->taxes->push($iit);
		}
	}

	public function module_text(){
		switch ($this->module_id) {
			// Charges Module
			case 30: return Charge::findOrFail($this->module_ref)->name;

			default: abort(500,'Unable to handle '.$this->module_id);
		}
	}
}