<?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\Campaigns\TrackedUrl;
use App\Models\ArtifactFieldSource;
use App\Models\Campaign;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;

/**
 * The tracking parameters on the destination URL.
 *
 * Every ad published so far went to a bare landing page, so nothing downstream
 * could tell a click came from Meta, from which campaign, or from which ad.
 *
 * Asked before publish rather than left to be fixed later: Meta treats an ad's
 * creative as immutable, so changing the destination URL afterwards means a new
 * creative and a new ad, and the reporting for what already ran stays wrong.
 */
class SetTracking implements Tool
{
    use ActsOnFocusedCampaign;

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

    public function description(): Stringable|string
    {
        return 'Set the UTM tracking parameters appended to the destination URL of the '
            .'campaign in focus. Call with no argument to offer the usual ones. Pass an '
            .'empty string to publish with no tracking at all.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'utm' => $schema->string()
                ->description('The query string to append, without a leading ? or &, for example '
                    .'utm_source=facebook&utm_medium=paid_social. Meta macros in double braces are '
                    .'filled in by Meta. Omit to offer the usual ones; pass "" for no tracking.'),
        ];
    }

    /**
     * The usual taggings, plus none.
     *
     * The macros are Meta's and are filled in at click time, so one string
     * works for every campaign and nobody has to retype the campaign name into
     * its own tracking.
     */
    private function offerTagging(Campaign $campaign): void
    {
        $this->choices->offer(
            field: 'utm',
            question: 'How should clicks be tagged?',
            options: [
                // The platform's, because the macros are the platform's. This
                // used to be one config string with Meta's utm_source=facebook
                // and its {{campaign.name}} macros, offered on Google too.
                ...app(Platforms::class)->for($campaign)->trackingOptions(),
                [
                    'value' => '',
                    'label' => 'No tracking',
                    'hint' => 'The landing page gets no parameters. Clicks cannot be attributed.',
                    'sends' => 'Publish with no tracking parameters',
                ],
            ],
        );
    }

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

        $arguments = $request->all();

        // Key presence, not blankness: an empty string is the answer "none",
        // and has to be storable rather than re-asked.
        if (! array_key_exists('utm', $arguments)) {
            $this->offerTagging($campaign);

            return $this->json([
                'ok' => true,
                'note' => 'The tagging options are on screen. Say one short line and wait.',
            ]);
        }

        $utm = trim((string) $arguments['utm'], " \t\n\r\0\x0B?&");

        if ($utm !== '' && ! preg_match('/^[^=&\s]+=[^&]*(&[^=&\s]+=[^&]*)*$/', $utm)) {
            return $this->json([
                'ok' => false,
                'error' => 'Tracking parameters must be key=value pairs joined by &, with no spaces, '
                    .'for example utm_source=facebook&utm_medium=paid_social.',
            ]);
        }

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

        return $this->json(array_filter([
            'ok' => true,
            'campaign_id' => $campaign->id,
            'utm' => $utm,
            // Shown with the macros filled in, because {{campaign.name}} in a
            // summary tells nobody whether the tagging is right.
            'destination_url' => $campaign->landing_url ? TrackedUrl::asExample($campaign->fresh()) : null,
            'note' => $utm === ''
                ? 'Recorded as no tracking. Say so plainly rather than treating it as an oversight.'
                : null,
        ], fn (mixed $value): bool => $value !== null));
    }
}
