<?php

namespace App\Agent\Tools\Campaign;

use App\Services\Meta\Meta;
use App\Services\Meta\MetaException;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;

/**
 * The campaigns on an ad account, whoever built them.
 *
 * The product used to see only what it had made itself, which is a small
 * fraction of what anyone works on. This is how someone finds the campaign
 * they mean.
 *
 * Read only.
 */
class ListCampaigns implements Tool
{
    public function __construct(private readonly Meta $meta) {}

    public function description(): Stringable|string
    {
        return 'List the campaigns on a Meta ad account, including ones not built here. Use it '
            .'when the user asks what is on an account, or names a campaign you do not have.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'ad_account_id' => $schema->string()
                ->description('The ad account to look in, for example act_123456789.')
                ->required(),
            'search' => $schema->string()
                ->description('Optional text to match against the campaign name.'),
        ];
    }

    public function handle(Request $request): Stringable|string
    {
        $arguments = $request->all();
        $adAccountId = trim((string) ($arguments['ad_account_id'] ?? ''));
        $search = trim((string) ($arguments['search'] ?? ''));

        if ($adAccountId === '') {
            return 'error: an ad account id is needed. Call list_ad_accounts first.';
        }

        try {
            $campaigns = collect($this->meta->campaigns($adAccountId));
        } catch (MetaException $e) {
            return 'error: could not read that account: '.$e->getMessage();
        }

        if ($search !== '') {
            $campaigns = $campaigns->filter(
                fn (array $c): bool => str_contains(mb_strtolower((string) ($c['name'] ?? '')), mb_strtolower($search))
            );
        }

        if ($campaigns->isEmpty()) {
            return $search === ''
                ? "No campaigns on {$adAccountId}."
                : "No campaigns on {$adAccountId} matching \"{$search}\".";
        }

        // Capped because an account can hold hundreds and the whole list is
        // neither readable nor useful in a chat reply.
        $shown = $campaigns->take(25);

        return "Campaigns on {$adAccountId}:\n".$shown
            ->map(fn (array $c): string => sprintf(
                '- %s: %s (%s%s)',
                $c['id'] ?? '?',
                $c['name'] ?? 'untitled',
                strtolower((string) ($c['status'] ?? 'unknown')),
                isset($c['objective']) ? ', '.$c['objective'] : '',
            ))
            ->implode("\n")
            .($campaigns->count() > $shown->count()
                ? sprintf("\n\n%d more not shown. Narrow it with search.", $campaigns->count() - $shown->count())
                : '')
            ."\n\nOpen one with open_campaign to edit it.";
    }
}
