<?php

namespace idoit\Module\SyneticsJdisc\Controller;

use Exception;
use idoit\Module\SyneticsJdisc\Controller\Exception\NotFoundException;
use idoit\Module\SyneticsJdisc\Controller\Response\NotFoundResponse;
use idoit\Module\SyneticsJdisc\Model\DiscoveryDao;
use isys_application;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Throwable;

class DiscoveryController
{
    private ?DiscoveryDao $dao = null;

    /**
     * @return DiscoveryDao
     * @throws Exception
     */
    private function getDao(): DiscoveryDao
    {
        if (null === $this->dao) {
            $this->dao = isys_application::instance()->container->get('idoit.jdisc.discovery.dao');
        }

        return $this->dao;
    }

    /**
     * Initialize DAO with serverId and execute the callback function
     *
     * @param Request $request
     * @param callable $callback Function to execute if initialization is successful
     * @return Response
     */
    private function executeWithDao(Request $request, callable $callback): Response
    {
        $serverId = $request->get('serverId');

        $dao = $this->getDao();
        if (!$dao->init($serverId)) {
            return new JsonResponse([
                'success' => false,
                'status' => 'Could not init discovery',
            ]);
        }

        try {
            return new JsonResponse($callback($dao));
        } catch (NotFoundException $e) {
            return new NotFoundResponse($e->getMessage());
        } catch (Throwable $e) {
            return new JsonResponse(
                [
                    'error' => $e->getMessage()
                ],
                Response::HTTP_INTERNAL_SERVER_ERROR
            );
        }
    }

    /**
     * @param Request $request
     * @return Response
     * @throws NotFoundException
     */
    public function find(Request $request): Response
    {
        return $this->executeWithDao($request, function (DiscoveryDao $dao) {
            $list = $dao->list();
            if (empty($list)) {
                throw new NotFoundException('Discovery');
            }
            return $list;
        });
    }

    /**
     * @param Request $request
     * @return Response
     */
    public function triggerAll(Request $request): Response
    {
        return $this->executeWithDao($request, function (DiscoveryDao $dao) {
            return $dao->triggerAll();
        });
    }

    /**
     * @param Request $request
     * @return Response
     */
    public function stopAll(Request $request): Response
    {
        return $this->executeWithDao($request, function (DiscoveryDao $dao) {
            return $dao->stopAll();
        });
    }

    /**
     * @param Request $request
     * @param int $id
     * @return Response
     */
    public function trigger(Request $request, int $id): Response
    {
        return $this->executeWithDao($request, function (DiscoveryDao $dao) use ($id) {
            return $dao->trigger($id);
        });
    }

    /**
     * @param Request $request
     * @param int $id
     * @return Response
     */
    public function stop(Request $request, int $id): Response
    {
        return $this->executeWithDao($request, function (DiscoveryDao $dao) use ($id) {
            return $dao->stop($id);
        });
    }

    /**
     * @param Request $request
     * @return Response
     */
    public function getStatus(Request $request): Response
    {
        return $this->executeWithDao($request, function (DiscoveryDao $dao) {
            return $dao->getStatus();
        });
    }

    /**
     * @param Request $request
     * @return Response
     */
    public function discoverDevice(Request $request): Response
    {
        $data = json_decode($request->getContent(), true);
        $keys = ['serial' => 1, 'ip' => 1, 'hostname' => 1];
        if (empty($data) || !array_intersect_key($data, $keys)) {
            return new JsonResponse(['error' => 'No data provided'], Response::HTTP_BAD_REQUEST);
        }

        return $this->executeWithDao($request, function (DiscoveryDao $dao) use ($data) {
            return $dao->discoverDevice($data);
        });
    }
}
