<?php

namespace App\Jobs;

use Carbon\Carbon;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Foundation\Bus\Dispatchable;
use Illuminate\Queue\InteractsWithQueue;
use Illuminate\Queue\SerializesModels;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Mail;

use App\Mail\TrafficMismatch;
use App\Models\Service\Adsl;
use App\Models\Service\AdslTraffic;
use App\Models\AdslSupplier;

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

	private const LOGKEY = 'JBT';

	protected Model $o;                     // The supplier we are updating from
	private const class_prefix = 'App\Classes\External\Supplier\\';

	public function __construct(AdslSupplier $o)
	{
		$this->o = $o;
	}

	/**
	 * Execute the job.
	 *
	 * @return void
	 * @throws \Exception
	 * @todo The column stats_lastupdate is actually the "next" date that stats should be retrieved. Rename it.
	 */
	public function handle()
	{
		Log::info(sprintf('%s:Importing Broadband Traffic from [%s]',self::LOGKEY,$this->o->name));

		$u = 0;

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

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

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

			// Delete traffic, since we'll refresh it.
			AdslTraffic::where('supplier_id',$this->o->id)
				->where('date',$this->o->stats_lastupdate)
				->delete();

			$c = 0;
			foreach ($o->fetch() 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 exportupda');

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

				try {
					// @todo Put the date format in the DB.
					$date = Carbon::createFromFormat('Y-m-d',$row[$o->getColumnKey('Date')]);

					// Find the right service dependant on the dates we supplied the service
					$oo = Adsl::where('service_username',$row[$o->getColumnKey('Login')])
						->select(DB::raw('ab_service__adsl.*'))
						->join('ab_service','ab_service.id','=','service_id')
						->where('ab_service.date_start','<=',$date->format('U'))
						->where(function($query) use ($date) {
							$query->whereNULL('ab_service.date_end')
								->orWhere('ab_service.date_end','<=',$date->format('U'));
						})
						->get();

					$to = new AdslTraffic;
					$to->site_id = 1; // @todo TO ADDRESS
					$to->date = $this->o->stats_lastupdate;
					$to->supplier_id = $this->o->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->count() != 1) {
						Log::error(sprintf('%s:Too many services return for [%s]',self::LOGKEY,$row[$o->getColumnKey('Login')]),['date'=>$date,'count'=>$oo->count()]);

						$to->service = $row[$o->getColumnKey('Login')];
						$to->save();

					} else {
						$oo->first()->traffic()->save($to);
					}

					$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 date');
				}
			}

			Log::info(sprintf('%s: Records Imported [%d] for [%s]',self::LOGKEY,$u,$this->o->stats_lastupdate->format('Y-m-d')));

			if ($u) {
				$this->o->stats_lastupdate = $this->o->stats_lastupdate->addDay();
				$this->o->save();

				if ($this->o->trafficMismatch($date)->count())
					Mail::to('deon@graytech.net.au')        // @todo To change
						->send(new TrafficMismatch($this->o,$date));
			}
		}
	}
}