<?php

namespace App\Agent\Tools\Campaign;

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

/**
 * Offer the campaign types this platform sells, or set the one chosen.
 *
 * Called with no type it offers; called with one it sets. The same shape as
 * choose_objective, and for the same reason: the options come from the
 * platform's own rules, so a button can never offer a type the platform would
 * reject.
 *
 * One is marked as suggested rather than applied, and which one depends on the
 * campaign: the rules read its objective and what it already holds, so an
 * awareness campaign is pointed at Display rather than at the search network,
 * which sells intent and no reach at all.
 *
 * Suggested, never set. Picking Display commits the buyer to finding creatives
 * at two specific sizes, so that is not a choice to make on their behalf.
 *
 * Does nothing at all where the platform has no types, which is how Meta never
 * sees this question.
 */
class ChooseCampaignType implements Tool
{
    use ActsOnFocusedCampaign;

    public function __construct(
        private readonly Workspace $workspace,
        private readonly Platforms $platforms,
        private readonly Choices $choices,
    ) {}

    public function description(): Stringable|string
    {
        return 'Set the campaign type on the campaign in focus, or call with no type to show the '
            .'user the types this platform sells so they can pick one. Only some platforms have '
            .'types; calling this where there are none is a no-op. Prefer offering over asking in prose.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            // The values are the platform's, so they are not listed here. Naming
            // one platform's set would tell the model to send it to all of them.
            'campaign_type' => $schema->string()
                ->description('The chosen campaign type. Omit to offer the supported types instead.'),
        ];
    }

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

        $rules = $this->platforms->for($campaign);
        $supported = $rules->campaignTypes();

        if ($supported === []) {
            return $this->json([
                'ok' => true,
                'note' => sprintf('%s has no campaign types to choose between. Move on.', $rules->name()),
            ]);
        }

        $chosen = strtolower(trim((string) ($request->all()['campaign_type'] ?? '')));

        if ($chosen === '') {
            // Read from the campaign rather than fixed on Search. The reason
            // travels with it so the option says why, the same way the
            // objective drawn from the landing page does.
            $suggested = $rules->suggestedCampaignType($campaign);

            $this->choices->offer(
                field: 'campaign_type',
                question: 'What kind of Google campaign should this be?',
                options: array_map(function (string $type) use ($rules, $suggested): array {
                    ['label' => $label, 'hint' => $hint] = $rules->describeCampaignType($type);
                    $isSuggested = $type === ($suggested['type'] ?? null);

                    return [
                        'value' => $type,
                        'label' => $label,
                        'hint' => $isSuggested
                            ? trim($hint.' Suggested because '.$suggested['because'].'.')
                            : $hint,
                        'badge' => $isSuggested ? 'Suggested' : null,
                        'sends' => 'Make this a '.$label.' campaign',
                    ];
                }, $supported),
            );

            return $this->json([
                'ok' => true,
                'offered' => $supported,
                'suggested' => $suggested['type'] ?? null,
                'note' => 'The types are on screen for the user to pick. Say one short line and wait; '
                    .'do not list them again in prose, and do not choose one for them.',
            ]);
        }

        if (! in_array($chosen, $supported, true)) {
            return $this->json([
                'ok' => false,
                'error' => sprintf('%s does not sell a %s campaign.', $rules->name(), $chosen),
                'supported' => $supported,
            ]);
        }

        $campaign->update(['campaign_type' => $chosen]);
        $this->workspace->recordSource($campaign, 'campaign_type', ArtifactFieldSource::USER_STATED);

        return $this->json([
            'ok' => true,
            'campaign_id' => $campaign->id,
            'campaign_type' => $chosen,
            'note' => $rules->describeCampaignType($chosen)['hint'],
        ]);
    }
}
