<?php

namespace App\Models;

use Carbon\Carbon;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File as FileFacade;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Storage;
use Rennokki\QueryCache\Traits\QueryCacheable;

use App\Casts\{CollectionOrNull,CompressedString};
use App\Traits\EncodeUTF8;

class File extends Model
{
	use SoftDeletes,EncodeUTF8,QueryCacheable;

	private const LOGKEY = 'MF-';
	private bool $no_export = FALSE;

	protected $casts = [
		'kludges' => CollectionOrNull::class,
		'rogue_seenby' => CollectionOrNull::class,
		'rogue_path' => CollectionOrNull::class,
		'desc' => CompressedString::class,
		'ldesc' => CompressedString::class,
		'size' => 'int',
	];

	private const cast_utf8 = [
		'desc',
		'ldesc',
	];

	protected $dates = ['datetime'];

	public function __set($key,$value)
	{
		switch ($key) {
			case 'fullname':
			case 'replaces':
			case 'no_export':
			case 'set_path':
			case 'set_packet':
			case 'set_seenby':
				$this->{$key} = $value;
				break;

			default:
				parent::__set($key,$value);
		}
	}

	public static function boot()
	{
		parent::boot();

		static::creating(function($model) {
			Log::debug(sprintf('%s:- Storing file [%s] in [%s]',self::LOGKEY,$model->fullname,$model->full_storage_path));

			// Store file
			if (Storage::put($model->full_storage_path,Storage::disk('local')->get($model->fullname),'public')) {
				unlink(Storage::disk('local')->path($model->fullname));
			} else {
				throw new \Exception(sprintf('Unable to move file [%s] to [%s]',$model->fullname,$model->full_storage_path));
			};

			// Delete anything being replaced
			// @todo implement replace
		});

		// @todo if the file is updated with new SEEN-BY's from another route, we'll delete the pending export for systems (if there is one)
		static::created(function($model) {
			if (! $model->filearea_id) {
				Log::alert(sprintf('%s:- File has no filearea, not exporting',self::LOGKEY,$model->id));
				return;
			}

			$so = Setup::findOrFail(config('app.id'));

			// Our address
			$ftns = $so
				->system
				->match($model->fftn->zone,Address::NODE_ACTIVE|Address::NODE_PVT|Address::NODE_HOLD);

			// Add our address to the seenby;
			$model->set_seenby = $model->set_seenby->merge($ftns->pluck('id'))->unique();
			$model->set_path = $model->set_path->merge([[
				'address'=>$ftns->first(),
				'datetime'=>($x=Carbon::now())->timestamp,
				'extra'=>sprintf('%s %s (%s)',$x->toRfc7231String(),$so::PRODUCT_NAME,$so->version),
			]]);

			// Make sure all the path is in the seenby
			$model->set_seenby = $model->set_seenby->merge($model->set_path->pluck('address.id'))->unique();

			// Save the seenby
			$model->seenby()->sync($model->set_seenby);

			// Save the Path
			$ppoid = NULL;
			foreach ($model->set_path as $path) {
				$po = DB::select('INSERT INTO file_path (file_id,address_id,parent_id,datetime,extra) VALUES (?,?,?,?,?) RETURNING id',[
					$model->id,
					$path['address']->id,
					$ppoid,
					Carbon::createFromTimestamp($path['datetime']),
					$path['extra'],
				]);

				$ppoid = $po[0]->id;
			}

			// See if we need to export this message.
			$exportto = $model->filearea->addresses->pluck('id')->diff($model->set_seenby);

			if ($exportto->count()) {
				if ($model->no_export) {
					Log::debug(sprintf('%s:- NOT processing exporting of message by configuration [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(',')));
					return;
				}

				Log::debug(sprintf('%s:- Exporting file [%s] to [%s]',self::LOGKEY,$model->id,$exportto->join(',')));

				// Save the seenby for the exported systems
				$model->seenby()->syncWithPivotValues($exportto,['export_at'=>Carbon::now()],FALSE);
			}
		});
	}

	/* RELATIONS */

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

	public function fftn()
	{
		return $this->belongsTo(Address::class)
			->withTrashed();
	}

	public function seenby()
	{
		return $this->belongsToMany(Address::class,'file_seenby')
			->ftnOrder();
	}

	public function path()
	{
		return $this->belongsToMany(Address::class,'file_path')
			->withPivot(['id','parent_id','datetime','extra']);
	}

	/* ATTRIBUTES */

	public function getFullStoragePathAttribute(): string
	{
		return sprintf('%04X/%s',$this->filearea_id,$this->file);
	}

	/* METHODS */

	public function jsonSerialize(): array
	{
		return $this->encode();
	}
}