<?php

namespace idoit\Module\SyneticsJdisc\Controller;

use idoit\Module\SyneticsJdisc\Controller\Response\ArrayResponse;
use idoit\Module\SyneticsJdisc\Controller\Response\NotFoundResponse;
use idoit\Module\SyneticsJdisc\Controller\Table\SearchParams;
use idoit\Module\SyneticsJdisc\Model\JDiscServerDao;
use idoit\Module\SyneticsJdisc\Service\CertificateChecker;
use idoit\Module\SyneticsJdisc\SSLCertificateException;
use idoit\Module\SyneticsJdisc\View\DiscoveryServer;
use idoit\Module\SyneticsJdisc\View\DiscoveryServerList;
use isys_application;
use isys_jdisc_dao;
use isys_notify;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Throwable;

class DiscoveryServerController extends AbstractController
{
    private ?JDiscServerDao $dao = null;

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

    /**
     * @return Response
     * @throws \Exception
     */
    public function page(): Response
    {
        $navMode = (int)$_POST[C__GET__NAVMODE];

        if ($navMode === C__NAVMODE__NEW) {
            return DiscoveryServer::factory()->setMode($navMode)->render();
        }

        $id = null;

        if (!empty($_POST[C__GET__ID])) {
            $id = (int)((is_array($_POST[C__GET__ID]) && count($_POST[C__GET__ID]) === 1) ? current($_POST[C__GET__ID]) : $_POST[C__GET__ID]);
        }

        $discoveryServer = DiscoveryServer::factory()->setMode($navMode)->setId($id);

        if (in_array($navMode, [C__NAVMODE__EDIT, C__NAVMODE__CANCEL]) && $id > 0) {
            return $discoveryServer->render();
        }

        if ($navMode === C__NAVMODE__SAVE) {
            $dao = $discoveryServer->getDao();
            $data = $dao->prepareData();
            $discoveryServer->setValidationErrors($dao->validateData($data));

            if (null === $id) {
                $discoveryServer->unsetValidationError('id');
            }

            if ($navMode === C__NAVMODE__SAVE && !$discoveryServer->hasValidationErrors() && $dao->save($id, $data)) {
                isys_notify::success($discoveryServer->language->get('LC__JDISC__DISCOVERY_SERVERS__SAVED'));
            }

            return $discoveryServer->render();
        }

        return DiscoveryServerList::factory()->setMode($navMode)->render();
    }

    /**
     * @return Response
     */
    public function handleEntry(string $id): Response
    {
        $navMode = (int)$_POST[C__GET__NAVMODE];
        if ((!$id && !in_array($navMode, [C__NAVMODE__EDIT, C__NAVMODE__SAVE]))) {
            return DiscoveryServerList::factory()->setMode($navMode)->render();
        }

        $discoveryServer = DiscoveryServer::factory()->setMode($navMode)->setId((int)$id);
        if ($navMode === C__NAVMODE__SAVE) {
            $dao = $discoveryServer->getDao();
            $data = $dao->prepareData();
            $discoveryServer->setValidationErrors($dao->validateData($data));

            if ($navMode === C__NAVMODE__SAVE && !$discoveryServer->hasValidationErrors() && $dao->save($id, $data)) {
                isys_notify::success($discoveryServer->language->get('LC__JDISC__DISCOVERY_SERVERS__SAVED'));
            }
        }

        return $discoveryServer->render();
    }

    /**
     * @param Request $request
     * @return Response
     */
    public function find(Request $request): Response
    {
        $search = SearchParams::fromParams($request->get('params'));

        return new ArrayResponse(
            $this->getDao()->getData(null, $search),
            $this->getDao()->getCount(),
        );
    }

    /**
     * @param int $id
     * @return Response
     */
    public function get(int $id): Response
    {
        $server = $this->getDao()->getConfiguration($id);
        if ($server === null) {
            return new NotFoundResponse('DiscoveryServer');
        }
        $server['name'] = $server['title'];
        unset($server['title']);
        return new JsonResponse($server);
    }

    /**
     * @param Request $request
     * @param int|null $id
     * @return Response
     */
    public function save(Request $request, ?int $id = null): Response
    {
        $server = json_decode($request->getContent(), true);
        if (empty($server)) {
            return new JsonResponse(['error' => 'No data provided'], Response::HTTP_BAD_REQUEST);
        }

        if (!empty($id)) {
            $server['id'] = $id;
        } else {
            unset($server['id']);
        }

        $server['title'] = $server['name'];
        unset($server['name']);

        $jdiscDao = $this->getDao()->getJdiscDao();
        try {
            $serverId = $jdiscDao->save(isys_jdisc_dao::C__CONFIGURATION, $server);
        } catch (Throwable $e) {
            return new JsonResponse(['error' => $e->getMessage()], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
        if (empty($serverId)) {
            return new JsonResponse(['error' => 'Server save error'], Response::HTTP_INTERNAL_SERVER_ERROR);
        }

        if (!empty($server['default_server']) && (int) $server['default_server'] === 1) {
            $jdiscDao->reset_default_server($serverId);
        }

        return new JsonResponse(['id' => $serverId]);
    }

    /**
     * @param int $id
     * @return Response
     */
    public function delete(int $id): Response
    {
        $result = $this->getDao()->delete([$id]);
        if (!$result) {
            return new JsonResponse(['error' => 'Server deletion error'], Response::HTTP_INTERNAL_SERVER_ERROR);
        }
        return new JsonResponse(['success' => true]);
    }

    /**
     * @param int $id
     * @return Response
     */
    public function checkConfig(int $id): Response
    {
        try {
            $this->getDao()->getJdiscDao()->get_connection($id);

            return new JsonResponse([
                'connection' => true,
                'message'    => $this->language->get('LC__MODULE__JDISC__CONNECTION_SUCCESS'),
            ]);
        } catch (Throwable $e) {
            return new JsonResponse([
                'connection' => false,
                'message'    => $e->getMessage(),
            ]);
        }
    }

    /**
     * @param int $id
     * @return Response
     */
    public function checkDiscovery(int $id): Response
    {
        try {
            $jdiscDao = $this->getDao()->getJdiscModule();
            if ($jdiscDao->web_service_active($id) || $jdiscDao->isGraphqlAvailable($id)) {
                return new JsonResponse([
                    'success' => true,
                    'message' => $this->language->get('LC__MODULE__JDISC__DISCOVERY__CONNECTION_SUCCESS'),
                ]);
            }

        } catch (Throwable $e) {
            // Do nothing
        }
        return new JsonResponse([
            'success' => false,
            'message' => $this->language->get('LC__MODULE__JDISC__DISCOVERY__CONNECTION_FAILED'),
        ]);

    }

    /**
     * @param int $id
     * @return Response
     */
    public function checkGraphql(int $id): Response
    {
        try {
            if ($this->getDao()->getJdiscModule()->isGraphqlAvailable($id)) {
                return new JsonResponse([
                    'success' => true,
                ]);
            }
        } catch (Throwable $e) {
            // Do nothing
        }
        return new JsonResponse([
            'success' => false,
        ]);
    }

    /**
     * @param int $id
     * @return Response
     */
    public function checkCertificate(int $id): Response
    {
        $response = new JsonResponse();

        $server = $this->getDao()->getConfiguration($id) ?? [];
        try {
            CertificateChecker::checkCertificate($server, $this->language);

            $response->setData(['valid' => true]);
        } catch (SSLCertificateException $e) {
            $response->setData(['warning' => $e->getMessage()]);
        } catch (\Throwable $e) {
            $response->setData(['error' => $e->getMessage()]);
            $response->setStatusCode(Response::HTTP_INTERNAL_SERVER_ERROR);
        }

        return $response;
    }
}
