<?php

namespace App\Console\Commands\Intuit;

use Illuminate\Console\Command;
use Intuit\Jobs\AccountingInvoiceUpdate;
use Intuit\Models\Invoice as AccInvoice;
use Intuit\Traits\ProviderTokenTrait;

use App\Models\Invoice;

class InvoiceAdd extends Command
{
	use ProviderTokenTrait;

	/**
	 * The name and signature of the console command.
	 *
	 * @var string
	 */
	protected $signature = 'intuit:invoice:add'
		.' {id : Invoice ID}'
		.' {user? : User Email}';

	/**
	 * The console command description.
	 *
	 * @var string
	 */
	protected $description = 'Add an invoice to the accounting provider';

	/**
	 * Execute the console command.
	 *
	 * @return int
	 * @throws \Exception
	 */
	public function handle()
	{
		$to = $this->providerToken($this->argument('user'));

		$io = Invoice::findOrFail($this->argument('id'));

		// Check the customer exists
		if ($io->account->providers->where('pivot.provider_oauth_id',$to->provider->id)->count() !== 1)
			throw new \Exception(sprintf('Account [%d] for Invoice [%d] not defined',$io->account_id,$io->id));

		$ao = $io->account->providers->where('pivot.provider_oauth_id',$to->provider->id)->pop();

		// Some validation
		if (! $ao->pivot->ref) {
			$this->error(sprintf('Accounting not defined for account [%d]',$io->account_id));

			return self::FAILURE;
		}

		$acc = new AccInvoice;
		$acc->CustomerRef = (object)['value'=>$ao->pivot->ref];
		$acc->DocNumber = $io->lid;
		$acc->TxnDate = $io->created_at->format('Y-m-d');
		$acc->DueDate = $io->due_at->format('Y-m-d');

		$lines = collect();
		$c = 0;
		$subtotal = 0;

		// @todo Group these by ItemRef and the Same Unit Price and Description, so that we can then use quantity to represent the number of them.
		foreach ($io->items->groupBy(
			fn($item)=>
				sprintf('%s.%s.%s.%s',
					$item->item_type_name,
					$item->price_base,
					$item->product->provider_ref($to->provider),
					$item->taxes->pluck('description')->join('|'))) as $os)
		{
			$key = $os->first();

			// Some validation
			if (! ($ref=$key->product->provider_ref($to->provider))) {
				$this->error(sprintf('Accounting not defined in product [%d]',$key->product_id));

				return self::FAILURE;
			}

			if ($key->taxes->count() !== 1) {
				$this->error(sprintf('Cannot handle when there is not just 1 tax line [%d]',$key->id));

				return self::FAILURE;
			}

			$c++;
			$line = new \stdClass;
			$line->Id = $c;
			$line->DetailType = 'SalesItemLineDetail';
			$line->Description = $key->item_type_name;
			$line->SalesItemLineDetail = (object)[
				'Qty' => $os->sum('quantity'),
				'UnitPrice' => $key->price_base,
				'ItemRef' => ['value'=>$ref],
				// @todo It is assumed there is only 1 tax category
				'TaxCodeRef' => ['value'=>$tcf=$key->taxes->first()->tax->provider_ref($to->provider)],
			];
			$line->Amount = round($os->sum('quantity')*$key->price_base,2);

			$subtotal += $line->Amount;

			$lines->push($line);
		}

		$acc->Line = $lines;

		// If our subtotal doesnt match, we need to add a tax line
		if ($io->subtotal !== $subtotal) {
			$acc->TxnTaxDetail = (object)[
				'TotalTax' => $x=$io->total-$subtotal,
				'TaxLine' => [
					(object) [
						'Amount' => $x,
						'DetailType' => 'TaxLineDetail',
						'TaxLineDetail' => (object)[
							// @todo It is assumed there is only 1 tax category
							'TaxRateRef' => (object)['value'=>$to->API()->getTaxCodeQuery($tcf)->getTaxRateRef()->first()],
							'NetAmountTaxable' => $io->subtotal,
						]
					]
				]
			];
		}

		return AccountingInvoiceUpdate::dispatchSync($to,$acc);
	}
}