<?php

namespace Mainto\RpcServer\Service\Struct;


use Illuminate\Support\Collection;
use Mainto\RpcServer\RpcServer\RpcDefinition;
use RuntimeException;

class Config {
    const Prefix = "/mainto.lutty/service/";

    private Collection $paths;

    private Collection $messages;

    private Collection $crons;

    private Collection $websockets;

    /**
     * @var array ["[POST]/test/example/add" => true]
     */
    private array $_pathUrlMap;

    public function __construct () {
        $this->paths = collect([]);
        $this->messages = collect([]);
        $this->crons = collect([]);
        $this->websockets = collect([]);
    }

    public static function createFromDefinition (RpcDefinition $definition) {
        $config = new self();

        foreach ($definition->controllers as $controller) {
            foreach ($controller->methods as $method) {
                if ($method->httpApi) {
                    $path = new Path();
                    $path->setClass($controller->registerName);
                    $path->setMethod($method->name);
                    $path->setHttpMethod($method->httpApi->method);
                    $path->setHttpUrl($method->httpApi->url);
                    $path->setFormat($method->resSerialize);

                    foreach ($method->needAuthorities as $authority) {
                        $path->addAuthority($authority);
                    }
                    foreach ($method->resHeaders as $resHeader) {
                        $path->addResponseHeader($resHeader->name, $resHeader->value);
                    }

                    foreach ($method->reqHeaders as $header) {
                        $path->addHeader($header->name);
                    }

                    $config->addPath($path);
                }
                if ($method->cronHookName) {
                    $cron = new Cron();

                    $cron->setClass($controller->registerName);
                    $cron->setMethod($method->name);
                    $cron->setSpecStr($method->cronSpec);
                    $cron->setCronHookName($method->cronHookName);
                    $config->addCron($cron);
                }
                if ($method->messageHookName) {
                    $message = new Message();

                    $message->setClass($controller->registerName);
                    $message->setMethod($method->name);
                    $message->setMessageHookName($method->messageHookName);
                    $config->addMessage($message);
                }
                if ($method->wsRoom) {
                    $websocket = new Websocket();

                    $websocket->setClass($controller->registerName);
                    $websocket->setMethod($method->name);
                    $websocket->setNeedConnectMessage($method->wsNeedConnectMessage);
                    $websocket->setNeedDisconnectMessage($method->wsNeedDisconnectMessage);
                    $websocket->setRoomName($method->wsRoom);
                    $config->addWebsocket($websocket);
                }
            }
        }

        return $config;
    }

    public function addPath (Path $path): void {
        if ($path->getHttpUrl() && $path->getHttpMethod()) {
            $urlKey = sprintf("[%s]%s", $path->getHttpMethod(), $path->getHttpUrl());
            if (isset($this->_pathUrlMap[$urlKey])) {
                throw new RuntimeException("already has this router: {$urlKey}");
            }

            $this->_pathUrlMap[$urlKey] = true;
        }

        $this->paths->push($path);
    }

    public function addCron (Cron $cron): void {
        $this->crons->push($cron);
    }

    public function addMessage (Message $message): void {
        $this->messages->push($message);
    }

    public function addWebsocket (Websocket $message): void {
        $this->websockets->push($message);
    }

    /**
     * @return Collection
     */
    public function getPaths (): Collection {
        return $this->paths;
    }

    /**
     * @return Collection
     */
    public function getCrons (): Collection {
        return $this->crons;
    }

    /**
     * @return Collection
     */
    public function getMessages (): Collection {
        return $this->messages;
    }

    /**
     * @return Collection
     */
    public function getWebsockets (): Collection {
        return $this->websockets;
    }
}