<?php

namespace App\Agent\Tools\Campaign;

use App\Agent\Tools\Campaign\Concerns\ActsOnFocusedCampaign;
use App\Agent\Workspace;
use App\Models\Asset;
use App\Models\AssetPlatformRef;
use App\Services\Meta\Meta;
use App\Services\Meta\MetaException;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Illuminate\Support\Facades\Storage;
use Laravel\Ai\Contracts\Tool;
use Laravel\Ai\Tools\Request;
use Stringable;

/**
 * List the assets attached to this conversation and push any that are not yet
 * on the ad account.
 *
 * Lists and uploads in one call on purpose. Splitting them led to the agent
 * asking the user to upload something they had already uploaded, because
 * listing alone left it unsure whether the work was done.
 *
 * Uploading goes through Graph rather than the MCP: every MCP upload tool is
 * still gated on our accounts, and its local-file variant is interactive by
 * design.
 */
class ReviewAssets implements Tool
{
    use ActsOnFocusedCampaign;

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

    public function description(): Stringable|string
    {
        return 'List the creatives attached to this conversation and upload any that are not '
            .'yet on the ad account. Always call this before asking the user to upload anything.';
    }

    public function schema(JsonSchema $schema): array
    {
        return [];
    }

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

        $assets = $this->workspace->artifacts(Asset::class)
            ->map(fn ($link) => $link->artifactable)
            ->filter()
            ->values();

        if ($assets->isEmpty()) {
            return $this->json([
                'ok' => true,
                'attached' => 0,
                'note' => 'No creative has been attached yet. Ask the user to upload one.',
            ]);
        }

        if (blank($campaign->ad_account_id)) {
            return $this->json([
                'ok' => false,
                'error' => 'Verify the ad account before uploading; creatives are uploaded per account.',
                'attached' => $assets->count(),
            ]);
        }

        $meta = $this->meta->on($campaign->id, 'agent');
        $ready = [];
        $justUploaded = [];
        $failed = [];

        foreach ($assets as $asset) {
            if ($existing = $asset->refFor($campaign->platform, $campaign->ad_account_id)?->reference()) {
                $ready[] = ['asset_id' => $asset->id, 'name' => $asset->original_name, 'reference' => $existing];

                continue;
            }

            try {
                $path = Storage::disk('local')->path($asset->path);

                $reference = $asset->kind === 'video'
                    ? $meta->uploadVideo($campaign->ad_account_id, $path, (string) $asset->original_name)
                    : $meta->uploadImage($campaign->ad_account_id, $path, (string) $asset->original_name);

                AssetPlatformRef::updateOrCreate(
                    [
                        'asset_id' => $asset->id,
                        'platform' => $campaign->platform,
                        'ad_account_id' => $campaign->ad_account_id,
                    ],
                    $asset->kind === 'video'
                        ? ['external_id' => $reference]
                        : ['external_hash' => $reference],
                );

                $justUploaded[] = ['asset_id' => $asset->id, 'name' => $asset->original_name, 'reference' => $reference];
            } catch (MetaException $e) {
                $failed[] = ['asset_id' => $asset->id, 'name' => $asset->original_name, 'error' => $e->getMessage()];
            }
        }

        return $this->json(array_filter([
            'ok' => $failed === [],
            'attached' => $assets->count(),
            'already_on_account' => $ready ?: null,
            'just_uploaded' => $justUploaded ?: null,
            'failed' => $failed ?: null,
        ], fn ($v) => $v !== null));
    }
}
