<?php

namespace App\Agent\Tools\Campaign;

use App\Agent\Tools\Campaign\Concerns\ActsOnFocusedCampaign;
use App\Agent\Workspace;
use App\Campaigns\Publisher;
use App\Support\Actor;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Concerns\InteractsWithApprovals;
use Laravel\Ai\Contracts\Approvable;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;
use Throwable;

/**
 * Start a published campaign spending, from the chat.
 *
 * The second of the two tools that can spend money, and the one that actually
 * starts it. PublishCampaign builds a campaign and parks it; this is what makes
 * it deliver, and it pauses for a human on its own account.
 *
 * Approved separately from publishing on purpose. One approval covering both
 * would make "created paused" a description of an intermediate state nobody ever
 * sees rather than a decision point, and the reason to build paused at all is that
 * somebody looks before money moves.
 *
 * Every level goes live together - Meta's ad set and ads, Google's ad group and
 * ad, LinkedIn's group and creatives - because a live campaign over paused
 * children delivers nothing and reports it as a campaign with no impressions,
 * which is indistinguishable from one that is simply not winning auctions.
 */
class ActivateCampaign implements Approvable, Tool
{
    // The trait supplies the approval methods; the interface is what the run loop
    // tests for. With the trait alone a tool looks gated and executes straight
    // through, which is how publishing once ran with no pause at all.
    use ActsOnFocusedCampaign, InteractsWithApprovals;

    public function __construct(
        private readonly Workspace $workspace,
        private readonly Publisher $publisher,
        private readonly Actor $actor,
    ) {}

    public function description(): Stringable|string
    {
        return 'Start the campaign in focus so it begins spending. Only for a campaign that has '
            .'already been published; publishing creates everything paused. This always asks the '
            .'user to approve first. Only call it when they have clearly asked to start it.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [];
    }

    /** Always. Money starting to move is never done on the model's say-so. */
    protected function needsApproval(Request $request): bool
    {
        return true;
    }

    public function handle(Request $request): Stringable|string
    {
        if (! $campaign = $this->focused()) {
            return $this->noCampaign();
        }

        if ($blocking = $this->publisher->activationProblems($campaign)) {
            return $this->json([
                'ok' => false,
                'running' => $campaign->isRunning(),
                'error' => 'This campaign cannot be started: '.implode('; ', $blocking),
                'still_needed' => $blocking,
            ]);
        }

        try {
            $decision = $this->publisher->activate(
                $this->publisher->proposeActivation($campaign, $this->actor->describe()),
                $this->actor->describe(),
            );
        } catch (Throwable $e) {
            // Reported as a result rather than thrown, so the model can explain it
            // instead of the turn dying. Nothing is rolled back: activation walks
            // upward from the deepest object, so a failure leaves the campaign
            // itself still paused and nothing spending.
            return $this->json([
                'ok' => false,
                'running' => false,
                'error' => $e->getMessage(),
                'note' => 'The campaign is still paused and is not spending. Say that plainly.',
            ]);
        }

        if (! $decision->wasApplied()) {
            return $this->json([
                'ok' => false,
                'running' => false,
                'error' => $decision->reason,
            ]);
        }

        $campaign = $campaign->fresh();

        return $this->json([
            'ok' => true,
            'running' => true,
            'campaign' => $campaign->name,
            'platform' => $campaign->platform,
            'external_campaign_id' => $campaign->external_campaign_id,
            'activated_at' => $campaign->activated_at?->toIso8601String(),
            'note' => 'It is now live and spending. Say so in one line with the budget, and mention '
                .'they can stop it here at any time.',
        ]);
    }
}
