<?php

namespace App\Agent\Tools\Campaign;

use App\Agent\Tools\Campaign\Concerns\ActsOnFocusedCampaign;
use App\Agent\Workspace;
use App\Campaigns\CampaignUpdater;
use App\Campaigns\PendingChanges;
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;

/**
 * Send pending changes to the live campaign.
 *
 * Editing a running campaign changes what is being spent, so it goes through
 * the same lock as publishing: the run pauses, the user approves, and only then
 * does anything reach the platform. Refusals come back as results the model can
 * explain rather than exceptions that end the turn.
 */
class PushChanges implements Approvable, Tool
{
    use ActsOnFocusedCampaign, InteractsWithApprovals;

    public function __construct(
        private readonly Workspace $workspace,
        private readonly PendingChanges $pending,
        private readonly CampaignUpdater $updater,
    ) {}

    public function description(): Stringable|string
    {
        return 'Push pending changes to the live campaign on the platform. Only call this when '
            .'the user has asked for the change to take effect. This always asks them to approve first.';
    }

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

    /** Always. Changing live spend is never done on the model's say-so alone. */
    protected function needsApproval(Request $request): bool
    {
        return true;
    }

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

        if (! $campaign->isLive()) {
            return $this->json([
                'ok' => false,
                'error' => 'This campaign has not been published yet, so there is nothing to push.',
            ]);
        }

        if ($rebuild = $this->pending->requiringRebuild($campaign)) {
            return $this->json([
                'ok' => false,
                'error' => 'Some changes cannot be applied to a live campaign.',
                'reasons' => $rebuild,
                'note' => 'Say this plainly. Do not imply the change took effect.',
            ]);
        }

        $change = $this->updater->apply(
            $this->updater->propose($campaign, 'chat'),
            'chat',
        );

        if ($change->state !== 'applied') {
            return $this->json(['ok' => false, 'error' => $change->error]);
        }

        return $this->json([
            'ok' => true,
            'applied' => $change->changes,
            'note' => 'The live campaign now matches what we hold.',
        ]);
    }
}
