<?php

namespace App\Classes\Dynamic;

use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Log;

use App\Classes\Dynamic;
use App\Models\{Address,System};

/**
 * This method will generate a nodelist for an upstream Host/RC/ZC/NC
 *
 * // @todo Only generate if nodes have been updated, added or removed
 */
class NodelistSegment extends Dynamic
{
	private const LOGKEY = 'DNL';

	private string $name = '';
	private Address $our_address;
	private Carbon $now;

	public function __construct(Address $ao,Collection $arg)
	{
		$this->our_address = our_address($ao->zone->domain)->first();
		$this->now = Carbon::now();

		$this->name = sprintf('z%dn%d.%d',
			$this->our_address->zone->zone_id,
			$this->our_address->host_id,
			$this->now->format('z'),
		);

		Log::debug(sprintf('%s:- Generating Nodelist for [%s] from [%s] as [%s] with arguments',self::LOGKEY,$ao->ftn,$this->our_address->ftn,$this->our_address->role_name),['args'=>$arg]);
	}

	public function __toString(): string
	{
		return $this->crc($this->generate($this->our_address));
	}

	private function crc(string $text): string
	{
		return sprintf(";A %s Nodelist for %s -- Day number %d : %s\r\n",
			$this->our_address->role_name,
			$this->now->format('l, M d, Y'),
			$this->now->format('z'),
			crc16($text)
		).$text;
	}

	private function flags(Address $ao): Collection
	{
		$result = collect();

		if (($ao->system->pollmode === TRUE) || in_array($ao->role_name,['ZC','RC','NC','HC']))
			$result->push('CM');

		if ($ao->system->address) {
			$result->push(sprintf('INA:%s',$ao->system->address));

			if (($x=$ao->system->mailers->pluck('name')->search('BINKP')) !== FALSE)
				$result->push(sprintf('IBN%s',(($y=$ao->system->mailers->get($x)->pivot->port) !== 24554) ? ':'.$y : ''));

			if (($x=$ao->system->mailers->pluck('name')->search('EMSI')) !== FALSE)
				$result->push(sprintf('ITN%s',(($y=$ao->system->mailers->get($x)->pivot->port) !== 23) ? ':'.$y : ''));
		}

		return $result;
	}

	private function entry(Address $ao): string
	{
		$format = '%s,%d,%s,%s,%s,%s,300';

		$prefix = '';
		$address = $ao->node_id;

		if ((! $ao->system->address) || $ao->isPrivate) {
			$prefix = 'Pvt';

		} elseif ($ao->isHold) {
			$prefix = 'Hold';

		} elseif ($ao->isDown) {
			$prefix = 'Down';

		} else
			switch ($ao->role_id) {
				case Address::NODE_ZC:
					$prefix = 'Zone';
					$address = $ao->zone->zone_id;
					break;

				case Address::NODE_RC:
					$prefix = 'Region';
					$address = $ao->region_id;
					break;

				case Address::NODE_NC:
					$prefix = 'Host';
					$address = $ao->host_id;
					break;

				case Address::NODE_HC:
					$prefix = 'Hub';
					break;

				case Address::NODE_NN:
					break;

				case Address::NODE_POINT:
					throw new \Exception(sprintf('We have no method to include points in the nodelist [%s]',$ao->ftn));

				default:
					throw new \Exception(sprintf('Unknown role [%d] for [%s]',$ao->role,$ao->ftn));
			}

		return sprintf($format,
			$prefix,
			$address,
			$this->format($ao->system->name),
			$this->format($ao->system->location),
			$this->format($ao->system->sysop),
			$ao->system->phone ?: '-Unpublished-',
		).(($x=$this->flags($ao)->join(',')) ? sprintf(',%s',$x) : '');
	}

	private function format(string $string): string
	{
		return str_replace(' ','_',str_replace(',','',$string));
	}

	private function generate(Address $ao): string
	{
		$result = collect();
		$so = System::createUnknownSystem();

		$result->push($this->entry($ao));

		foreach ($ao->children() as $oo) {
			// We dont include points in the ndoelist
			if ($oo->point_id)
				continue;

			// We exclude discovered systems
			if ($oo->system_id == $so->id)
				continue;

			$result->push($this->generate($oo) ?: $this->entry($oo));
		}

		return $result->join("\n");
	}

	public function getName(): string
	{
		return $this->name;
	}
}