<?php

namespace App\Agent\Tools\Campaign;

use App\Agent\Choices;
use App\Agent\Tools\Campaign\Concerns\ActsOnFocusedCampaign;
use App\Agent\Workspace;
use App\Models\ArtifactFieldSource;
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 Instagram account an ad runs as.
 *
 * Instagram has been offered as a placement from the start and nothing ever
 * attached an account, so those ads ran under the Facebook Page's name and
 * picture. Meta permits it, which is why nothing failed and nobody noticed.
 *
 * Only asked when an Instagram placement was actually chosen: for a
 * Facebook-only campaign this is not an outstanding step, it is not a step.
 */
class ChooseInstagramAccount implements Tool
{
    use ActsOnFocusedCampaign;

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

    public function description(): Stringable|string
    {
        return 'List the Instagram accounts the ads could run as, or attach one to the campaign '
            .'in focus by passing instagram_account_id. Only relevant when Instagram is one of '
            .'the placements. Never invent an account id.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [
            'instagram_account_id' => $schema->string()
                ->description('Attach this Instagram account. Omit to list what is available.'),
        ];
    }

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

        if (blank($campaign->ad_account_id)) {
            return $this->json([
                'ok' => false,
                'error' => 'Verify the ad account first; Instagram accounts are account scoped.',
            ]);
        }

        try {
            $accounts = $this->meta->on($campaign->id, 'agent')
                ->instagramAccounts($campaign->ad_account_id, $campaign->page_id);
        } catch (MetaException $e) {
            return $this->json(['ok' => false, 'error' => $e->getMessage()]);
        }

        // Nothing to pick from, and that is a real answer rather than a
        // failure. The ads still run, under the Page.
        if ($accounts === []) {
            // Recorded as answered, or the flow would ask again every turn and
            // an advertiser with no Instagram handle could never finish. The
            // source goes down with it: the panel reads a bare value with no
            // source as "not set", so this showed "runs under the Page" next to
            // a grey dot saying nobody had decided anything.
            $campaign->update(['instagram_account_id' => '']);
            $this->workspace->recordSource($campaign, 'instagram_account_id', ArtifactFieldSource::PLATFORM_RETURNED);

            return $this->json([
                'ok' => true,
                'instagram_accounts' => [],
                // Says what to do about it, not only what happened.
                //
                // "No Instagram account is available" reads as a missing
                // feature, and Kaushal asked for the option as though it did
                // not exist. It does; the Page simply has none linked, and
                // that is fixed in Page settings rather than here.
                'note' => 'There is no Instagram account on this ad account or Page. Tell the user, in one '
                    .'sentence, that the Instagram placements will run under the Page instead, and that an '
                    .'Instagram account can be offered here once one is linked to the Page in Page settings. '
                    .'Then move on.',
            ]);
        }

        $arguments = $request->all();

        // Key presence, not blankness: an empty string is "run under the Page
        // instead", which has to be storable rather than re-asked.
        if (! array_key_exists('instagram_account_id', $arguments)) {
            $this->choices->offer(
                field: 'instagram_account_id',
                question: 'Which Instagram account should the ads run as?',
                options: [
                    ...array_map(fn (array $account): array => [
                        'value' => $account['id'],
                        'label' => $account['username'] ? '@'.$account['username'] : $account['id'],
                        'hint' => $account['source'],
                        // The account already tied to the Page is the one almost
                        // everyone means, so it is marked rather than merely listed.
                        'badge' => $account['source'] === 'connected to the Page' ? 'Suggested' : null,
                        'sends' => 'Run the Instagram ads as '.($account['username'] ? '@'.$account['username'] : $account['id']),
                    ], $accounts),
                    [
                        'value' => '',
                        'label' => 'Use the Page instead',
                        'hint' => 'The Instagram ads show the Facebook Page name and picture.',
                        'sends' => 'Run the Instagram ads under the Page, with no Instagram account',
                    ],
                ],
            );

            return $this->json([
                'ok' => true,
                'instagram_accounts' => $accounts,
                'note' => 'The accounts are on screen for the user to pick. Say one short line and wait.',
            ]);
        }

        $chosen = (string) $arguments['instagram_account_id'];

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

            return $this->json([
                'ok' => true,
                'instagram_account_id' => null,
                'note' => 'Recorded. The Instagram placements will run under the Page.',
            ]);
        }

        $match = collect($accounts)->firstWhere('id', $chosen);

        if (! $match) {
            return $this->json([
                'ok' => false,
                'error' => "Instagram account {$chosen} is not one this ad account can run as.",
                'instagram_accounts' => $accounts,
            ]);
        }

        $campaign->update(['instagram_account_id' => $match['id']]);
        $this->workspace->recordSource($campaign, 'instagram_account_id', ArtifactFieldSource::PLATFORM_RETURNED);

        return $this->json([
            'ok' => true,
            'instagram_account_id' => $match['id'],
            'username' => $match['username'],
        ]);
    }
}
