<?php

namespace App\Jobs;

use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\Log;

use App\Classes\FTN\{Message,Process};
use App\Models\{Echoarea,Echomail,Netmail,Setup};

class MessageProcess implements ShouldQueue
{
	private const LOGKEY = 'JMP';

	use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

	private Message $msg;
	private bool $skipbot;

	public function __construct(Message $msg,bool $skipbot=FALSE)
	{
		// Some checks
		$this->msg = $msg;
		$this->skipbot = $skipbot;
	}

	/**
	 * When calling MessageProcess - we assume that the packet is from a valid source
	 */
	public function handle()
	{
		// Load our details
		$ftns = Setup::findOrFail(config('app.id'))->system->addresses;

		// If we are a netmail
		if ($this->msg->isNetmail()) {
			// @todo Enable checks to reject old messages
			// @todo Enable checks to reject duplicate
			// @todo Enable checks to see if this is a file request or file send

			// Determine if the message is to this system, or in transit
			if ($ftns->search(function($item) { return $this->msg->tftn == $item->ftn; }) !== FALSE) {
				// @todo Check if it is a duplicate message
				// @todo Check if the message is from a system we know about

				$processed = FALSE;

				// If the message is to a bot, we'll process it
				if (! $this->skipbot)
					foreach (config('process.robots') as $class) {
						if ($processed = $class::handle($this->msg)) {
							break;
						}
					}

				// We'll ignore messages from *fix users
				if (in_array(strtolower($this->msg->user_from),['filefix','areafix'])) {
					Log::info(sprintf('Ignoring Netmail to the Hub from (%s) [%s] - its from a bot.',$this->msg->user_from,$this->msg->fftn));

					$o = $this->create_netmail($this->msg);
					$o->local = TRUE;

					$o->save();

					$processed = TRUE;
				}

				// If not processed, no users here!
				if (! $processed) {
					$reject = [
						'ÚÄ¿ÚÄ¿  ÂÚÄ¿ÚÄ¿Ú¿',
						'³  ³ÄÙ  ³³ÄÙ³   ³ ',
						'Á  ÀÄÙÀÄÙÀÄÙÀÄÙ Á '
					];

					Log::info(sprintf('Netmail to the Hub from (%s) [%s] but no users here.',$this->msg->user_from,$this->msg->fftn));

					$reply = "Your Netmail was not processed by the hub and cannot be delivered to anybody (as there are no users here).\r";

					$reply .= "\r";
					$reply .= "\r";
					$reply .= "This is your original message:\r";
					$reply .= "------------------------------ BEING MESSAGE ------------------------------\r";
					$reply .= sprintf("TO: %s\r",$this->msg->user_to);
					$reply .= sprintf("SUBJECT: %s\r",$this->msg->subject);
					$reply .= $this->msg->message;
					$reply .= "------------------------------ CONTROL LINES ------------------------------\r";
					$reply .= sprintf("DATE: %s\r",$this->msg->date->format('Y-m-d H:i:s'));
					$reply .= sprintf("MSGID: %s\r",$this->msg->msgid);

					foreach ($this->msg->kludge as $k=>$v)
						$reply .= sprintf("@%s: %s\r",strtoupper($k),$v);
					foreach ($this->msg->via as $via)
						$reply .= sprintf("VIA: %s\r",$via);

					$reply .= "------------------------------  END MESSAGE  ------------------------------\r";

					$o = new Netmail;
					$o->to = $this->msg->user_from;
					$o->from = Setup::PRODUCT_NAME;
					$o->subject = 'Message Undeliverable - '.$this->msg->msgid;
					$o->datetime = $this->msg->date;
					$o->tzoffset = $this->msg->date->utcOffset();

					$o->cost = 0;
					$o->flags = Message::FLAG_LOCAL;

					$o->fftn_id = ($x=$this->msg->tftn_o) ? $x->id : NULL;
					$o->tftn_id = ($x=$this->msg->fftn_o) ? $x->id : NULL;
					$o->replyid = $this->msg->msgid;
					$o->msg = Process::format_msg($reply,$reject);

					$o->tagline = 'Do you think it was fate which brought us together? Nah, bad luck :(';
					$o->tearline = sprintf('%s (%04X)',Setup::PRODUCT_NAME,Setup::PRODUCT_ID);
					$o->save();
				}

			// If in transit, store for collection
			} else {
				Log::info(sprintf('%s:Netmail [%s] in transit to (%s) [%s] from (%s) [%s].',
					self::LOGKEY,
					$this->msg->msgid,
					$this->msg->user_to,$this->msg->tftn,
					$this->msg->user_from,$this->msg->fftn,
				));

				// @todo Check if the message is to a system we know about
				// @todo In transit loop checking
				// @todo In transit TRACE response

				$o = $this->create_netmail($this->msg);
				$o->save();
			}

		// Else we are echomail
		} else {
			Log::debug(sprintf('%s: - Looking for echomail area [%s] for mail from [%s]',self::LOGKEY,$this->msg->echoarea,$this->msg->fboss));

			if (! $this->msg->fboss_o) {
				Log::error(sprintf('%s:! Cannot process message for echomail area [%s] for mail from [%s] with msgid [%s] - no boss object?',self::LOGKEY,$this->msg->echoarea,$this->msg->fboss,$this->msg->msgid));
				return;
			}

			$ea = Echoarea::where('name',strtoupper($this->msg->echoarea))
				->where('domain_id',$this->msg->fboss_o->zone->domain_id)
				->single();

			if (! $ea) {
				Log::alert(sprintf('%s:! Echoarea [%s] doesnt exist for zone [%d]',self::LOGKEY,$this->msg->echoarea,$this->msg->fboss_o->zone->zone_id));
				return;
			}

			Log::debug(sprintf('%s: - Processing echomail [%s] in [%s].',self::LOGKEY,$this->msg->msgid,$this->msg->echoarea));

			// Check for duplicate messages
			// FTS-0009.001
			if ($this->msg->msgid) {
				$o = Echomail::where('msgid',$this->msg->msgid)
					->where('fftn_id',($x=$this->msg->fboss_o) ? $x->id : NULL)
					->where('datetime','>',Carbon::now()->subYears(3))
					->single();

				Log::debug(sprintf('%s: - Checking for duplicate from host id [%d].',self::LOGKEY,($x=$this->msg->fboss_o) ? $x->id : NULL));

				if ($o) {
					Log::alert(sprintf('%s:! Duplicate echomail [%s] in [%s] from (%s) [%s] to (%s) - updating seenby.',
						self::LOGKEY,
						$this->msg->msgid,
						$this->msg->echoarea,
						$this->msg->user_from,$this->msg->fftn,
						$this->msg->user_to,
					));

					if (! $o->msg_crc)
						$o->msg_crc = md5($this->msg->message);

					$o->save();

					// If the path is empty, then its probably because of the previous bug, we'll replace it.
					// @todo This duplicate message may have gone via a different path, be nice to record it.
					//$o->path()->sync($o->path->pluck('id')->merge($this->msg->pathaddress)->toArray());
					// @todo if we have an export for any of the seenby addresses, remove it
					$o->seenby()->sync($o->seenby->pluck('id')->merge($this->msg->seenaddress)->filter()->toArray());

					return;
				}
			}

			// Find another message with the same msg_crc
			if ($this->msg->message) {
				$o = Echomail::where('msg_crc',$xx=md5($this->msg->message))
					->where('fftn_id',($x=$this->msg->fboss_o) ? $x->id : NULL)
					->where('datetime','>',Carbon::now()->subWeek())
					->get();

				if ($o->count())
					Log::alert(sprintf('%s:! Duplicate message CRC [%s] in [%s].',
						self::LOGKEY,
						$xx,
						$o->pluck('id')->join('|')
					));
			}

			// @todo Can the sender create it if it doesnt exist?
			// @todo Can the sender send messages to this area?
			// - Create it, or
			// - Else record in bad area

			// We know about this area, store it
			$o = new Echomail;
			$o->init();

			$o->to = $this->msg->user_to;
			$o->from = $this->msg->user_from;
			$o->subject = $this->msg->subject;
			$o->datetime = $this->msg->date;
			$o->tzoffset = $this->msg->date->utcOffset();

			$o->fftn_id = ($x=$this->msg->fboss_o) ? $x->id : NULL;     // @todo This should be the node that originated the message - but since that node is not in the DB it would be null
			$o->echoarea_id = $ea->id;
			$o->msgid = $this->msg->msgid;
			$o->replyid = $this->msg->replyid;

			$o->msg = $this->msg->message_src."\r";
			$o->msg_src = $this->msg->message_src;
			$o->msg_crc = md5($this->msg->message);
			$o->rogue_seenby = $this->msg->rogue_seenby;
			$o->rogue_path = $this->msg->rogue_path;
			$o->set_path = $this->msg->pathaddress;
			$o->set_seenby = $this->msg->seenaddress;

			$o->save();

			Log::info(sprintf('%s: - Echomail [%s] in [%s] from (%s) [%s] to (%s) - [%s].',
				self::LOGKEY,
				$this->msg->msgid,
				$this->msg->echoarea,
				$this->msg->user_from,$this->msg->fftn,
				$this->msg->user_to,
				$o->id,
			));

			// If the message is to a bot, but not rescanned, or purposely skipbot set, we'll process it
			if ((! $this->skipbot) && (! $this->msg->rescanned->count()))
				foreach (config('process.echomail') as $class) {
					if ($class::handle($this->msg)) {
						break;
					}
				}
		}
	}

	private function create_netmail(Message $msg): Netmail
	{
		$o = new Netmail;
		$o->to = $msg->user_to;
		$o->from = $msg->user_from;

		$o->fftn_id = ($x=$msg->fftn_o) ? $x->id : NULL;
		$o->tftn_id = ($x=$msg->tftn_o) ? $x->id : NULL;

		$o->datetime = $msg->date;
		$o->tzoffset = $msg->date->utcOffset();

		$o->flags = $msg->flags;
		$o->cost = $msg->cost;
		$o->msgid = $msg->msgid;

		$o->subject = $msg->subject;
		$o->msg = $msg->message;

		foreach ($msg->via as $v)
			$o->msg .= sprintf("\01Via %s\r",$v);

		$o->msg_src = $msg->message_src;
		$o->msg_crc = md5($msg->message);	// @todo DB schema needs to handle this

		return $o;
	}
}