<?php

namespace App\Models;

use AgliPanci\LaravelCase\Facades\CaseBuilder;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

use App\Casts\CompressedStringOrNull;
use App\Traits\{QueryCacheableConfig,ScopeActive};

class Domain extends Model
{
	use HasFactory,QueryCacheableConfig,ScopeActive;

	private const CACHE_TIME = 3600;
	private const STATS_MONTHS = 6;

	protected $casts = [
		'homepage' => CompressedStringOrNull::class,
	];

	/* SCOPES */

	/**
	 * Only query active records
	 */
	public function scopePublic($query)
	{
		$user = Auth::user();

		return $query
			->when(((! $user) || (! $user->isAdmin())),function($query) { return $query->where('public',TRUE)->active(); });
	}

	/* RELATIONS */

	public function echoareas()
	{
		return $this->hasMany(Echoarea::class);
	}

	public function fileareas()
	{
		return $this->hasMany(Filearea::class);
	}

	public function nodelist_filearea()
	{
		return $this->belongsTo(Filearea::class);
	}

	public function nodestatusarea()
	{
		return $this->belongsTo(Echoarea::class,'nodestatus_id');
	}

	public function zones()
	{
		return $this->hasMany(Zone::class)
			->select(['id','zone_id','domain_id','active']);
	}

	/* ATTRIBUTES */

	public function getCanAcceptAppAttribute(): bool
	{
		return ($x=our_address($this))
			&& $x->count()
			&& $this->active
			&& $this->accept_app
			&& Auth::id()
			&& $this->userHasSystemsNotInNet(Auth::user())->count();
	}

	public function getHomePageAttribute(?string $value): string
	{
		//0xFD2FB528
		return $this->castAttribute('homepage',$value) ?: 'No available information at the moment.';
	}

	/* METHODS */

	public function echoarea_stats(): Collection
	{
		$dt = Carbon::now()->startOfday();
		$case = CaseBuilder::whenRaw("datetime >= '?'",$dt->subDay()->format('Y-m-d'))->thenRaw("'day'")
			->whenRaw("datetime >= '?'",$dt->subDays(7)->format('Y-m-d'))->thenRaw("'week'")
			->whenRaw("datetime >= '?'",$dt->subMonth()->format('Y-m-d'))->thenRaw("'month'")
			->elseRaw("'all'");

		return Echoarea::select(['echoareas.id','name','description','active',DB::raw('count(echomails.id) AS count'),DB::raw('min(datetime) as first_message'),DB::raw('max(datetime) as last_message')])
			->selectRaw($case->toRaw().' AS stats')
			->join('echomails',['echomails.echoarea_id'=>'echoareas.id'],NULL,NULL,'left outer')
			->where('domain_id',$this->id)
			->groupBy('echoareas.id')
			->groupBy('echoareas.name')
			->groupBy('stats')
			->orderBy('echoareas.name')
			->orderBy('last_message','DESC')
			->get();
	}

	public function echoarea_total_daily(Collection $systems=NULL): Collection
	{
		return Cache::remember(md5(sprintf('%d-%s',$this->id,$systems?->pluck('id')->join(','))),self::CACHE_TIME,function() use ($systems) {
			return DB::query()
				->select(['echoareas.name','echoareas.show',DB::raw('COUNT(echoareas.name) AS count'),DB::raw('datetime::date AS date')])
				->from($this->getTable())
				->join('echoareas',['echoareas.domain_id'=>'domains.id'])
				->join('echomails',['echomails.echoarea_id'=>'echoareas.id'])
				->where('domains.id',$this->id)
				->where('echomails.datetime','>=',Carbon::now()->subMonths(self::STATS_MONTHS)->startOfMonth())
				->when($systems?->count(),function($query) use ($systems) { return $query->whereIn('echomails.fftn_id',$systems->pluck('addresses')->flatten()->pluck('id')); })
				->groupBy(['echoareas.name','echoareas.show','date'])
				->orderBy('echoareas.name')
				->orderBy('date')
				->get();
		});
	}

	/**
	 * Determine if this zone is managed by this host
	 *
	 * @return bool
	 * @throws \Exception
	 */
	public function isManaged(): bool
	{
		return ($x=our_address())
			&& $x->pluck('zone.domain')
				->pluck('id')
				->contains($this->id);
	}

	/**
	 * Work out which of the users systems are not in this domain
	 *
	 * @param User $o
	 * @return Collection
	 */
	public function userHasSystemsNotInNet(User $o): Collection
	{
		$o->load('systems.akas.zone');

		$result = collect();
		foreach ($o->systems->filter(function($item) { return $item->active; }) as $so) {
			if (! $so->akas->pluck('zone')->unique('domain_id')->pluck('domain_id')->contains($this->id))
				$result->push($so);
		}

		return $result;
	}
}