<?php

namespace App\Jobs;

use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;

use App\Classes\External\Supplier as ExternalSupplier;
use App\Mail\TrafficMismatch;
use App\Models\{Rtm,Supplier};
use App\Models\Service\Broadband as ServiceBroadband;
use App\Models\Usage\Broadband as UsageBroadband;

/**
 * Class BroadbandTraffic
 * Read and update the traffic for a Broadband supplier
 *
 * @package App\Jobs
 */
final class BroadbandTraffic implements ShouldQueue
{
	use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

	private const LOGKEY = 'JBT';

	private const class_prefix = 'App\Classes\External\Supplier\\';

	private const traffic = 'broadband';

	public function __construct(private string $supplier) {}

	/**
	 * Execute the job.
	 *
	 * @return void
	 * @throws \Exception
	 */
	public function handle()
	{
		$so = Supplier::active()
			->where('name','ilike',$this->supplier)
			->sole();

		// Wholesaler email
		$ro = Rtm::where('parent_id',NULL)->sole();

		Log::info(sprintf('%s:Importing Broadband Traffic from [%s]',self::LOGKEY,$so->name));

		if ((! $connection=$so->detail->connections->get('broadband')) || (count(array_intersect(array_keys($connection),ExternalSupplier::traffic_connection_keys)) !== 3))
			throw new \Exception('No or missing connection details for:'.self::traffic);

		// Count of updated records
		$u = 0;

		// Load our class for this supplier
		$class = self::class_prefix.$so->name;
		if (class_exists($class)) {
			$o = new $class($so);

		} else {
			Log::error(sprintf('%s: Class doesnt exist: %s',self::LOGKEY,$class));
			exit(1);
		}

		$last_update = Carbon::create(Arr::get($connection,'last'))->addDay();
		Arr::set($connection,'last',$last_update->format('Y-m-d'));

		// Repeat pull traffic data until yesterday
		while ($last_update < Carbon::now()->subDay()) {
			Log::notice(sprintf('%s:Next update is [%s]',self::LOGKEY,$last_update->format('Y-m-d')));

			// Delete traffic, since we'll refresh it.
			UsageBroadband::where('supplier_id',$so->id)
				->where('date',$last_update->format('Y-m-d'))
				->delete();

			$c = 0;
			foreach ($o->fetch($connection,self::traffic) as $line) {
				// The first row is our header
				if (! $c++) {
					$fields = $o->getColumns(preg_replace('/,\s+/',',',$line),collect($o->header()));
					continue;
				}

				if (! $fields->count())
					abort(500,'? No fields in data export');

				$row = str_getcsv(trim($line));

				try {
					$date = Carbon::createFromFormat('Y-m-d',$row[$o->getColumnKey('Date')]);

					// Find the right service dependent on the dates we supplied the service
					$oo = ServiceBroadband::where('service_username',$row[$o->getColumnKey('Login')])
						->select('service_broadband.*')
						->join('services',['service_broadband.service_id'=>'services.id'])
						->where('services.start_at','<=',$date)
						->where(fn($query)=>
							$query->whereNULL('services.stop_at')
								->orWhere('services.stop_at','>=',$date)
						)
						->single();

					$to = new UsageBroadband;
					$to->date = $last_update;
					$to->supplier_id = $so->id;
					$to->up_peak = $row[$o->getColumnKey('Peak upload')];
					$to->up_offpeak = $row[$o->getColumnKey('Off peak upload')];
					$to->down_peak = $row[$o->getColumnKey('Peak download')];
					$to->down_offpeak = $row[$o->getColumnKey('Off peak download')];
					// $to->peer
					// $to->internal
					$to->time = '24:00'; // @todo

					// If we have no records
					if (! $oo) {
						Log::error(sprintf('%s:None or too many services return for [%s]',self::LOGKEY,$row[$o->getColumnKey('Login')]),['date'=>$date]);

						$to->service = $row[$o->getColumnKey('Login')];
						$to->site_id = 1; // @todo This needs to be worked out a better way

					} else {
						$to->site_id = $oo->site_id;
						$to->service_item_id = $oo->id;
					}

					if ($to->save())
						$u++;

				} catch (\Exception $e) {
					Log::error(sprintf('%s:Exception occurred when storing traffic record for [%s].',self::LOGKEY,$row[$o->getColumnKey('Login')]),['row'=>$row,'line'=>$line]);
					throw new \Exception('Error while storing traffic data: '.$e->getMessage());
				}
			}
			Log::info(sprintf('%s: Records Imported [%d] for [%s]',self::LOGKEY,$u,$last_update->format('Y-m-d')));

			// Save our current progress.
			$so->detail->connections = $so->detail->connections->put(self::traffic,array_merge($connection,['last'=>$last_update->format('Y-m-d')]));
			$so->detail->save();

			// Update our details for the next iteration.
			$last_update = $last_update->addDay();
			Arr::set($connection,'last',$last_update->format('Y-m-d'));

			if ($u) {
				if ($so->trafficMismatch($date)->count())
					Mail::to($ro->owner->email)
						->send(new TrafficMismatch($so,$date));
			}
		}
	}
}