<?php

namespace App\Http\Controllers;

use Carbon\Carbon;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Gate;

use App\Classes\File;
use App\Classes\FTN\{Message,Packet};
use App\Http\Requests\SetupRequest;
use App\Models\File as FileModel;
use App\Models\{Address,Echomail,Netmail,Setup,System};

class HomeController extends Controller
{
	public function home()
	{
		return redirect(Auth::check() ? 'dashboard' : 'about');
	}

	public function file_contents(System $o,string $date)
	{
		$f = FileModel::select('files.*')
			->distinct()
			->leftJoin('file_seenby',['file_seenby.file_id'=>'files.id'])
			->where(function($query) use ($o,$date) {
				return $query
					->where('created_at',$date)
					->whereIn('fftn_id',$o->addresses->pluck('id'));
			})
			->Orwhere(function($query) use ($o,$date) {
				return $query
					->where('sent_at',$date)
					->whereIn('address_id',$o->addresses->pluck('id'));
			})
			->get();

		return view('widgets.file')
			->with('f',$f);
	}

	public function packet_contents(System $o,string $packet)
	{
		$nm = Netmail::select(['netmails.id','fftn_id','tftn_id','netmails.datetime'])
			->leftJoin('netmail_path',['netmail_path.netmail_id'=>'netmails.id'])
			->where(function($query) use ($o,$packet) {
				return $query
					->where('sent_pkt',$packet)
					->orWhere('netmail_path.recv_pkt',$packet);
			})
			->get();

		$em = Echomail::select(['echomails.id','fftn_id','echoarea_id','msgid','datetime'])
			->leftJoin('echomail_seenby',['echomail_seenby.echomail_id'=>'echomails.id'])
			->where(function($query) use ($o,$packet) {
				return $query
					->where('sent_pkt',$packet)
					->whereIn('echomail_seenby.address_id',$o->addresses->pluck('id'));
				})
			->union(
				Echomail::select(['echomails.id','fftn_id','echoarea_id','msgid','datetime'])
					->leftJoin('echomail_path',['echomail_path.echomail_id'=>'echomails.id'])
					->where(function($query) use ($o,$packet) {
						return $query
							->where('recv_pkt',$packet)
							->whereIn('echomail_path.address_id',$o->addresses->pluck('id'));
					})
			)
			->with(['echoarea'])
			->get();

		return view('widgets.packet')
			->with('nm',$nm)
			->with('em',$em);
	}

	/**
	 * Show a packet dump
	 *
	 * @param Request $request
	 * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Http\RedirectResponse
	 */
	public function pkt(Request $request)
	{
		$pkt = collect();
		$file = NULL;
		$f = NULL;

		if ($request->post()) {
			$request->validate([
				'file' => 'required|filled|min:1',
			]);

			foreach ($request->allFiles() as $key => $filegroup) {
				if ($key !== 'file')
					continue;

				foreach ($filegroup as $file) {
					try {
						$f = new File($file);

						foreach ($f as $packet)
							$pkt->push([$f->itemName()=>Packet::process($packet,$f->itemName(),$f->itemSize(),NULL,FALSE)]);

					} catch (\Exception $e) {
						return redirect()->back()->withErrors(sprintf('%s (%s:%d)',$e->getMessage(),$e->getFile(),$e->getLine()));
					}

					break;
				}
			}
		}

		return view('pkt')
			->with('file',$f)
			->with('filename',$f ? $file->getClientOriginalName() : NULL)
			->with('results',$pkt)
			->with('hexdump',$file ? hex_dump(file_get_contents($file)) : '');
	}

	/**
	 * Process searching
	 *
	 * @param Request $request
	 * @return Collection
	 */
	public function search(Request $request): Collection
	{
		$this->middleware('auth');

		$result = collect();

		list($zone_id,$host_id,$node_id,$point_id,$domain) = sscanf($request->query('term'),'%d:%d/%d.%d@%s');

		// Look for Systems
		foreach (Address::select(['systems.name',DB::raw('systems.id AS system_id'),'zones.zone_id','region_id','host_id','node_id','point_id'])
			->join('zones',['zones.id'=>'addresses.zone_id'])
			->rightjoin('systems',['systems.id'=>'addresses.system_id'])
			->when($zone_id || $host_id || $node_id,function($query) use ($zone_id,$host_id,$node_id) {
				return $query
					->when($zone_id,function($q,$zone_id) { return $q->where('zones.zone_id',$zone_id); })
					->where(function($q) use ($host_id) {
						return $q
							->when($host_id,function($q,$host_id) { return $q->where('region_id',$host_id); })
							->when($host_id,function($q,$host_id) { return $q->orWhere('host_id',$host_id); });
					})
					->when($node_id,function($q,$node_id) { return $q->where('node_id',$node_id); });
			})
			->orWhere('systems.name','ilike','%'.$request->query('term').'%')
			->orWhere('systems.sysop','ilike','%'.$request->query('term').'%')
			->OrderBy('systems.name')
			->get() as $o)
		{
			$ftn = NULL;

			if ($o->zone_id && ($o->region_id||$o->host_id) && is_numeric($o->node_id) && is_numeric($o->point_id))
				$ftn = sprintf('%d:%d/%d.%d',$o->zone_id,$o->host_id ?: $o->region_id,$o->node_id,$o->point_id);

			$result->push(['id'=>$o->system_id,'name'=>htmlspecialchars($o->name).($ftn ? ' '.$ftn : ''),'value'=>url('system/view',[$o->system_id]),'category'=>'Systems']);
		}

		// Look for Echomail
		foreach (Echomail::select(['id','fftn_id','from'])
				->where('msgid','like','%'.$request->query('term').'%')
				->orWhere('replyid','like','%'.$request->query('term').'%')
				->get() as $o)
		{
			$result->push(['id'=>$o->id,'name'=>sprintf('%s (%s)',Message::tr($o->from),$o->fftn->ftn3d),'value'=>url('echomail/view',[$o->id]),'category'=>'Echomail']);
		}

		// Look for Netmail
		if (Gate::check('admin'))
			foreach (Netmail::select(['id','fftn_id','from'])
						 ->where('msgid','like','%'.$request->query('term').'%')
						 ->orWhere('replyid','like','%'.$request->query('term').'%')
						 ->get() as $o)
			{
				$result->push(['id'=>$o->id,'name'=>sprintf('%s (%s)',$o->from,$o->fftn->ftn3d),'value'=>url('netmail/view',[$o->id]),'category'=>'Netmail']);
			}

		return $result->unique(['id'])->take(10)->values();
	}

	/**
	 * System Setup
	 *
	 * @note: Protected by Route
	 */
	public function setup(SetupRequest $request)
	{
		$o = Setup::findOrNew(config('app.id'));

		if ($request->post()) {
			if (! $o->exists)
				$o->id = config('app.id');

			$servers = collect();
			$options = collect();

			$x = collect();
			$x->put('options',collect($request->post('binkp'))->sum());
			$x->put('port',$request->post('binkp_port'));
			$x->put('bind',$request->post('binkp_bind'));
			$x->put('active',(bool)$request->post('binkp_active'));
			$servers->put('binkp',$x);

			$x = collect();
			$x->put('options',collect($request->post('emsi'))->sum());
			$x->put('port',$request->post('emsi_port'));
			$x->put('bind',$request->post('emsi_bind'));
			$x->put('active',(bool)$request->post('emsi_active'));
			$servers->put('emsi',$x);

			$x = collect();
			$x->put('options',collect($request->post('dns'))->sum());
			$x->put('port',$request->post('dns_port'));
			$x->put('bind',$request->post('dns_bind'));
			$x->put('active',(bool)$request->post('dns_active'));
			$servers->put('dns',$x);

			$options->put('options',collect($request->post('options'))->sum());
			$options->put('msgs_pkt',$request->post('msgs_pkt'));

			$o->servers = $servers;
			$o->options = $options;
			$o->system_id = $request->post('system_id');
			$o->save();
		}

		return view('setup')
			->with('o',$o);
	}

	/**
	 * Return a list of unsent items
	 *
	 * @return \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\View\Factory|\Illuminate\Contracts\View\View|\Illuminate\Foundation\Application
	 * @see \App\Jobs\AddressIdle:class
	 */
	public function status()
	{
		$date = Carbon::now()->yesterday()->endOfday();

		$r = Address::select([
				'a.id',
				'a.system_id',
				'a.zone_id',
				'addresses.region_id',
				'a.host_id',
				'a.node_id',
				'a.point_id',
				'addresses.hub_id',
				'addresses.role',
				DB::raw('sum(a.uncollected_echomail) as uncollected_echomail'),
				DB::raw('sum(a.uncollected_netmail) as uncollected_netmail'),
				DB::raw('sum(a.uncollected_files) as uncollected_files')
			])
			->from(
				Address::UncollectedEchomailTotal()
					->where('echomails.created_at','<',$this->yesterdayEOD())
					->union(Address::UncollectedNetmailTotal()
						->where('netmails.created_at','<',$this->yesterdayEOD())
					)
					->union(Address::UncollectedFilesTotal()
						->where('files.created_at','<',$this->yesterdayEOD())
					),'a')
			->where('systems.active',TRUE)
			->where('addresses.active',TRUE)
			->where('zones.active',TRUE)
			->where('domains.active',TRUE)
			->when(! ($x=Auth::user()) || (! $x->isAdmin()),function($query) { return $query->where('domains.public',TRUE); })
			->join('addresses',['addresses.id'=>'a.id'])
			->join('systems',['systems.id'=>'a.system_id'])
			->join('zones',['zones.id'=>'addresses.zone_id'])
			->join('domains',['domains.id'=>'zones.domain_id'])
			->ftnOrder()
			->groupBy('a.system_id','a.id','a.zone_id','addresses.region_id','a.host_id','a.node_id','a.point_id','addresses.hub_id','addresses.role')
			->with(['system','zone.domain']);

		return view('status')
			->with('date',$date)
			->with('uncollected',$r->get());
	}

	private function yesterdayEOD(): Carbon
	{
		return Carbon::now()->yesterday()->endOfday();
	}
}