<?php

declare(strict_types=1);

namespace MauticPlugin\DealFlowBundle\EventSubscriber;

use Doctrine\Persistence\ManagerRegistry;
use Mautic\DashboardBundle\Event\WidgetDetailEvent;
use Mautic\DashboardBundle\EventListener\DashboardSubscriber as MainDashboardSubscriber;
use Mautic\UserBundle\Entity\User;
use MauticPlugin\DealFlowBundle\Entity\DealRepository;
use MauticPlugin\DealFlowBundle\Entity\DealTaskRepository;
use MauticPlugin\DealFlowBundle\Entity\PipelineRepository;
use MauticPlugin\DealFlowBundle\Form\Type\DashboardDealFlowWidgetType;
use MauticPlugin\DealFlowBundle\Service\LicenseService;

class DashboardSubscriber extends MainDashboardSubscriber
{
    public const TYPE_DEALS_BY_STAGE = 'dealflow.deals.by.stage';
    public const TYPE_DEALS_CLOSING_MONTH = 'dealflow.deals.closing.this.month';
    public const TYPE_DEALS_WON_LOST = 'dealflow.deals.won.vs.lost';
    public const TYPE_MY_TASKS = 'dealflow.my.tasks';
    public const TYPE_OVERDUE_TASKS = 'dealflow.overdue.tasks';
    public const TYPE_OWNER_LEADERBOARD = 'dealflow.owner.leaderboard';
    public const TYPE_PIPELINE_SUMMARY = 'dealflow.pipeline.summary';
    public const TYPE_PIPELINE_VALUE = 'dealflow.pipeline.value';
    public const TYPE_STALE_DEALS = 'dealflow.stale.deals';
    public const TYPE_REVENUE_OVER_TIME = 'dealflow.revenue.over.time';
    public const TYPE_WEIGHTED_FORECAST = 'dealflow.weighted.forecast';

    protected $bundle = 'deals';

    protected $types = [
        self::TYPE_DEALS_BY_STAGE => ['formAlias' => DashboardDealFlowWidgetType::class],
        self::TYPE_DEALS_CLOSING_MONTH => ['formAlias' => DashboardDealFlowWidgetType::class],
        self::TYPE_DEALS_WON_LOST => ['formAlias' => DashboardDealFlowWidgetType::class],
        self::TYPE_MY_TASKS => [],
        self::TYPE_OVERDUE_TASKS => [],
        self::TYPE_OWNER_LEADERBOARD => ['formAlias' => DashboardDealFlowWidgetType::class],
        self::TYPE_PIPELINE_SUMMARY => ['formAlias' => DashboardDealFlowWidgetType::class],
        self::TYPE_PIPELINE_VALUE => ['formAlias' => DashboardDealFlowWidgetType::class],
        self::TYPE_STALE_DEALS => ['formAlias' => DashboardDealFlowWidgetType::class],
        self::TYPE_REVENUE_OVER_TIME => ['formAlias' => DashboardDealFlowWidgetType::class],
        self::TYPE_WEIGHTED_FORECAST => ['formAlias' => DashboardDealFlowWidgetType::class],
    ];

    public function __construct(
        private readonly DealRepository $dealRepository,
        private readonly DealTaskRepository $taskRepository,
        private readonly PipelineRepository $pipelineRepository,
        private readonly ManagerRegistry $managerRegistry,
        private readonly LicenseService $licenseService,
    ) {
    }

    public function onWidgetListGenerate(\Mautic\DashboardBundle\Event\WidgetTypeListEvent $event): void
    {
        if (!$this->licenseService->isLicensed()) {
            return;
        }

        parent::onWidgetListGenerate($event);
    }

    public function onWidgetFormGenerate(\Mautic\DashboardBundle\Event\WidgetFormEvent $event): void
    {
        if (!$this->licenseService->isLicensed()) {
            return;
        }

        parent::onWidgetFormGenerate($event);
    }

    public function onWidgetDetailGenerate(WidgetDetailEvent $event): void
    {
        if (!$this->licenseService->isLicensed()) {
            return;
        }

        $handlers = [
            self::TYPE_PIPELINE_SUMMARY => 'renderPipelineSummary',
            self::TYPE_MY_TASKS => 'renderMyTasks',
            self::TYPE_DEALS_BY_STAGE => 'renderDealsByStage',
            self::TYPE_PIPELINE_VALUE => 'renderPipelineValue',
            self::TYPE_DEALS_WON_LOST => 'renderDealsWonLost',
            self::TYPE_DEALS_CLOSING_MONTH => 'renderDealsClosingMonth',
            self::TYPE_OWNER_LEADERBOARD => 'renderOwnerLeaderboard',
            self::TYPE_STALE_DEALS => 'renderStaleDeals',
            self::TYPE_WEIGHTED_FORECAST => 'renderWeightedForecast',
            self::TYPE_OVERDUE_TASKS => 'renderOverdueTasks',
            self::TYPE_REVENUE_OVER_TIME => 'renderRevenueOverTime',
        ];

        $method = $handlers[$event->getType()] ?? null;

        if (null !== $method) {
            $this->$method($event);
            $event->stopPropagation();
        }
    }

    private function renderPipelineSummary(WidgetDetailEvent $event): void
    {
        $pipeline = $this->getPipelineForWidget($event);
        if (null === $pipeline) {
            return;
        }

        $pipelineValue = $this->dealRepository->getPipelineValue($pipeline);
        $deals = $this->dealRepository->findOpenDeals(50);

        $event->setTemplate('@DealFlow/Dashboard/pipeline_summary.html.twig');
        $event->setTemplateData(array_merge([
            'deals' => $deals,
            'totalValue' => (float) $pipelineValue,
            'dealCount' => count($deals),
        ], $this->getWidgetConfigData($event)));
    }

    private function renderMyTasks(WidgetDetailEvent $event): void
    {
        $tasks = $this->taskRepository->findOpenTasks(10);

        $event->setTemplate('@DealFlow/Dashboard/my_tasks.html.twig');
        $event->setTemplateData([
            'tasks' => $tasks,
        ]);
    }

    private function renderDealsByStage(WidgetDetailEvent $event): void
    {
        $pipeline = $this->getPipelineForWidget($event);
        if (null === $pipeline) {
            return;
        }

        $stages = $this->dealRepository->getStageBreakdown($pipeline);
        $maxCount = 0;
        $totalDeals = 0;
        foreach ($stages as $stage) {
            $count = (int) $stage['count'];
            $totalDeals += $count;
            if ($count > $maxCount) {
                $maxCount = $count;
            }
        }

        $event->setTemplate('@DealFlow/Dashboard/deals_by_stage.html.twig');
        $event->setTemplateData(array_merge([
            'stages' => $stages,
            'maxCount' => $maxCount,
            'totalDeals' => $totalDeals,
        ], $this->getWidgetConfigData($event)));
    }

    private function renderPipelineValue(WidgetDetailEvent $event): void
    {
        $pipeline = $this->getPipelineForWidget($event);
        if (null === $pipeline) {
            return;
        }

        $pipelineValue = $this->dealRepository->getPipelineValue($pipeline);
        $dealCount = $this->dealRepository->countByPipeline($pipeline);

        $event->setTemplate('@DealFlow/Dashboard/pipeline_value.html.twig');
        $event->setTemplateData(array_merge([
            'pipelineValue' => $pipelineValue,
            'dealCount' => $dealCount,
        ], $this->getWidgetConfigData($event)));
    }

    private function renderDealsWonLost(WidgetDetailEvent $event): void
    {
        $pipeline = $this->getPipelineForWidget($event);
        if (null === $pipeline) {
            return;
        }

        $counts = $this->dealRepository->getWonLostCounts($pipeline);

        $event->setTemplate('@DealFlow/Dashboard/deals_won_lost.html.twig');
        $event->setTemplateData(array_merge([
            'wonCount' => $counts['won'] ?? 0,
            'lostCount' => $counts['lost'] ?? 0,
            'openCount' => $counts['open'] ?? 0,
        ], $this->getWidgetConfigData($event)));
    }

    private function renderDealsClosingMonth(WidgetDetailEvent $event): void
    {
        $pipeline = $this->getPipelineForWidget($event);
        if (null === $pipeline) {
            return;
        }

        $now = new \DateTimeImmutable();
        $deals = $this->dealRepository->findClosingThisMonth($pipeline, $now);

        $event->setTemplate('@DealFlow/Dashboard/deals_closing_this_month.html.twig');
        $event->setTemplateData(array_merge([
            'deals' => $deals,
            'monthLabel' => $now->format('F Y'),
        ], $this->getWidgetConfigData($event)));
    }

    private function renderOwnerLeaderboard(WidgetDetailEvent $event): void
    {
        $pipeline = $this->getPipelineForWidget($event);
        if (null === $pipeline) {
            return;
        }

        $rawLeaders = $this->dealRepository->getOwnerLeaderboard($pipeline);
        $ownerNames = $this->resolveOwnerNames(array_column($rawLeaders, 'ownerId'));

        $leaders = [];
        foreach ($rawLeaders as $row) {
            $ownerId = $row['ownerId'];
            $leaders[] = [
                'ownerName' => $ownerNames[$ownerId] ?? ($ownerId ? sprintf('User #%d', $ownerId) : 'Unassigned'),
                'dealsCount' => $row['dealsCount'],
                'totalAmount' => $row['totalAmount'],
            ];
        }

        $event->setTemplate('@DealFlow/Dashboard/owner_leaderboard.html.twig');
        $event->setTemplateData(array_merge([
            'leaders' => $leaders,
        ], $this->getWidgetConfigData($event)));
    }

    /**
     * @param list<int|null> $ownerIds
     *
     * @return array<int, string>
     */
    private function resolveOwnerNames(array $ownerIds): array
    {
        $ownerIds = array_values(array_unique(array_filter($ownerIds)));
        if ([] === $ownerIds) {
            return [];
        }

        $names = [];
        foreach ($this->managerRegistry->getRepository(User::class)->findBy(['id' => $ownerIds]) as $user) {
            $id = $user->getId();
            if (null === $id) {
                continue;
            }
            $names[$id] = method_exists($user, 'getName') && '' !== trim((string) $user->getName())
                ? trim((string) $user->getName())
                : (method_exists($user, 'getUserIdentifier') ? (string) $user->getUserIdentifier() : sprintf('User #%d', $id));
        }

        return $names;
    }

    private function renderStaleDeals(WidgetDetailEvent $event): void
    {
        $pipeline = $this->getPipelineForWidget($event);
        if (null === $pipeline) {
            return;
        }

        $staleDays = 14;
        $deals = $this->dealRepository->findStaleDeals($pipeline, $staleDays);

        $event->setTemplate('@DealFlow/Dashboard/stale_deals.html.twig');
        $event->setTemplateData(array_merge([
            'deals' => $deals,
            'staleDays' => $staleDays,
        ], $this->getWidgetConfigData($event)));
    }

    private function renderWeightedForecast(WidgetDetailEvent $event): void
    {
        $pipeline = $this->getPipelineForWidget($event);
        if (null === $pipeline) {
            return;
        }

        $forecastValue = $this->dealRepository->getWeightedForecast($pipeline);
        $pipelineValue = $this->dealRepository->getPipelineValue($pipeline);

        $event->setTemplate('@DealFlow/Dashboard/weighted_forecast.html.twig');
        $event->setTemplateData(array_merge([
            'forecastValue' => $forecastValue,
            'pipelineValue' => $pipelineValue,
        ], $this->getWidgetConfigData($event)));
    }

    private function renderOverdueTasks(WidgetDetailEvent $event): void
    {
        $overdueCount = $this->taskRepository->countOverdueTasks();

        $event->setTemplate('@DealFlow/Dashboard/overdue_tasks.html.twig');
        $event->setTemplateData([
            'overdueCount' => $overdueCount,
        ]);
    }

    private function renderRevenueOverTime(WidgetDetailEvent $event): void
    {
        $pipeline = $this->getPipelineForWidget($event);
        if (null === $pipeline) {
            return;
        }

        $params = $event->getWidget()->getParams();
        $weeks = (int) ($params['weeks'] ?? 13);
        if ($weeks < 1) {
            $weeks = 13;
        }

        $weeklyData = $this->dealRepository->getWeeklyRevenue($pipeline, $weeks);

        // Pre-compute cumulative data
        $cumulativeData = [];
        $runningTotal = 0.0;
        foreach ($weeklyData as $week) {
            $runningTotal += (float) $week['revenue'];
            $cumulativeData[] = [
                'weekStart' => $week['weekStart'],
                'weekEnd' => $week['weekEnd'],
                'revenue' => number_format($runningTotal, 0, '.', ''),
            ];
        }

        // Find max values for bar scaling
        $maxWeekly = 0.0;
        foreach ($weeklyData as $week) {
            $val = (float) $week['revenue'];
            if ($val > $maxWeekly) {
                $maxWeekly = $val;
            }
        }
        $maxCumulative = $runningTotal;

        $event->setTemplate('@DealFlow/Dashboard/revenue_over_time.html.twig');
        $event->setTemplateData(array_merge([
            'weeklyData' => $weeklyData,
            'cumulativeData' => $cumulativeData,
            'maxWeekly' => $maxWeekly,
            'maxCumulative' => $maxCumulative,
            'weeks' => $weeks,
        ], $this->getWidgetConfigData($event, [
            'showDateRange' => true,
            'currentWeeks' => $weeks,
        ])));
    }

    private function getPipelinesForConfig(): array
    {
        $pipelines = $this->pipelineRepository->findBy([], ['id' => 'ASC']);
        $result = [];
        foreach ($pipelines as $pipeline) {
            $result[] = ['id' => $pipeline->getId(), 'name' => $pipeline->getName()];
        }

        return $result;
    }

    private function getWidgetConfigData(WidgetDetailEvent $event, array $extra = []): array
    {
        $widget = $event->getWidget();
        $params = $widget->getParams();

        return array_merge([
            'widgetId' => $widget->getId(),
            'pipelines' => $this->getPipelinesForConfig(),
            'currentPipelineId' => $params['pipeline_id'] ?? null,
        ], $extra);
    }

    private function getPipelineForWidget(WidgetDetailEvent $event): ?\MauticPlugin\DealFlowBundle\Entity\Pipeline
    {
        $params = $event->getWidget()->getParams();
        $pipelineId = $params['pipeline_id'] ?? null;

        if ($pipelineId) {
            $pipeline = $this->pipelineRepository->find($pipelineId);
            if ($pipeline) {
                return $pipeline;
            }
        }

        return $this->pipelineRepository->findMarkedDefaultPipeline()
            ?? $this->pipelineRepository->findOneBy([], ['id' => 'ASC']);
    }
}
