<?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\Models\Casts\CompressedStringOrNull;
use App\Traits\{QueryCacheableConfig,ScopeActive};

/**
 * This represents an FTN Domain.
 *
 * We are either a member of the domain (because we have an AKA in it) or NOT.
 *
 * The assumption is, if we are a member of the domain, we receive/send mail to an uplink and possibly downlinks
 */
class Domain extends Model
{
	use HasFactory,QueryCacheableConfig,ScopeActive;

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

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

	/* SCOPES */

	/**
	 * A domain is public (visible), if the user is an admin or, the domain is marked public)
	 */
	public function scopePublic($query)
	{
		$user = Auth::user();

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

	/* RELATIONS */

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

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

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

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

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

	/* ATTRIBUTES */

	/**
	 * We can accept applications if we have an address in the domain
	 *
	 * @return bool
	 * @throws \Exception
	 */
	public function getCanAcceptAppAttribute(): bool
	{
		return $this->isManaged()
			&& $this->accept_app
			&& Auth::id();
	}

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

	/* METHODS */

	/**
	 * Show count of messages by day/week/month/all stats for each echoarea in this domain
	 *
	 * @return Collection
	 */
	public function echoarea_stats(): Collection
	{
		return Cache::remember(md5(sprintf('%d-%s',$this->id,__METHOD__)),self::CACHE_TIME,function() {
			$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();
		});
	}

	/**
	 * Show daily total of messages by echoarea in this domain
	 *
	 * @param Collection|NULL $systems
	 * @return Collection
	 */
	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(),fn($query)=>$query->whereIn('echomails.fftn_id',$systems->pluck('addresses')->flatten()->pluck('id')))
				->groupBy(['echoareas.name','echoareas.show','date'])
				->orderBy('echoareas.name')
				->orderBy('date')
				->get();
		});
	}

	/**
	 * Show count of files by week/month/all status for each filearea in this domain
	 */
	public function filearea_stats()
	{
		return Cache::remember(md5(sprintf('%d-%s',$this->id,__METHOD__)),self::CACHE_TIME,function() {
			$dt = Carbon::now()->startOfday();
			$case = CaseBuilder::whenRaw("datetime >= '?'",$dt->subDays(7)->format('Y-m-d'))->thenRaw("'week'")
				->whenRaw("datetime >= '?'",$dt->subMonth()->format('Y-m-d'))->thenRaw("'month'")
				->elseRaw("'all'");

			return Filearea::select(['fileareas.id','fileareas.name','description','active',DB::raw('count(files.id) AS count'),DB::raw('min(datetime) as first_file'),DB::raw('max(datetime) as last_file')])
				->selectRaw($case->toRaw().' AS stats')
				->join('files',['files.filearea_id'=>'fileareas.id'],NULL,NULL,'left outer')
				->where('domain_id',$this->id)
				->groupBy('fileareas.id')
				->groupBy('fileareas.name')
				->groupBy('stats')
				->orderBy('fileareas.name')
				->orderBy('last_file','DESC')
				->get();
		});
	}

	/**
	 * Determine if this zone is managed by this host
	 *
	 * @return bool
	 * @throws \Exception
	 */
	public function isManaged(): bool
	{
		return our_address($this)->count() > 0;
	}
}