<?php

namespace App\Providers;

use App\Agent\Choices;
use App\Agent\Instructions;
use App\Agent\SummarisingConversationStore;
use App\Agent\ToolRegistry;
use App\Agent\Tools\Campaign\ActivateCampaign;
use App\Agent\Tools\Campaign\AnalyzeLandingPage;
use App\Agent\Tools\Campaign\AttachCreatives;
use App\Agent\Tools\Campaign\ChooseCampaignType;
use App\Agent\Tools\Campaign\ChooseConversionEvent;
use App\Agent\Tools\Campaign\ChooseInstagramAccount;
use App\Agent\Tools\Campaign\ChooseObjective;
use App\Agent\Tools\Campaign\ChoosePage;
use App\Agent\Tools\Campaign\ChoosePixel;
use App\Agent\Tools\Campaign\ChooseTikTokIdentity;
use App\Agent\Tools\Campaign\CreatePixel;
use App\Agent\Tools\Campaign\DeclareAdCategory;
use App\Agent\Tools\Campaign\ImportCreatives;
use App\Agent\Tools\Campaign\ListAdAccounts;
use App\Agent\Tools\Campaign\ListCampaigns;
use App\Agent\Tools\Campaign\OpenCampaign;
use App\Agent\Tools\Campaign\PauseCampaign;
use App\Agent\Tools\Campaign\PublishCampaign;
use App\Agent\Tools\Campaign\PushChanges;
use App\Agent\Tools\Campaign\RequestActivation;
use App\Agent\Tools\Campaign\RequestPublish;
use App\Agent\Tools\Campaign\ReviewAssets;
use App\Agent\Tools\Campaign\ReviewChanges;
use App\Agent\Tools\Campaign\SetBidding;
use App\Agent\Tools\Campaign\SetCampaignBasics;
use App\Agent\Tools\Campaign\SetCampaignBudget;
use App\Agent\Tools\Campaign\SetSchedule;
use App\Agent\Tools\Campaign\SetTargeting;
use App\Agent\Tools\Campaign\SetTracking;
use App\Agent\Tools\Campaign\SplitBudget;
use App\Agent\Tools\Campaign\StartCampaign;
use App\Agent\Tools\Campaign\VerifyAdAccount;
use App\Agent\Tools\Campaign\WriteAdCopy;
use App\Agent\Tools\SwitchFocus;
use App\Agent\Workspace;
use App\Campaigns\Briefs;
use App\Campaigns\PendingChanges;
use Illuminate\Support\ServiceProvider;
use Laravel\Ai\Contracts\ConversationStore;

/**
 * Wires the agent core.
 *
 * The registry is a singleton so any provider can add to it. Other modules
 * should register their own tools from their own provider rather than adding
 * them here:
 *
 *     public function boot(ToolRegistry $registry): void
 *     {
 *         $registry->register('creative', GenerateImage::class, ApproveCreative::class);
 *     }
 *
 * That keeps campaign, creative, landing page and analysis work independent.
 */
class AgentServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Scoped, not singleton: one workspace per request, so a queued job
        // handling a different conversation never inherits another's focus.
        $this->app->scoped(Workspace::class, fn (): Workspace => new Workspace);

        // Scoped for the same reason: choices belong to one turn, and a queued
        // job must not inherit options offered to somebody's browser.
        $this->app->scoped(Choices::class, fn (): Choices => new Choices);

        $this->app->singleton(ToolRegistry::class);

        // Scoped, like the Workspace it reads: it holds the landing page
        // analysis across the window where a new conversation has no id yet,
        // and a second instance would drop what the first was holding.
        $this->app->scoped(Briefs::class);

        $this->app->bind(Instructions::class, fn ($app): Instructions => new Instructions(
            $app->make(Workspace::class),
            $app->make(PendingChanges::class),
        ));

        // The package's store drops anything past its window without saying so.
        // Decorating it keeps the recent turns verbatim and folds the rest into
        // a summary, so a long chat stops re-suggesting what the user rejected.
        $this->app->extend(
            ConversationStore::class,
            fn (ConversationStore $store): SummarisingConversationStore => new SummarisingConversationStore($store),
        );
    }

    public function boot(ToolRegistry $registry): void
    {
        // Focus is core rather than campaign-specific: every module's artifacts
        // can be switched to.
        $registry->register('workspace', SwitchFocus::class);

        // Listed in the order a campaign is usually assembled: the model reads
        // the declarations top to bottom and that ordering nudges the sequence.
        $registry->register(
            'campaign',
            AnalyzeLandingPage::class,
            StartCampaign::class,
            ChooseCampaignType::class,
            ChooseObjective::class,
            SetCampaignBasics::class,
            DeclareAdCategory::class,
            SetCampaignBudget::class,
            SplitBudget::class,
            SetTargeting::class,
            SetBidding::class,
            SetSchedule::class,
            SetTracking::class,
            WriteAdCopy::class,
            ListAdAccounts::class,
            ListCampaigns::class,
            OpenCampaign::class,
            VerifyAdAccount::class,
            ChoosePage::class,
            ChoosePixel::class,
            ChooseTikTokIdentity::class,
            CreatePixel::class,
            ChooseConversionEvent::class,
            ChooseInstagramAccount::class,
            ImportCreatives::class,
            ReviewAssets::class,
            AttachCreatives::class,
            RequestPublish::class,
            PublishCampaign::class,
            // Starting a campaign is its own pair: one to summarise and ask, one
            // that pauses for approval and does it. Publishing builds and parks;
            // nothing in this product spends until these are used.
            RequestActivation::class,
            ActivateCampaign::class,
            PauseCampaign::class,
            ReviewChanges::class,
            PushChanges::class,
        );

        // No `google` namespace.
        //
        // Four Google MCP tools were registered here so the shared chat could
        // reach Google: list_accounts, select_account, list_conversion_actions
        // and list_campaigns. One common internal account removed the need for
        // all four.
        //
        // Listing and selecting an account is a question with one answer when
        // there is one account, and it was only ever asked because Google had
        // been built around a per-user OAuth connection. Conversion actions are
        // the shared choose_pixel tool's job, which stores them on the campaign
        // like Meta's pixel. list_campaigns read the parallel GoogleAdsCampaign
        // table rather than ours, so it answered a question about a different
        // product.
        //
        // The mutating Google tools were never registered and still must not
        // be: publishing goes through a recorded Decision that a human
        // approves, and Publisher is the only thing that touches an account.
    }
}
