<?php

namespace App\Console\Commands;

use App\User;
use Illuminate\Console\Command;

use App\Classes\External\Accounting\Quickbooks;
use App\Models\Account;
use App\Models\External\Integrations;
use QuickBooksOnline\API\Data\IPPEmailAddress;
use QuickBooksOnline\API\Data\IPPPhysicalAddress;

class QuickAccounts extends Command
{
	/**
	 * The name and signature of the console command.
	 *
	 * @var string
	 */
	protected $signature = 'external:sync:accounts {--m|match : Match Display Name}';

	/**
	 * The console command description.
	 *
	 * @var string
	 */
	protected $description = 'Sync Account numbers with External Sources';

	/**
	 * Create a new command instance.
	 *
	 * @return void
	 */
	public function __construct()
	{
		parent::__construct();
	}

	/**
	 * Execute the console command.
	 *
	 * @return mixed
	 * @throws \Exception
	 */
	public function handle()
	{
		foreach (Integrations::active()->type('ACCOUNTING')->get() as $into)
		{
			switch ($into->name)
			{
				case 'quickbooks':
					$api = new Quickbooks($into->user);
					break;

				default:
					throw new \Exception('No handler for: ',$into-name);
			}

			foreach ($api->getCustomers(TRUE) as $r)
			{
				$this->info(sprintf('Checking [%s] (%s)',$r->Id,$r->DisplayName));

				if ($r->Notes == 'Cash Only')
				{
					$this->warn(sprintf('Skipping [%s] (%s)',$r->Id,$r->DisplayName));
					continue;
				}

				if (! $this->option('match') AND (! $r->CompanyName AND (! $r->FamilyName AND ! $r->GivenName)))
				{
					$this->error(sprintf('No COMPANY or PERSONAL details for [%s] (%s)',$r->Id,$r->DisplayName));
					continue;
				}

				if ($this->option('match')) {
					$ao = Account::where('company',$r->DisplayName);

					if (! $ao->count()) {
						$uo = User::where('lastname',$r->FamilyName);

						if ($r->GivenName)
							$uo->where('firstname',$r->GivenName);

						if ($uo->count() > 1 OR (! $uo->count()))
						{
							$this->error(sprintf('No SINGLE Users matched for [%s] (%s)',$r->Id,$r->DisplayName));
							continue;
						}

						$uo = $uo->first();
						$ao = $uo->accounts->where('active',TRUE);
					}

				} else {
					if ($r->CompanyName) {
						$ao = Account::where('company',$this->option('match') ? $r->DisplayName : $r->CompanyName);
					} else {
						$uo = User::where('lastname',$r->FamilyName);

						if ($r->GivenName)
							$uo->where('firstname',$r->GivenName);

						if ($uo->count() > 1 OR (! $uo->count()))
						{
							$this->error(sprintf('No SINGLE matched for [%s] (%s)',$r->Id,$r->DisplayName));
							continue;
						}

						$uo = $uo->first();
						$ao = $uo->accounts->where('active',TRUE);
					}
				}

				if (! $ao->count())
				{
					$this->error(sprintf('No Accounts matched for [%s] (%s)',$r->Id,$r->DisplayName));
					continue;
				}

				if ($ao->count() > 1)
				{
					$this->error(sprintf('Too Many Accounts (%s) matched for [%s] (%s)',$ao->count(),$r->Id,$r->DisplayName));
					continue;
				}

				$ao = $ao->first();

				// If we are matching on DisplayName, make sure the account is updated correct for Business or Personal Accounts
				$oldr = clone $r;

				// @NOTE: This overwrites the ABN if it exists.
				if ($r->PrimaryTaxIdentifier)
				{
					$this->warn(sprintf('ABN Overwrite for (%s)',$r->DisplayName));
				}

				switch ($ao->type)
				{
					case 'Business':
						$r->CompanyName = $ao->company;
						$r->ResaleNum = $ao->AccountId;
						$r->SalesTermRef = '7'; // @todo

						if ($ao->first_name)
							$r->GivenName = chop($ao->user->firstname); // @todo shouldnt be required
						if ($ao->last_name)
							$r->FamilyName = $ao->user->lastname;

						if ($ao->address1)
						{
							if (! $r->BillAddr)
								$r->BillAddr = new IPPPhysicalAddress;

							$r->BillAddr->Line1 = $ao->user->address1;
							$r->BillAddr->Line2 = $ao->user->address2;
							$r->BillAddr->City = $ao->user->city;
							$r->BillAddr->CountrySubDivisionCode = strtoupper($ao->user->state);
							$r->BillAddr->PostalCode = $ao->user->postcode;
							$r->BillAddr->Country = 'Australia'; // @todo

							//$r->ShipAddr = $r->BillAddr;
						}

						if ($ao->email) {
							if (! $r->PrimaryEmailAddr)
								$r->PrimaryEmailAddr = new IPPEmailAddress;

							$r->PrimaryEmailAddr->Address = $ao->user->email;
							$r->PreferredDeliveryMethod = 'Email';
						}

						if (! $r->Balance)
							$r->Active = $ao->active ? 'true' : 'false';

						break;

					case 'Private':
						$r->CompanyName = NULL;
						$r->DisplayName = sprintf('%s %s',$ao->user->lastname,$ao->user->firstname);
						$r->ResaleNum = $ao->AccountId;
						$r->SalesTermRef = '7'; // @todo

						if ($ao->first_name)
							$r->GivenName = chop($ao->user->firstname); // @todo shouldnt be required
						if ($ao->last_name)
							$r->FamilyName = $ao->user->lastname;

						if ($ao->address1)
						{
							if (! $r->BillAddr)
								$r->BillAddr = new IPPPhysicalAddress;

							$r->BillAddr->Line1 = $ao->user->address1;
							$r->BillAddr->Line2 = $ao->user->address2;
							$r->BillAddr->City = $ao->user->city;
							$r->BillAddr->CountrySubDivisionCode = strtoupper($ao->user->state);
							$r->BillAddr->PostalCode = $ao->user->postcode;
							$r->BillAddr->Country = 'Australia'; // @todo

							//$r->ShipAddr = $r->BillAddr;
						}

						if ($ao->email) {
							if (! $r->PrimaryEmailAddr)
								$r->PrimaryEmailAddr = new IPPEmailAddress;

							$r->PrimaryEmailAddr->Address = $ao->user->email;
							$r->PreferredDeliveryMethod = 'Email';
						}

						if (! $r->Balance)
							$r->Active = $ao->active ? 'true' : 'false';

						break;

					default:
						throw new \Exception('Unhandled account type: '.$ao->type);

				}

				// If something changed, lets update it.
				if (count(array_diff_assoc(object_to_array($r,FALSE),object_to_array($oldr,FALSE))))
				{
					$api->updateCustomer($r,[]);
				}

				// If external integration doesnt exist, lets create it.
				if (! $ao->ExternalAccounting($into))
				{
					$ao->external()->attach([$into->id=>['site_id'=>1,'link'=>$r->Id]]); // @todo site_id
				}

				// If the integration ID doesnt exist in the integration source, add it.
				if (! $r->ResaleNum)
				{
					$api->updateCustomer($r,['ResaleNum'=>$ao->AccountId]);
				}

				// If integration exist, double check the numbers match
				if ($r->ResaleNum != $ao->AccountId) {
					$this->warn(sprintf('Integration ID Mismatch AID [%s] ID [%s]',$ao->id,$r->Id));
					continue;
				}
			}
		}
	}
}

function object_to_array($object,$encode=TRUE)
{
	// For child arrays, we just encode
	if ($encode)
		return json_encode($object);

	if (is_object($object)) {
		return array_map(__FUNCTION__,get_object_vars($object));
	} else if (is_array($object)) {
		return array_map(__FUNCTION__,$object);
	} else {
		return $object;
	}
}