<?php

declare(strict_types=1);

namespace MauticPlugin\DealFlowBundle\Controller;

use Mautic\CoreBundle\Controller\CommonController;
use Mautic\DashboardBundle\Model\DashboardModel;
use MauticPlugin\DealFlowBundle\EventSubscriber\DashboardSubscriber as DealFlowDashboardSubscriber;
use MauticPlugin\DealFlowBundle\Service\DealAuthorization;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\Security\Csrf\CsrfToken;
use Symfony\Component\Security\Csrf\CsrfTokenManagerInterface;

class DashboardAjaxController extends CommonController
{
    private const ALLOWED_PARAM_KEYS = ['pipeline_id', 'weeks'];

    private const ALLOWED_WIDGET_TYPES = [
        DealFlowDashboardSubscriber::TYPE_DEALS_BY_STAGE,
        DealFlowDashboardSubscriber::TYPE_DEALS_CLOSING_MONTH,
        DealFlowDashboardSubscriber::TYPE_DEALS_WON_LOST,
        DealFlowDashboardSubscriber::TYPE_MY_TASKS,
        DealFlowDashboardSubscriber::TYPE_OVERDUE_TASKS,
        DealFlowDashboardSubscriber::TYPE_OWNER_LEADERBOARD,
        DealFlowDashboardSubscriber::TYPE_PIPELINE_SUMMARY,
        DealFlowDashboardSubscriber::TYPE_PIPELINE_VALUE,
        DealFlowDashboardSubscriber::TYPE_REVENUE_OVER_TIME,
        DealFlowDashboardSubscriber::TYPE_STALE_DEALS,
        DealFlowDashboardSubscriber::TYPE_WEIGHTED_FORECAST,
    ];

    private const ALLOWED_WEEK_VALUES = [4, 8, 13, 26];

    public function saveWidgetConfigAction(
        Request $request,
        DashboardModel $dashboardModel,
        DealAuthorization $authorization,
        CsrfTokenManagerInterface $csrfTokenManager,
    ): JsonResponse {
        if (!$authorization->canViewDeals() && !$authorization->canViewReports()) {
            return new JsonResponse(['success' => false, 'error' => 'Access denied'], 403);
        }

        $widgetId = $request->request->getInt('widgetId');

        if ($widgetId <= 0) {
            return new JsonResponse(['success' => false, 'error' => 'Missing widget ID'], 400);
        }

        $token = (string) $request->request->get('_token', '');
        if (!$csrfTokenManager->isTokenValid(new CsrfToken(sprintf('dealflow_widget_config_%d', $widgetId), $token))) {
            return new JsonResponse(['success' => false, 'error' => 'Invalid CSRF token'], 403);
        }

        $widget = $dashboardModel->getEntity($widgetId);

        if (!$widget) {
            return new JsonResponse(['success' => false, 'error' => 'Widget not found'], 404);
        }

        if (!method_exists($widget, 'getType') || !in_array((string) $widget->getType(), self::ALLOWED_WIDGET_TYPES, true)) {
            return new JsonResponse(['success' => false, 'error' => 'Widget not found'], 404);
        }

        $params = $request->request->all('params');
        if (!is_array($params)) {
            $params = [];
        }

        try {
            $normalizedParams = $this->normalizeParams($params);
        } catch (\InvalidArgumentException $exception) {
            return new JsonResponse(['success' => false, 'error' => $exception->getMessage()], 400);
        }

        $existingParams = $widget->getParams() ?? [];
        foreach ($normalizedParams as $key => $value) {
            if (null === $value) {
                unset($existingParams[$key]);
            } else {
                $existingParams[$key] = $value;
            }
        }
        $widget->setParams($existingParams);
        $dashboardModel->saveEntity($widget);

        return new JsonResponse(['success' => true]);
    }

    /**
     * @param array<mixed> $params
     *
     * @return array<string, int|null>
     */
    private function normalizeParams(array $params): array
    {
        $normalized = [];

        foreach ($params as $key => $value) {
            $key = (string) $key;
            if (!in_array($key, self::ALLOWED_PARAM_KEYS, true)) {
                throw new \InvalidArgumentException(sprintf('Unsupported widget parameter: %s', $key));
            }

            if ('' === $value || null === $value) {
                $normalized[$key] = null;

                continue;
            }

            if (!is_scalar($value)) {
                throw new \InvalidArgumentException(sprintf('Invalid value for widget parameter: %s', $key));
            }

            $stringValue = trim((string) $value);

            if ('pipeline_id' === $key) {
                if (!ctype_digit($stringValue) || (int) $stringValue <= 0) {
                    throw new \InvalidArgumentException('Pipeline selection must be a valid pipeline ID.');
                }

                $normalized[$key] = (int) $stringValue;

                continue;
            }

            if (!ctype_digit($stringValue) || !in_array((int) $stringValue, self::ALLOWED_WEEK_VALUES, true)) {
                throw new \InvalidArgumentException('Time range selection is invalid.');
            }

            $normalized[$key] = (int) $stringValue;
        }

        return $normalized;
    }
}
