<?php

namespace App\Domain\Outbound\Actions;

use App\Domain\Outbound\Connectors\DummyConnector;
use App\Domain\Outbound\Enums\OutboundActionStatus;
use App\Domain\Outbound\Events\OutboundSending;
use App\Domain\Outbound\Events\OutboundSent;
use App\Domain\Outbound\Exceptions\BlacklistedException;
use App\Domain\Outbound\Exceptions\RateLimitExceededException;
use App\Domain\Outbound\Managers\OutboundConnectorManager;
use App\Domain\Outbound\Middleware\ChannelRateLimit;
use App\Domain\Outbound\Middleware\TargetRateLimit;
use App\Domain\Outbound\Models\OutboundAction;
use App\Domain\Outbound\Models\OutboundProposal;
use Illuminate\Support\Facades\Log;

class SendOutboundAction
{
    public function __construct(
        private readonly CheckBlacklistAction $checkBlacklist,
        private readonly ChannelRateLimit $channelRateLimit,
        private readonly TargetRateLimit $targetRateLimit,
        private readonly OutboundConnectorManager $connectorManager,
    ) {}

    public function execute(OutboundProposal $proposal): OutboundAction
    {
        // Check blacklist
        $blacklistResult = $this->checkBlacklist->execute($proposal);
        if ($blacklistResult['blocked']) {
            throw new BlacklistedException($blacklistResult['reason']);
        }

        // Check channel rate limit
        if (! $this->channelRateLimit->check($proposal)) {
            throw new RateLimitExceededException(
                "Channel rate limit exceeded for {$proposal->channel->value}",
            );
        }

        // Check target rate limit
        if (! $this->targetRateLimit->check($proposal)) {
            throw new RateLimitExceededException(
                'Target rate limit exceeded — contact cooldown active',
            );
        }

        // Plugin hook: allow plugins to cancel outbound delivery
        $sending = new OutboundSending($proposal);
        event($sending);
        if ($sending->cancel) {
            throw new BlacklistedException($sending->cancelReason ?? 'Cancelled by plugin');
        }

        $channel = $proposal->channel->value;
        $connector = $this->connectorManager->connectorFor($channel);

        if ($connector instanceof DummyConnector) {
            Log::warning("No outbound connector configured for channel '{$channel}' — delivery will be skipped");
        }

        $action = $connector->send($proposal);

        // Plugin hook: notify plugins of delivery result
        event(new OutboundSent($proposal, $action, $action->status === OutboundActionStatus::Sent));

        return $action;
    }
}
