<?php

declare(strict_types=1);

namespace MauticPlugin\DealFlowBundle\ViewModel;

use MauticPlugin\DealFlowBundle\Entity\Deal;
use Symfony\Component\DependencyInjection\Attribute\Exclude;

/**
 * Plain view model for the deal detail page.
 * Encapsulates computed display logic that was previously inline in Twig.
 * No service dependencies — constructor takes a Deal entity only.
 *
 * #[Exclude] prevents Symfony's service auto-registration from treating this
 * value object as a service. It is always instantiated directly: new DealViewViewModel($deal).
 */
#[Exclude]
final class DealViewViewModel
{
    /** @var array<int, \MauticPlugin\DealFlowBundle\Entity\DealTask> */
    private array $overdueTasks = [];

    /** @var array<int, \MauticPlugin\DealFlowBundle\Entity\DealTask> */
    private array $openTasks = [];

    /** @var array<int, \MauticPlugin\DealFlowBundle\Entity\DealTask> */
    private array $completedTasks = [];

    private \DateTimeImmutable $now;

    public function __construct(
        private readonly Deal $deal,
        ?\DateTimeImmutable $now = null,
    ) {
        $this->now = $now ?? new \DateTimeImmutable('now');
        $this->categorizeTasks();
    }

    public function getDeal(): Deal
    {
        return $this->deal;
    }

    /**
     * Number of days since the deal was last updated (or created if never modified).
     */
    public function getUpdatedDaysAgo(): int
    {
        $reference = $this->deal->getDateModified() ?? $this->deal->getDateAdded();
        if (null === $reference) {
            return 0;
        }

        return (int) $this->now->diff($reference)->days;
    }

    /**
     * Traffic-light color for the "last updated" health signal.
     *
     * @return 'green'|'yellow'|'red'
     */
    public function getUpdatedColor(): string
    {
        $days = $this->getUpdatedDaysAgo();
        if ($days < 3) {
            return 'green';
        }

        return $days <= 7 ? 'yellow' : 'red';
    }

    /**
     * Signed number of days until expected close. Negative means overdue.
     * Returns null when no close date is set.
     */
    public function getDaysToClose(): ?int
    {
        $closeDate = $this->deal->getExpectedCloseDate();
        if (null === $closeDate) {
            return null;
        }

        $rawDays = (int) $this->now->diff($closeDate)->days;

        return $closeDate < $this->now ? -$rawDays : $rawDays;
    }

    /**
     * Traffic-light color for the "days to close" health signal.
     *
     * @return 'green'|'yellow'|'red'|'gray'
     */
    public function getCloseColor(): string
    {
        $days = $this->getDaysToClose();
        if (null === $days) {
            return 'gray';
        }
        if ($days > 30) {
            return 'green';
        }

        return $days >= 14 ? 'yellow' : 'red';
    }

    /**
     * Number of overdue tasks on this deal.
     */
    public function getOverdueCount(): int
    {
        return \count($this->overdueTasks);
    }

    /**
     * Traffic-light color for overdue tasks.
     *
     * @return 'green'|'red'
     */
    public function getOverdueColor(): string
    {
        return $this->getOverdueCount() > 0 ? 'red' : 'green';
    }

    /**
     * Traffic-light color for confidence.
     *
     * @return 'green'|'yellow'|'red'|'gray'
     */
    public function getConfidenceColor(): string
    {
        $confidence = $this->deal->getConfidence();
        if (null === $confidence) {
            return 'gray';
        }
        if ($confidence > 70) {
            return 'green';
        }

        return $confidence >= 40 ? 'yellow' : 'red';
    }

    /**
     * @return array<int, \MauticPlugin\DealFlowBundle\Entity\DealTask>
     */
    public function getOverdueTasks(): array
    {
        return $this->overdueTasks;
    }

    /**
     * @return array<int, \MauticPlugin\DealFlowBundle\Entity\DealTask>
     */
    public function getOpenTasks(): array
    {
        return $this->openTasks;
    }

    /**
     * @return array<int, \MauticPlugin\DealFlowBundle\Entity\DealTask>
     */
    public function getCompletedTasks(): array
    {
        return $this->completedTasks;
    }

    /**
     * Summary of health signals as a structured array.
     *
     * @return array{updatedDaysAgo: int, updatedColor: string, daysToClose: ?int, closeColor: string, overdueCount: int, overdueColor: string, confidenceColor: string}
     */
    public function getHealthSummary(): array
    {
        return [
            'updatedDaysAgo' => $this->getUpdatedDaysAgo(),
            'updatedColor' => $this->getUpdatedColor(),
            'daysToClose' => $this->getDaysToClose(),
            'closeColor' => $this->getCloseColor(),
            'overdueCount' => $this->getOverdueCount(),
            'overdueColor' => $this->getOverdueColor(),
            'confidenceColor' => $this->getConfidenceColor(),
        ];
    }

    /**
     * Index of the current stage within the pipeline stage list.
     * Returns -1 if no current stage or stage not found.
     *
     * @param array<int, \MauticPlugin\DealFlowBundle\Entity\DealStage> $stages
     */
    public function getCurrentStageIndex(array $stages): int
    {
        $currentStageId = $this->deal->getStage()?->getId();
        if (null === $currentStageId) {
            return -1;
        }

        foreach ($stages as $index => $stage) {
            if ($stage->getId() === $currentStageId) {
                return $index;
            }
        }

        return -1;
    }

    private function categorizeTasks(): void
    {
        foreach ($this->deal->getTasks() as $task) {
            if ('completed' === $task->getStatus()) {
                $this->completedTasks[] = $task;
            } else {
                $this->openTasks[] = $task;
                if (null !== $task->getDueAt() && $task->getDueAt() < $this->now) {
                    $this->overdueTasks[] = $task;
                }
            }
        }
    }
}
