<?php

namespace App\Agent\Tools\Campaign;

use App\Agent\Workspace;
use App\Campaigns\CampaignImporter;
use App\Models\Campaign;
use App\Services\Meta\MetaException;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;
use Throwable;

/**
 * Open a campaign so it can be worked on.
 *
 * Whether it was built here or somewhere else is our problem, not the user's:
 * they asked to edit a campaign on an account they administer. Reading it and
 * recording a baseline to diff against happens underneath, and nothing about
 * that belongs in a reply.
 *
 * Not gated for approval: it only reads, and writes locally. The gate sits
 * where it belongs, on pushing a change back.
 */
class OpenCampaign implements Tool
{
    public function __construct(
        private readonly Workspace $workspace,
        private readonly CampaignImporter $importer,
    ) {}

    public function description(): Stringable|string
    {
        return 'Open a campaign on a Meta or configured Google ad account so it can be edited here, whether or not it '
            .'was built here. Reads its current settings and changes nothing on the platform. '
            .'Use list_campaigns first to find the id.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'campaign_id' => $schema->string()
                ->description('The campaign id, as shown by list_campaigns.')
                ->required(),
            'platform' => $schema->string()
                ->description('meta or google; defaults to meta.'),
        ];
    }

    /**
     * The budget as the campaign actually holds it.
     *
     * An adopted campaign can be on a lifetime budget, held on the campaign
     * rather than the ad set. Reporting a bare number reads as daily, and the
     * first thing the user does with an opened campaign is change it.
     */
    private function budget(Campaign $campaign): string
    {
        if (! $campaign->budget) {
            return 'not set';
        }

        return sprintf(
            '%s%s %s, on the %s',
            $campaign->currency ? $campaign->currency.' ' : '',
            $campaign->budget,
            $campaign->usesLifetimeBudget() ? 'in total' : 'a day',
            $campaign->usesCampaignBudget() ? 'campaign' : 'ad set',
        );
    }

    public function handle(Request $request): Stringable|string
    {
        $campaignId = trim((string) ($request->all()['campaign_id'] ?? ''));
        $platform = strtolower(trim((string) ($request->all()['platform'] ?? 'meta')));

        if ($campaignId === '') {
            return 'error: a campaign id is needed.';
        }

        if (! in_array($platform, ['meta', 'google'], true)) {
            return 'error: choose Meta or Google.';
        }

        if (! $owner = $this->workspace->owner()) {
            return 'error: no signed-in user.';
        }

        try {
            $campaign = $platform === 'google'
                ? $this->importer->adoptGoogle($campaignId, $owner)
                : $this->importer->adopt($campaignId, $owner);
        } catch (MetaException $e) {
            return 'error: could not read that campaign: '.$e->getMessage();
        } catch (Throwable $e) {
            report($e);

            return 'error: could not open that campaign: '.$e->getMessage();
        }

        $this->workspace->produced($campaign);
        $this->workspace->focusOn($campaign);

        $ids = (array) ($campaign->external_ids ?? []);
        $others = $ids['other_adset_ids'] ?? [];

        return sprintf(
            'Opened "%s" (%s). Budget %s, bidding %s, running from %s.%s '
            .'Nothing has been changed. Say what you want different and it will be shown before anything is sent.',
            $campaign->name,
            $campaignId,
            $this->budget($campaign),
            $campaign->bid_strategy ?: 'not set',
            $campaign->starts_at?->toDateString() ?? 'not set',
            $others
                ? sprintf(' It has %d other ad set(s); edits here apply to the first one only.', count($others))
                : '',
        );
    }
}
