<?php

namespace onespace\tools\activeApi\models\onedevice;

use onespace\tools\activeApi\components\clients\OneDeviceClient;
use onespace\tools\activeApi\models\oneaccess\AccessGateway;
use thamtech\uuid\helpers\UuidHelper;
use Yii;
use yii\helpers\ArrayHelper;
use yii\web\NotFoundHttpException;

/**
 * @property integer $id
 * @property string $serial_number
 * @property integer $logical_address
 * @property string $site_id
 * @property string $first_seen
 * @property string $last_seen
 * @property string $last_discovery
 * @property string $last_info_update
 * @property string $offline_timestamp
 * @property integer $online
 * @property integer $device_mode
 * @property integer $last_on_prov
 * @property integer $routed_count_max
 * @property integer $discovery_refresh_seconds
 * @property integer $default_ttl
 * @property integer $logging_level
 * @property integer $log_revert_timeout
 * @property string $boot_firmware
 * @property string $app_firmware
 * @property string $hardware_platform
 * @property integer $hardware_version
 * @property integer $internal_filesystem_size
 * @property integer $external_filesystem_size
 * @property string $mcu_uuid
 * @property integer $mcu_soc_revision
 * @property integer $mcu_part_id
 * @property array $interface_info
 * @property string $active_dfu
 * @property integer $dfu_timestamp
 * @property string $site_unlink_id
 * @property integer $site_unlink_timestamp
 * @property integer $marked_for_format
 * @property integer $mode_change_in_progress
 * @property array $site_sync_details
 * @property string $serial_hex
 * @property string $notes
 *
 * @property \app\models\DfuDetails $activeDfu
 * @property \app\models\GatewayEventRule[] $gatewayEventRules
 * @property \app\models\GatewayInterface[] $gatewayInterfaces
 * @property \app\models\GatewayMetric[] $gatewayMetrics
 * @property \app\models\Routing[] $routings
 * @property \app\models\Routing[] $routings0
 * @property \app\models\Rpc[] $rpcs
 */
class Gateway extends \onespace\tools\activeApi\models\BaseActiveApiModel {
    public const int SYNC_TYPE_QUICK = 0;
    public const int SYNC_TYPE_SAFE = 1;
    public const int SYNC_TYPE_BETTER = 2;

    public $id;
    public $serial_number;
    public $logical_address;
    public $site_id;
    public $first_seen;
    public $last_seen;
    public $last_discovery;
    public $last_info_update;
    public $offline_timestamp;
    public $online;
    /** @see vendor/onespace/protobuf-library/protobuf/php/OS/PB/DeviceConfig/DeviceMode.php */
    public $device_mode;
    public $last_on_prov;
    public $routed_count_max;
    public $discovery_refresh_seconds;
    public $default_ttl;
    public $logging_level;
    public $log_revert_timeout;
    public $boot_firmware;
    public $app_firmware;
    public $hardware_platform;
    public $hardware_version;
    public $internal_filesystem_size;
    public $external_filesystem_size;
    public $mcu_uuid;
    public $mcu_soc_revision;
    public $mcu_part_id;
    public $interface_info;
    public $active_dfu;
    public $dfu_timestamp;
    public $site_unlink_id;
    public $site_unlink_timestamp;
    public $marked_for_format;
    public $mode_change_in_progress;
    public $site_sync_details;
    public $serial_hex;
    public $notes;

    // public $gatewayEventRules;
    // public $gatewayInterfaces;
    // public $gatewayMetrics;
    // public $routings;
    // public $routings0;
    // public $rpcs;

    /**
     * {@inheritdoc}
     */
    public function rules() {
        $parentRules = parent::rules();
        return ArrayHelper::merge($parentRules, [
            [['first_seen', 'last_seen'], 'default', 'value' => date('Y-m-d H:i:s')],
            [['mode_change_in_progress'], 'default', 'value' => 0],
            [[
                'logical_address',
                'site_id',
                'last_discovery',
                'last_info_update',
                'offline_timestamp',
                'last_on_prov',
                'routed_count_max',
                'discovery_refresh_seconds',
                'default_ttl',
                'logging_level',
                'log_revert_timeout',
                'boot_firmware',
                'app_firmware',
                'hardware_platform',
                'hardware_version',
                'internal_filesystem_size',
                'external_filesystem_size',
                'mcu_uuid',
                'mcu_soc_revision',
                'mcu_part_id',
                'interface_info',
                'active_dfu',
                'dfu_timestamp',
                'site_unlink_id',
                'site_unlink_timestamp',
                'site_sync_details',
                'serial_hex',
                'notes',
            ], 'default', 'value' => null],
            [['device_mode'], 'default', 'value' => 0],
            [['serial_number', 'first_seen', 'last_seen'], 'required'],
            [[
                'id',
                'logical_address',
                'online',
                'device_mode',
                'last_on_prov',
                'routed_count_max',
                'discovery_refresh_seconds',
                'default_ttl',
                'logging_level',
                'log_revert_timeout',
                'hardware_version',
                'internal_filesystem_size',
                'external_filesystem_size',
                'mcu_soc_revision',
                'mcu_part_id',
                'dfu_timestamp',
                'site_unlink_timestamp',
                'marked_for_format',
                'mode_change_in_progress',
            ], 'integer'],
            [['first_seen', 'last_seen', 'last_discovery', 'last_info_update', 'offline_timestamp', 'interface_info', 'site_sync_details'], 'safe'],
            [['notes'], 'string'],
            [['serial_number', 'site_id', 'boot_firmware', 'app_firmware', 'hardware_platform'], 'string', 'max' => 45],
            [['mcu_uuid', 'active_dfu', 'site_unlink_id'], 'string', 'max' => 30],
            [['serial_hex'], 'string', 'max' => 32],
            [['serial_number'], 'unique'],
            [['logical_address'], 'unique'],
            [['logical_address'], 'integer', 'min' => 2],
        ]);
    }

    /**
     * {@inheritdoc}
     */
    public function attributeLabels() {
        return [
            'site_id' => 'Site',
        ];
    }

    /**
     * {@inheritdoc}
     */
    public function client(): OneDeviceClient {
        return new OneDeviceClient();
    }

    /**
     * {@inheritdoc}
     */
    public function primaryKey(): string {
        return 'id';
    }

    /**
     * {@inheritdoc}
     */
    public function baseEndpoints(): array {
        return [
            'v1' => 'v2/gateway', // This is done on purpose
            'v2' => 'v1/gateway',
        ];
    }

    public static function findModelById(int $id): static {
        $model = self::findOne(['id' => $id]);
        if ($model === null) {
            throw new NotFoundHttpException('Gateway not found');
        }

        return $model;
    }

    public function linkViaLogicalAddress(string $logicalAddress, string $site): bool {
        $success = true;
        $existingModel = self::findOne(['logical_address' => $logicalAddress]);
        if ($existingModel !== null) {
            /** @var Gateway $existingModel */
            $success &= $existingModel->unlinkLogicalAddress();
            if (!$success) {
                Yii::warning("Failed at removing existing model", __METHOD__);
            }
        }

        $this->logical_address = $logicalAddress;

        // return true;

        $success &= $this->save();
        if (!$success) {
            Yii::warning("Failed at saving current model", __METHOD__);
        }

        if ($success) {
            /**
             * This finds an existing OneAccess Gateway with the logical address
             *
             * 1. If this entry exists, simply update the link, associating all existing
             *    methods, rights, users etc with the new hardware.
             * 2. If this entry doesn't exist, create one, so new methods rights and users etc.
             *    can be associated with the hardware.
             */
            $OAmodel = AccessGateway::findOne(['logical_address' => $logicalAddress]);
            if ($OAmodel === null) {
                /** @var \common\models\api\oneaccess\AccessGateway $OAmodel */
                $OAmodel = new AccessGateway();
                $OAmodel->guid = UuidHelper::uuid();
                $OAmodel->logical_address = $logicalAddress;
                $OAmodel->site_id = $site;
                $OAmodel->name = "New Access Gateway";
                $OAmodel->physical_perimeter = 0;
                $OAmodel->first_seen = $this->first_seen;
                $OAmodel->online = $this->online;
            }
            /**
             * Save either way to update any timestamp fields
             */
            $OAmodel->last_seen = $this->last_seen;
            $success &= $OAmodel->save();
            if (!$success) {
                Yii::warning("Failed at saving OneAccess Model", __METHOD__);
            }
        }

        return $success;
    }

    public function unlinkLogicalAddress(): bool {
        $this->logical_address = null;
        return $this->save();
    }

    public function updatePerimeter(int $perimeter): bool {
        $OAmodel = AccessGateway::findOne(['logical_address' => $this->logical_address]);
        $OAmodel->physical_perimeter = $perimeter;
        return $OAmodel->save();
    }

    public function updateGatewayName(string $name): bool {
        $OAmodel = AccessGateway::findOne(['logical_address' => $this->logical_address]);
        $OAmodel->name = $name;
        return $OAmodel->save();
    }

    public function linkToSite(string $siteId): void {
        $this->site_id = $siteId;
    }
}
