<?php

declare(strict_types=1);

namespace MauticPlugin\DealFlowBundle\Model;

use Mautic\CoreBundle\Model\FormModel;
use MauticPlugin\DealFlowBundle\Compat\GlobalSearchCompat;
use MauticPlugin\DealFlowBundle\Entity\Deal;
use MauticPlugin\DealFlowBundle\Entity\DealRepository;
use MauticPlugin\DealFlowBundle\Entity\DealStage;
use MauticPlugin\DealFlowBundle\Form\Type\DealType;
use MauticPlugin\DealFlowBundle\Service\DealManager;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\Form\FormInterface;

/**
 * Thin FormModel wrapper for Deal entity.
 *
 * Provides Mautic's standard getModel('dealflow.deal') retrieval pattern,
 * automatic timestamp/user tracking via FormModel::setTimestamps(),
 * and publish toggle support. Complex deal operations remain in DealManager.
 *
 * @extends FormModel<Deal>
 */
class DealModel extends FormModel implements GlobalSearchCompat
{
    private DealManager $dealManager;
    private DealRepository $dealRepository;

    /**
     * Captured before form handling so DealManager can detect changes
     * for event dispatch (stage change, owner change, etc.).
     *
     * @var array{previousStage: ?DealStage, previousOwnerId: ?int, previousCompanyId: ?int, previousStatus: ?string}|null
     */
    private ?array $previousState = null;

    public function setDealManager(DealManager $dealManager): void
    {
        $this->dealManager = $dealManager;
    }

    public function getDealManager(): DealManager
    {
        return $this->dealManager;
    }

    public function setDealRepository(DealRepository $dealRepository): void
    {
        $this->dealRepository = $dealRepository;
    }

    public function getRepository(): DealRepository
    {
        return $this->dealRepository;
    }

    /**
     * Capture the deal's previous state before form handling so that
     * saveEntity() can pass it to DealManager for change-detection events.
     *
     * Call this BEFORE form->handleRequest() in the controller.
     */
    public function setPreviousState(
        ?DealStage $previousStage,
        ?int $previousOwnerId,
        ?int $previousCompanyId,
        ?string $previousStatus,
    ): void {
        $this->previousState = [
            'previousStage' => $previousStage,
            'previousOwnerId' => $previousOwnerId,
            'previousCompanyId' => $previousCompanyId,
            'previousStatus' => $previousStatus,
        ];
    }

    /**
     * Save a Deal via FormModel (automatic timestamp/user tracking)
     * then delegate to DealManager for business logic (events, audit, stage sync).
     *
     * @param Deal $entity
     * @param bool $unlock
     */
    public function saveEntity($entity, $unlock = true): void
    {
        if (!$entity instanceof Deal) {
            throw new \InvalidArgumentException('Entity must be an instance of Deal.');
        }

        $isNew = $this->isNewEntity($entity);

        // FormModel sets dateAdded/createdBy (new) and dateModified/modifiedBy (edit)
        // from the session user — no manual actorId passing required.
        $this->setTimestamps($entity, $isNew, $unlock);

        $actorId = $this->userHelper->getUser()?->getId();

        // Delegate to DealManager for persist, flush, audit, and domain events.
        $this->dealManager->saveDealEntity(
            $entity,
            is_int($actorId) ? $actorId : null,
            'ui',
            $this->previousState['previousStage'] ?? null,
            $this->previousState['previousOwnerId'] ?? null,
            $this->previousState['previousCompanyId'] ?? null,
            $this->previousState['previousStatus'] ?? null,
        );

        // Reset previous state after save so it doesn't leak to the next call.
        $this->previousState = null;
    }

    /**
     * @param Deal $entity
     */
    public function deleteEntity($entity): void
    {
        if (!$entity instanceof Deal) {
            throw new \InvalidArgumentException('Entity must be an instance of Deal.');
        }

        // FormModel delete: dispatches pre_delete/post_delete and removes entity.
        parent::deleteEntity($entity);
    }

    public function getPermissionBase(): string
    {
        return 'dealflow:deals';
    }

    /**
     * @param Deal|null $entity
     * @param string|null $action
     * @param array<string, mixed> $options
     */
    public function createForm($entity, FormFactoryInterface $formFactory, $action = null, $options = []): FormInterface
    {
        if (!$entity instanceof Deal) {
            throw new \InvalidArgumentException('Entity must be an instance of Deal.');
        }

        if ($action) {
            $options['action'] = $action;
        }

        return $formFactory->create(DealType::class, $entity, $options);
    }

    /**
     * @param int|null $id
     */
    public function getEntity($id = null): ?Deal
    {
        if (null === $id) {
            return new Deal();
        }

        return $this->getRepository()->find($id);
    }

    /**
     * @return class-string<Deal>
     */
    public function getEntityName(): string
    {
        return Deal::class;
    }
}
