<?php

namespace App\Agent\Tools\Campaign;

use App\Agent\Choices;
use App\Agent\Tools\Campaign\Concerns\ActsOnFocusedCampaign;
use App\Agent\Workspace;
use App\Campaigns\Publisher;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;

/**
 * Ask for a published campaign to start running.
 *
 * Everything this product builds is created paused: Meta and Google PAUSED at
 * every level, LinkedIn DRAFT. Publishing assembles a campaign on the account and
 * stops there. Nothing spends until a buyer asks for it here and approves it.
 *
 * A request, not an act, for the same reason RequestPublish is one - and approved
 * separately from the publish, which was a deliberate decision rather than an
 * inherited pattern. Publishing and spending are different things to agree to,
 * and if one approval did both then building paused would be decoration.
 *
 * There was no way to do this at all until now. Campaigns were published paused,
 * correctly, and the only way to start one was the platform's own interface -
 * so the product could stop money being spent and not start it, which is half a
 * feature and the wrong half to ship on its own.
 */
class RequestActivation implements Tool
{
    use ActsOnFocusedCampaign;

    public function __construct(
        private readonly Workspace $workspace,
        private readonly Publisher $publisher,
        private readonly Choices $choices,
    ) {}

    public function description(): Stringable|string
    {
        return 'Ask the user to approve starting the campaign in focus, so it begins spending. '
            .'Only for a campaign that has already been published - publishing creates it paused. '
            .'You cannot start a campaign yourself; only the user can approve it.';
    }

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

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

        if (! $campaign->isPublished()) {
            return $this->json([
                'ok' => false,
                'error' => 'This campaign has not been published yet, so there is nothing on '
                    .ucfirst($campaign->platform).' to start.',
                'note' => 'Publish it first with request_publish. Do not describe this as a failure to '
                    .'start; it has not been built yet.',
            ]);
        }

        if ($campaign->isRunning()) {
            return $this->json([
                'ok' => false,
                'error' => sprintf(
                    'This campaign has been running since %s.',
                    $campaign->activated_at->toDayDateTimeString(),
                ),
                'note' => 'Say so in one line. Do not offer to start it again.',
            ]);
        }

        // Asked, not proposed. Creating a decision row to find out whether one
        // could be created leaves a blocked decision behind every question.
        if ($blocking = $this->publisher->activationProblems($campaign)) {
            return $this->json([
                'ok' => false,
                'error' => 'This campaign cannot be started: '.implode('; ', $blocking),
                'still_needed' => $blocking,
                'note' => 'Tell the user what is wrong and help with the first item. It is already '
                    .'published, so do not offer to publish it again.',
            ]);
        }

        $this->choices->offer(
            field: 'activation_approval',
            question: 'Start this campaign? It will begin spending.',
            options: [
                [
                    'value' => 'approve',
                    'label' => 'Start campaign',
                    'hint' => $this->spendHint($campaign),
                    'sends' => 'Yes, start the campaign.',
                ],
                [
                    'value' => 'reject',
                    'label' => 'Leave it paused',
                    'hint' => 'It stays built on '.ucfirst($campaign->platform).' and spends nothing',
                    'sends' => 'No, leave it paused for now.',
                ],
            ],
            multiple: false,
            allowOther: false,
        );

        return $this->json([
            'ok' => true,
            'summary' => [
                'campaign' => $campaign->name,
                'platform' => $campaign->platform,
                'external_campaign_id' => $campaign->external_campaign_id,
                'budget' => $campaign->budget,
                'currency' => $campaign->currency,
                'budget_is' => $campaign->usesLifetimeBudget()
                    ? 'a total for the whole run'
                    : 'per day',
                'starts_at' => $campaign->starts_at?->toDateString(),
                'ends_at' => $campaign->ends_at?->toDateString(),
                'ads' => $campaign->ads()->count(),
            ],
            // What is about to change, said plainly, because this is the only
            // moment in the product where money starts moving.
            'note' => 'Say what this will cost, per day or in total, and that everything under the '
                .'campaign starts with it. The options are on screen; say one short line and wait. '
                .'Do not claim it has started.',
        ]);
    }

    /**
     * What starting it will cost, in one phrase.
     *
     * On the button, because a hint that says "this begins spending" without a
     * number is asking somebody to agree to an amount they have to go and look up.
     */
    private function spendHint($campaign): string
    {
        if (blank($campaign->budget)) {
            return 'Begins delivering on '.ucfirst($campaign->platform);
        }

        return sprintf(
            'Begins spending %s %s %s',
            $campaign->currency ?: '',
            $campaign->budget,
            $campaign->usesLifetimeBudget() ? 'in total' : 'a day',
        );
    }
}
