<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Illuminate\Http\Request;

use App\Interfaces\IDs;
use App\Traits\NextKey;

/**
 * Class Product
 * Products that are available to sale, and appear on invoices
 *
 * Attributes for products:
 * + lid                    : Local ID for product (part number)
 *
 * @package App\Models
 */
class Product extends Model implements IDs
{
	use HasFactory,NextKey;

	const RECORD_ID = 'product';
	public $incrementing = FALSE;

	const CREATED_AT = 'date_orig';
	const UPDATED_AT = 'date_last';

	public $dateFormat = 'U';
	protected $table = 'ab_product';

	protected $casts = [
		// @todo convert existing data to a json array
		// 'price_group'=>'array',
	];

	protected $with = ['descriptions'];

	/* RELATIONS */

	public function descriptions()
	{
		return $this->hasMany(ProductTranslate::class);
	}

	public function services()
	{
		return $this->hasMany(Service::class);
	}

	/**
	 * Return a child model with details of the service
	 *
	 * @return \Illuminate\Database\Eloquent\Relations\MorphTo
	 */
	public function type()
	{
		return $this->morphTo(null,'model','prod_plugin_data');
	}

	/* ATTRIBUTES */

	/**
	 * Get the service category (from the product)
	 *
	 * @return string
	 */
	public function getCategoryAttribute()
	{
		return $this->prod_plugin_file ?: 'Other';
	}

	public function getContractTermAttribute()
	{
		switch ($this->prod_plugin_file) {
			case 'ADSL': return $this->plugin()->contract_term;
			// @todo Incorporate into DB
			case 'VOIP': return 12;

			// @todo Change this after contracts implemented.
			default:
				return 'TBA';
		}
	}

	public function getDefaultBillingAttribute()
	{
		return Arr::get($this->PricePeriods(),$this->price_recurr_default);
	}

	public function getDefaultCostAttribute()
	{
		// @todo Integrate this into a Tax::class
		return Arr::get($this->price_array,sprintf('%s.1.price_base',$this->price_recurr_default))*1.1;
	}

	private function getDefaultLanguage()
	{
		return config('SITE')->language;
	}

	public function getDescriptionAttribute()
	{
		// @todo If the user has selected a specific language.
		return $this->description($this->getDefaultLanguage());
	}

	/**
	 * Product Local ID
	 *
	 * @return string
	 */
	public function getLIDattribute(): string
	{
		return sprintf('%04s',$this->id);
	}

	public function getMinimumCostAttribute()
	{
		$table = [
			0=>4,
			1=>1,
			2=>1/3,
			3=>1/6,
			4=>1/12,
			5=>1/24,
			6=>1/36,
			7=>1/48,
			8=>1/60,
		];

		return $this->setup_cost + ( $this->default_cost * Arr::get($table,$this->price_recurr_default) * $this->contract_term);
	}

	public function getNameAttribute(Language $lo=NULL)
	{
		if (is_null($lo))
			$lo = $this->getDefaultLanguage();

		return $this->descriptions->where('language_id',$lo->id)->first()->description_short;
	}

	public function getNameShortAttribute(Language $lo=NULL)
	{
		if (is_null($lo))
			$lo = $this->getDefaultLanguage();

		return $this->descriptions->where('language_id',$lo->id)->first()->name;
	}

	public function getProductTypeAttribute()
	{
		return $this->plugin()->product->name;
	}

	public function getPriceArrayAttribute()
	{
		try {
			return unserialize($this->attributes['price_group']);
		} catch (\Exception $e) {
			Log::debug('Problem with Price array in product ',['pid'=>$this->id]);
			return [];
		}
	}

	public function getPriceTypeAttribute()
	{
		$table = [
			0=>_('One-time Charge'),
			1=>_('Recurring Membership/Subscription'),
			2=>_('Trial for Membership/Subscription'),
		];
	}

	public function getProductIdAttribute()
	{
		return sprintf('#%04s',$this->id);
	}

	public function getSetupCostAttribute()
	{
		// @todo Integrate this into a Tax::class
		return Arr::get($this->price_array,sprintf('%s.1.price_setup',$this->price_recurr_default))*1.1;
	}

	/**
	 * Product System ID
	 *
	 * @return string
	 */
	public function getSIDattribute(): string
	{
		return sprintf('%02s-%s',$this->site_id,$this->getLIDattribute());
	}

	/**
	 * Return if this product captures usage data
	 *
	 * @return bool
	 */
	public function hasUsage(): bool
	{
		// @todo This should be configured in the DB
		return in_array($this->model, ['App\Models\Product\Adsl']);
	}

	public function scopeActive()
	{
		return $this->where('active',TRUE);
	}

	public function description(Language $lo=NULL)
	{
		if (is_null($lo))
			$lo = $this->getDefaultLanguage();

		return $this->descriptions->where('language_id',$lo->id)->first()->description_full;
	}

	public function orderValidation(Request $request)
	{
		return $this->plugin()->orderValidation($request);
	}

	private function plugin()
	{
		switch ($this->prod_plugin_file) {
			case 'ADSL':
				return AdslPlan::findOrFail($this->prod_plugin_data);
			case 'VOIP':
				return new PlanVoip;
		}
	}

	/**
	 * Get the price for this product based on the period being requested.
	 *
	 * If the price period doesnt exist, we'll take the default period (0) which should.
	 *
	 * @param int $period
	 * @return mixed
	 */
	public function price(int $period,string $key='price_base')
	{
		return Arr::get(
			$this->price_array,
			sprintf('%s.1.%s',$period,$key),
			Arr::get($this->price_array,sprintf('%s.0.%s',$period,$key))
		);
	}

	public function PricePeriods()
	{
		return [
			0=>_('Weekly'),
			1=>_('Monthly'),
			2=>_('Quarterly'),
			3=>_('Semi-Annually'),
			4=>_('Annually'),
			5=>_('Two years'),
			6=>_('Three Years'),
			7=>_('Four Years'),
			8=>_('Five Years'),
		];
	}

	/**
	 * Get the product name
	 *
	 * @param Language $lo
	 * @return string Product Name
	 */
	public function name(Language $lo=NULL)
	{
		if (is_null($lo))
			$lo = $this->getDefaultLanguage();

		return $this->descriptions->where('language_id',$lo->id)->first()->name;
	}
}