<?php

namespace App\Http\Requests;

use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;

use App\Models\Address;
use App\Rules\{FidoInteger,TwoByteInteger};

/**
 * Validation to add an address to a system
 * o = System::class
 *
 * @note This validation only expects to be used with POST
 * Variables:
 * + "action" => "node"		// type of address being created
 * + "zone_id" => "2"
 * + "region_id" => "21"
 * + "host_id" => "3"
 * + "hub_id" => null
 * + "node_id" => "9999"
 * + "point_id" => "0"
 * + "region_id_new" => null	// creating a new region id
 * + "host_id_new" => null		// creating a new host id
 * + "node_id_new" => null		// creating a new node id
 * + "hub" => "0"
 * + "security" => "9"
 *
 * Rules:
 * - ZC is z:0/0.0 - region,node,point must be zero
 *   ZC is identified when region_id,host_id,node_id and point_id === 0
 * - RC is z:r/0.0 - region is non-zero, host_id = region_id, node,point must be zero.
 *   RC is identified when region_id === host_id and, node_id and point_id === 0
 * - NC is z:h/0.0 (r=r, r!=h, h!=z) [parent where z:r/0 and h=r, n=0]
 *   NC is identified when region_id !== host_id and, node_id and point_id === 0,
 * - HC is z:h/n.0 (r=r) [parent pointed by hub_id AND validate by z:h/0 is the hub_id]
 *   HC is a normal node with, but has children pointing to it with hub_id
 * - NN is z:h/n.0 when point_id === 0
 *   A normal node where node_id !== 0, it may or may not have a hub_id
 * - PT is z:h/n.p where point_id !== 0
 *   PT is identified when point_id !== 0;
 */
class AddressAdd extends FormRequest
{
	/**
	 * Determine if the user is authorized to make this request.
	 */
	public function authorize(): bool
	{
		session()->flash('accordion','address');

		return request()->isMethod('post')
			&& Gate::allows('admin',$this->route('o'));
	}

	/**
	 * Get the validation rules that apply to the request.
	 *
	 * @return array<string, \Illuminate\Contracts\Validation\ValidationRule|array<mixed>|string>
	 */
	public function rules(Request $request): array
	{
		$rules = [];

		switch ($request->action) {
			case 'region':
				$rules = [
					'region_id_new' => [
						'required',
						new TwoByteInteger,
						// Check that the region doesnt already exist
						function ($attribute,$value,$fail) use ($request) {
							$o = Address::where(fn($query)=>
								$query
									->where('region_id',$value)
									// Check that a host doesnt already exist
									->where('host_id',$value)
								)
								->where('zone_id',$request->zone_id)
								->where('node_id',0)
								->where('point_id',0)
								->first();

							if ($o && $o->count())
								$fail(sprintf('%s already exists [<a href="%s">here</a>]',$o->ftn,url('system/addedit',$o->system_id)));
						},
					],
				];

				break;

			case 'host':
				$rules = [
					'region_id' => [
						'required',
						new FidoInteger,		// @todo the RC should exist, ie: z:r/0.0 (h=0)
					],
					'host_id_new' => [
						'required',
						new TwoByteInteger,
						// Make sure that the host isnt already defined
						function ($attribute,$value,$fail) use ($request) {
							// Check that the region doesnt already exist
							$o = Address::where(fn($query)=>
								$query
									->where('region_id',$value)
									// Check that a host doesnt already exist
									->orWhere('host_id',$value)
								)
								->where('zone_id',$request->zone_id)
								->where('node_id',0)
								->where('point_id',0)
								->first();

							if ($o && $o->count())
								$fail(sprintf('%s already exists [<a href="%s">here</a>]',$o->ftn,url('system/addedit',$o->system_id)));
						},
					],
				];

				break;

			case 'update':
			case 'node':
				$rules = [
					'region_id' => [
						'required',
						new FidoInteger // @todo the RC should exist, ie: z:r/0.0 (h=0)
					],
					'host_id' => [
						'required',new FidoInteger // @todo the NC should exist, ie: z:r/0.0 (h=0)
					],
					'node_id' => [
						'required',
						new TwoByteInteger,
						function ($attribute,$value,$fail) use ($request) {
							if ($request->point_id === 0) {
								// Check that the host doesnt already exist
								$o = Address::where(function($query) use ($request,$value) {
									return $query
										->where('zone_id',$request->zone_id)
										->where('host_id',$request->host_id)
										->where('node_id',$value)
										->where('point_id',0)
										->where('id','<>',$request->submit);
									})
									->get();

								if ($o && $o->count())
									$fail(sprintf('%s already exists [<a href="%s">here</a>]',$o->ftn,url('system/addedit',$o->system_id)));
							}
						},
					],
					'point_id' => [
						'required',
						new FidoInteger,
						function ($attribute,$value,$fail) use ($request) {
							// Check that the host doesnt already exist
							$o = Address::where(function($query) use ($request,$value) {
								return $query
									->where('zone_id',$request->zone_id)
									->where('host_id',$request->host_id)
									->where('node_id',$request->node_id)
									->where('point_id',$value)
									->where('id','<>',$request->submit);
								})
								->first();

							if ($o && $o->count())
								$fail(sprintf('%s already exists [<a href="%s">here</a>]',$o->ftn,url('system/addedit',$o->system_id)));
						}
					],
					//'hub' => 'required|boolean',
					'hub_id' => 'nullable|exists:addresses,id',
					'submit' => 'nullable|exists:addresses,id',
				];

				break;
		}

		return array_merge($rules,
			[
				'action' => [
					'required',
					'in:region,host,node,update',
				],
				'zone_id' => [
					'required',
					'exists:zones,id',
				],
				'security' => [
					'nullable',
					'numeric',
					'min:0',
					'max:7',
				]
			]);
	}
}