<?php

namespace idoit\Module\SyneticsJdisc\Model;

use idoit\Module\SyneticsJdisc\Graphql\Connector;
use idoit\Module\SyneticsJdisc\Graphql\Mutation\Discovery\DiscoverDevice;
use idoit\Module\SyneticsJdisc\Graphql\Mutation\StartJobs;
use idoit\Module\SyneticsJdisc\Graphql\Mutation\StopJobs;
use idoit\Module\SyneticsJdisc\Graphql\Query\GetDiscoveryLog;
use idoit\Module\SyneticsJdisc\Graphql\Query\IdentifyDevice;
use idoit\Module\SyneticsJdisc\Graphql\Query\Job\GetJobs;
use idoit\Module\SyneticsJdisc\Graphql\Type\DeviceInputType;
use idoit\Module\SyneticsJdisc\Graphql\Type\StringType;

class DiscoveryDao extends AbstractJDiscDao
{
    /**
     * @return Connector
     */
    private function getConnector(): Connector
    {
        $connector = Connector::instance($this->serverId);
        $connector->connect();
        return $connector;
    }

    /**
     * @return array
     */
    private function getAllJobIds(): array
    {
        $jobs = $this->getConnector()->query(new GetJobs());
        return array_column($jobs, 'id');
    }

    /**
     * @return array
     */
    public function list(): array
    {
        $jobs = $this->getConnector()->query(new GetJobs());
        return [
            'rows'  => $jobs,
            'total' => count($jobs),
        ];
    }

    /**
     * @param array $jobIds
     * @param string $queryClass
     * @return array
     */
    private function processJobs(array $jobIds, string $queryClass): array
    {
        $query = new $queryClass();
        $query->setJobs($jobIds);
        return $this->getConnector()->query($query);
    }

    /**
     * @return array
     */
    public function triggerAll(): array
    {
        return $this->processJobs($this->getAllJobIds(), StartJobs::class);
    }

    /**
     * @return array
     */
    public function stopAll(): array
    {
        return $this->processJobs($this->getAllJobIds(), StopJobs::class);
    }

    /**
     * @param int $id
     * @return array
     */
    public function trigger(int $id): array
    {
        return $this->processJobs([$id], StartJobs::class);
    }

    /**
     * @param int $id
     * @return array
     */
    public function stop(int $id): array
    {
        return $this->processJobs([$id], StopJobs::class);
    }

    /**
     * @return array
     */
    public function getStatus(): array
    {
        return $this->getConnector()->query(new GetDiscoveryLog());
    }

    /**
     * @param array $data
     * @return array
     */
    public function discoverDevice(array $data): array
    {
        $connector = $this->getConnector();

        if (!empty($data['serial'])) {
            $deviceId = $this->identifyDevice($data['serial'], $connector);
            if (!$deviceId) {
                return [
                    'status'  => 'Failure',
                    'message' => 'Device not found',
                ];
            }

            $discoveryParameter = new StringType('id', $deviceId);
        } else {
            $discoveryParameter = new StringType('ipOrHostname', $data['ip'] ?? $data['hostname']);
            $discoveryParameter->setRequired(true);
        }

        $query = new DiscoverDevice();
        $query->setParameters([
            $discoveryParameter,
        ]);

        $result = $connector->query($query);

        return $result[0] ?? [
            'status'  => 'Unknown',
            'message' => 'Empty result',
        ];
    }

    /**
     * @param string $serial
     * @param Connector $connector
     * @return int|null
     */
    private function identifyDevice(string $serial, Connector $connector): ?int
    {
        $query = new IdentifyDevice();
        $query->setParameters([
            new DeviceInputType(
                'device',
                [
                   'serialNumber' => $serial,
                ]
            ),
        ]);
        $deviceData = $connector->query($query);

        return $deviceData['device']['id'] ?? null;
    }

}
