<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
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'];

	// @todo Change these to CONSTS so it's easier to reference through out the code
	public const type = [
		0 => 'Service Charge',
		1 => 'Hardware',                          // *
		2 => 'Service Relocation Fee',            // * Must have corresponding SERVICE_ID
		3 => 'Service Change',                    // * Must have corresponding SERVICE_ID
		4 => '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
	];

	/* 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 sprintf('%3.2f',$this->quantity * $this->price_base - $this->discount_amt);
	}

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

	/**
	 * Total including taxes
	 *
	 * @return float
	 */
	public function getTotalAttribute(): float
	{
		return sprintf('%3.2f',$this->getSubTotalAttribute()+$this->getTaxAttribute());
	}

	/* 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);
		}
	}
}