@php
    /**
     * The campaign as stored, with where each value came from.
     *
     * Rendered server side from the database rather than from the chat, so what
     * is shown and what would be published can never disagree.
     */
    $dot = fn (?string $source) => match ($source) {
        'user_stated' => ['bg-success', 'you said this'],
        'platform_returned' => ['bg-primary', 'returned by the platform'],
        'model_inferred' => ['bg-warning', 'worked out from context'],
        'fallback' => ['bg-danger', 'guessed, needs confirming'],
        default => ['bg-secondary-subtle', 'not set'],
    };

    $row = function (string $label, $value, ?string $source) use ($dot) {
        [$colour, $title] = $dot($source);

        return [$label, $value, $colour, $title];
    };

    /**
     * What this platform actually has, so the panel stops describing every
     * campaign as a Meta one.
     *
     * A Google campaign was shown a Facebook Page, an Instagram account and a
     * pixel, none of which it can ever hold, all reading "—" forever. An empty
     * row is not neutral: it reads as an unanswered question.
     *
     * Asked of the platform's own rules rather than listed here, so a platform
     * added later gets the right panel by declaring its rules and nothing else.
     */
    // Null for a conversation with no campaign yet, and for any caller that
    // renders this without saying which platform it is looking at, which shows
    // every row rather than silently hiding one.
    $rules ??= null;
    $platforms ??= null;

    /**
     * The platform's own word for a field, taken from its step list.
     *
     * Meta calls it a pixel and Google calls it a conversion action, and each
     * already says so in steps(). Reading the label from there means the panel
     * and the progress list can never drift apart, and neither needs its own
     * table of names.
     */
    $named = function (string $key, string $fallback) use ($rules): string {
        $label = collect($rules?->steps() ?? [])->firstWhere('key', $key)['label'] ?? $fallback;

        return ucfirst($label);
    };

    $has = function (string $field) use ($rules): bool {
        if (! $rules) {
            return true;
        }

        return match ($field) {
            'page' => $rules->requiresPage(),
            'placements' => $rules->placements() !== [],
            'instagram' => in_array('instagram', $rules->placements(), true),
            // No single objective decides it: the row is worth showing if this
            // platform has the concept at all.
            'pixel' => collect($rules->objectives())
                ->contains(fn (string $objective): bool => $rules->requiresPixel($objective)),
            // Asked of the flow, not of conversionEvent(), which LinkedIn also
            // answers: it maps objectives to events for its own publisher while
            // having no step to choose one and no tool that would accept it. A
            // row nothing can ever fill is the permanent grey dot this panel
            // already warns about above.
            'conversion_event' => collect($rules->steps())
                ->contains(fn (array $step): bool => ($step['key'] ?? null) === 'conversion_event'),
            default => true,
        };
    };

    $platformName = function ($key) use ($platforms): string {
        $platforms ??= app(\App\Campaigns\Platforms\Platforms::class);

        return $platforms->supports((string) $key)
            ? $platforms->for((string) $key)->name()
            : ucfirst((string) $key);
    };

    $selectedLocationRows = $campaign?->targetingSet?->locations()
        ->where('kind', '!=', 'country')
        ->orderBy('id')
        ->get(['country_code', 'display_label']) ?? collect();
    $selectedPlaces = $selectedLocationRows->pluck('display_label')->all();
    $narrowedCountries = in_array($campaign?->platform, ['meta', 'google'], true)
        ? $selectedLocationRows->pluck('country_code')->filter()->all()
        : [];
    $countryCodes = $campaign
        ? app(\App\Campaigns\Targeting\TargetingSelections::class)->countries($campaign)
        : [];
    $countrySummary = implode(', ', array_map(
        fn (string $code): string => in_array($code, $narrowedCountries, true)
            ? $code.' (selected places only)'
            : $code,
        $countryCodes,
    ));

    foreach (['cities' => 'city', 'regions' => 'region'] as $field => $kind) {
        foreach ((array) ($campaign?->locations['meta_geo_locations'][$field] ?? []) as $place) {
            if (filled($place['key'] ?? null)) {
                $selectedPlaces[] = 'Imported Meta '.$kind.' ID '.$place['key'];
            }
        }
    }

    foreach ((array) ($campaign?->locations['google_location_criteria'] ?? []) as $criterion) {
        if (strtolower((string) ($criterion['target_type'] ?? '')) === 'country' && ! ($criterion['negative'] ?? false)) {
            continue;
        }

        $selectedPlaces[] = sprintf(
            'Imported Google %s%s: %s',
            ($criterion['negative'] ?? false) ? 'excluded ' : '',
            strtolower((string) ($criterion['target_type'] ?? 'location')),
            (string) ($criterion['name'] ?: $criterion['geo_target'] ?? 'unknown'),
        );
    }
@endphp

{{-- A conversation can hold several campaigns. Show multiple platforms like nav tabs with only the platform name. --}}
@if (($campaigns ?? collect())->count() > 1)
    <div class="mb-campaign-switch mb-campaign-nav-tabs" role="tablist" aria-label="Campaign Platforms">
        @foreach ($campaigns as $option)
            <button type="button"
                    class="mb-campaign-tab mb-campaign-nav-tab{{ $campaign && $option->id === $campaign->id ? ' active' : '' }}"
                    role="tab"
                    aria-selected="{{ $campaign && $option->id === $campaign->id ? 'true' : 'false' }}"
                    data-focus-campaign="{{ $option->id }}">
                {{ $platformName($option->platform) }}
            </button>
        @endforeach
    </div>
@endif

{{-- Looking at one campaign while the chat is working on another.
     A tab click only changes what is on screen, so without this somebody
     reads one campaign and answers about a different one. --}}
@if (! empty($workingOn))
    <p class="mb-campaign-note mb-2">
        Looking at this one. Your next answer goes to <strong>{{ $workingOn }}</strong>,
        unless you say otherwise.
    </p>
@endif

@if (! $campaign)
    <p class="text-body-secondary small mb-0">No campaign yet. It appears here as the chat fills it in.</p>
@else
    <div class="d-flex justify-content-between align-items-start mb-2">
        <strong>{{ $campaign->name }}</strong>
        <span class="badge {{ $campaign->status === 'published' ? 'text-bg-success' : 'text-bg-secondary' }}">
            {{ $campaign->status }}
        </span>
    </div>

    {{-- Where they are in the build.

         The flow has always known which step comes next, because that is how it
         decides what to ask. It was never shown, so the product could lead
         somebody through fourteen steps without ever telling them how many there
         were or how far along they had got. Steps that do not apply to this
         campaign are already absent: a traffic campaign has no pixel step, so it
         is not counted against them. --}}
    @if (! empty($progress))
        @php
            $settled = collect($progress)->where('done', true)->count();
            $total = count($progress);
        @endphp

        <div class="mb-progress">
            <div class="mb-progress-head">
                <span class="mb-progress-count">{{ $settled }} of {{ $total }}</span>
                @if ($nextStep)
                    <span class="mb-progress-next">Next: {{ $nextStep['label'] }}</span>
                @else
                    <span class="mb-progress-next is-ready">Everything answered</span>
                @endif
            </div>

            <div class="mb-progress-track" role="progressbar"
                 aria-valuenow="{{ $settled }}" aria-valuemin="0" aria-valuemax="{{ $total }}"
                 aria-label="Campaign setup progress">
                <span class="mb-progress-fill" style="width: {{ $total ? round($settled / $total * 100) : 0 }}%"></span>
            </div>

            <ol class="mb-progress-steps">
                @foreach ($progress as $step)
                    {{-- The campaign is named, never called "this campaign".
                         This panel shows whichever campaign is being looked at,
                         which is not always the one the chat is working on, so
                         "this campaign" resolved to whichever the agent had in
                         focus and a click could quietly edit the other one. --}}
                    <li class="mb-progress-step{{ $step['done'] ? ' is-done' : '' }}{{ $nextStep && $nextStep['key'] === $step['key'] ? ' is-next' : '' }}"
                        data-step-prompt="{{ ($step['done'] ? 'Review or change the ' : 'Help me configure the ') . $step['label'] . ' for ' . $campaign->name }}"
                        title="{{ $step['done'] ? 'Completed: ' . $step['label'] . ' (click to change)' : 'Click to prompt AI for ' . $step['label'] }}">
                        <i class="bi {{ $step['done'] ? 'bi-check-circle-fill' : 'bi-circle' }}"></i>
                        {{ $step['label'] }}
                    </li>
                @endforeach
            </ol>
        </div>
    @endif

    <table class="table table-sm mb-3">
        <tbody>
            @foreach (array_values(array_filter([
                $row('Platform', $campaign->platform, $sources['platform'] ?? null),
                $row('Objective', $campaign->objective, $sources['objective'] ?? null),
                $row('Landing page', $campaign->landing_url, $sources['landing_url'] ?? null),
                {{-- Shown even when it is nothing, because "no tracking" is a
                     decision with consequences and an empty row reads as one
                     nobody made. --}}
                $row(
                    'Click tracking',
                    $campaign->utm === null ? null : ($campaign->utm === '' ? 'none' : $campaign->utm),
                    $sources['utm'] ?? null,
                ),
                {{-- Labelled by what it actually is. "Daily budget" was shown
                     for every campaign whatever the arrangement, and it never
                     said the money sits on the ad set rather than the campaign. --}}
                $row(
                    $campaign->usesLifetimeBudget() ? 'Lifetime budget' : 'Daily budget',
                    $campaign->budget
                        ? $campaign->currency.' '.$campaign->budget
                            .($campaign->usesCampaignBudget() ? ', on the campaign' : ', on the ad set')
                        : null,
                    $sources['budget'] ?? null,
                ),
                $row('Bidding', $campaign->bid_strategy, $sources['bid_strategy'] ?? null),
                $row('Target cost', $campaign->bid_amount, $sources['bid_amount'] ?? null),
                $row('Countries', $countrySummary, $sources['locations'] ?? null),
                $selectedPlaces !== [] ? $row('Cities / regions', implode('; ', $selectedPlaces), $sources['locations'] ?? null) : null,
                {{-- Meta calls these placements and Google calls them networks,
                     which is the same distinction the step list already draws. --}}
                $has('placements')
                    ? $row(
                        str_contains($named('targeting', 'placements'), 'networks') ? 'Networks' : 'Placements',
                        implode(', ', $campaign->placements ?? []),
                        $sources['placements'] ?? null,
                    )
                    : null,
                $row('Devices', implode(', ', $campaign->audience['devices'] ?? []) ?: null, $sources['audience'] ?? null),
                $row('Ad account', $campaign->ad_account_id, $sources['ad_account_id'] ?? null),
                $has('page') ? $row('Page', $campaign->page_id, $sources['page_id'] ?? null) : null,
                {{-- Empty string means asked and there is none, which is not the
                     same as never asked. Shown so an Instagram placement running
                     under the Page is visible rather than assumed. --}}
                $has('instagram')
                    ? $row(
                        'Instagram',
                        $campaign->instagram_account_id === null
                            ? null
                            : ($campaign->instagram_account_id ?: 'runs under the Page'),
                        $sources['instagram_account_id'] ?? null,
                    )
                    : null,
                $has('pixel') ? $row($named('pixel', 'pixel'), $campaign->pixel_id, $sources['pixel_id'] ?? null) : null,
                // Shown as the pixel spells it rather than as the enum, because
                // "Subscribe" is what the buyer picked and what they will look
                // for in Events Manager. SUBSCRIBE is Meta's word for it.
                $has('conversion_event')
                    ? $row(
                        'optimises toward',
                        \App\Campaigns\Objectives::eventName($campaign->conversion_event) ?: $campaign->conversion_event,
                        $sources['conversion_event'] ?? null,
                    )
                    : null,
            ])) as [$label, $value, $colour, $title])
                <tr class="mb-panel-row" data-row-prompt="I want to update the {{ $label }} for {{ $campaign->name }}." title="Click to ask AI about {{ $label }}">
                    <td class="text-body-secondary" style="width: 38%;">
                        <span class="mb-dot {{ $colour }}" title="{{ $title }}"></span>
                        {{ $label }}
                    </td>
                    <td class="text-break">{{ filled($value) ? $value : '—' }}</td>
                </tr>
            @endforeach
        </tbody>
    </table>

    {{-- Not shown where ads carry no creative at all. A Google search ad is
         headlines and descriptions, so "Creatives: 0 attached" reads as
         something outstanding rather than something that does not apply. --}}
    @if (! $rules || $rules->requiresCreativeAsset($campaign))
        <div class="small text-body-secondary mb-1">
            Creatives: {{ $assets->count() }} attached
        </div>
    @endif

    {{-- Copy alone did not say which creative each ad carries, which is the
         question the panel exists to answer once creatives are attached. Only
         the public disk is web served, so an uploaded file has no preview and
         says so rather than rendering a broken image. --}}
    {{-- Copy alone did not say which creative each ad carries, which is the
         question the panel exists to answer once creatives are attached. Only
         the public disk is web served, so an uploaded file has no preview and
         says so rather than rendering a broken image. --}}
    @if ($campaign->ads()->exists())
        <div class="mb-preview-toggle-bar">
            <button type="button" class="mb-preview-toggle-btn active" data-ad-view="mockup">Live Ad Preview</button>
            <button type="button" class="mb-preview-toggle-btn" data-ad-view="list">Ad Variations ({{ $campaign->ads->count() }})</button>
        </div>

        <div id="adViewMockup" class="mb-ad-mockups mb-3">
            @foreach ($campaign->ads as $ad)
                @php($asset = $ad->asset)
                @if ($campaign->platform === 'google')
                    <div class="mb-ad-mockup-google">
                        <div>
                            <span class="mb-mockup-google-badge">Sponsored</span>
                            <span class="mb-mockup-google-url">{{ parse_url($campaign->landing_url ?? 'https://example.com', PHP_URL_HOST) ?? 'example.com' }}</span>
                        </div>
                        <div class="mb-mockup-google-title">{{ $ad->headline }}</div>
                        <div class="mb-mockup-google-desc">{{ $ad->primary_text }}</div>
                    </div>
                @else
                    @foreach (($previewIdentities ?? []) ?: [['platform' => $platformName($campaign->platform), 'name' => $campaign->name, 'icon' => $campaign->platform === 'linkedin' ? 'linkedin' : 'meta']] as $identity)
                    <div class="mb-ad-mockup-meta">
                        <div class="mb-mockup-header">
                            <div class="mb-mockup-avatar">
                                <i class="bi bi-{{ $identity['icon'] }}" aria-label="{{ $identity['platform'] }}"></i>
                            </div>
                            <div class="mb-mockup-meta-info">
                                <div class="mb-mockup-page-name">{{ $identity['name'] }}</div>
                                <div class="mb-mockup-sponsored">{{ $identity['platform'] }} &middot; Sponsored</div>
                            </div>
                        </div>
                        <div class="mb-mockup-body">{{ $ad->primary_text }}</div>
                        @if ($asset && $asset->hasStill())
                            <div class="mb-mockup-media">
                                <img src="{{ route('assets.preview', $asset) }}" alt="">
                            </div>
                        @endif
                        <div class="mb-mockup-footer">
                            <div class="mb-mockup-link-info">
                                <div class="mb-mockup-domain">{{ parse_url($campaign->landing_url ?? 'https://example.com', PHP_URL_HOST) ?? 'example.com' }}</div>
                                <div class="mb-mockup-headline">{{ $ad->headline }}</div>
                            </div>
                            <span class="mb-mockup-cta-btn">{{ str_replace('_', ' ', $ad->cta ?? 'LEARN MORE') }}</span>
                        </div>
                    </div>
                    @endforeach
                @endif
            @endforeach
        </div>

        <div id="adViewList" class="small mb-3 d-none">
            @foreach ($campaign->ads as $ad)
                @php($asset = $ad->asset)
                <div class="border rounded p-2 mb-1 mb-ad">
                    @if ($asset && $asset->hasStill())
                        <span class="mb-ad-thumb-wrap">
                            <img class="mb-ad-thumb" src="{{ route('assets.preview', $asset) }}" alt="">
                            @if ($asset->kind === 'video')
                                <i class="bi bi-play-btn mb-ad-thumb-play" title="Video creative"></i>
                            @endif
                        </span>
                    @else
                        <span class="mb-ad-thumb mb-ad-thumb-empty"
                              title="{{ $asset ? 'Video creative' : 'No creative attached' }}">
                            <i class="bi bi-{{ $asset ? ($asset->kind === 'video' ? 'play-btn' : 'image') : 'dash' }}"></i>
                        </span>
                    @endif

                    <div class="mb-ad-body">
                        <div class="fw-semibold">{{ $ad->headline }}</div>
                        <div class="text-body-secondary">{{ $ad->primary_text }}</div>
                        <span class="badge text-bg-light">{{ $ad->cta }}</span>
                        @unless ($asset)
                            <span class="badge text-bg-warning">no creative</span>
                        @endunless
                    </div>
                </div>
            @endforeach
        </div>
    @endif

    @if ($pending || $needsRebuild)
        <div class="alert alert-warning small py-2">
            <div class="fw-semibold mb-1">Not yet on the platform</div>

            @foreach ($pending as $field => $change)
                <div>
                    {{ str_replace('_', ' ', $field) }}:
                    <span class="text-decoration-line-through">{{ $change['from'] ?? 'not set' }}</span>
                    &rarr; <strong>{{ $change['to'] ?? 'not set' }}</strong>
                </div>
            @endforeach

            @foreach ($needsRebuild as $reason)
                <div>{{ $reason }}</div>
            @endforeach

            @if ($pending)
                <div class="mt-1">Ask in the chat to push these changes.</div>
            @endif
        </div>
    @endif

    {{-- Was a raw json_encode of the ids, which is unreadable in a narrow panel
         and is the first thing anyone needs when checking Ads Manager. --}}
    @if ($campaign->external_ids)
        <div class="alert alert-success small py-2">
            <div class="fw-semibold mb-1">Created on the platform</div>
            <dl class="mb-ids font-monospace mb-0">
                @foreach ($campaign->external_ids as $label => $id)
                    <dt>{{ str_replace('_', ' ', $label) }}</dt>
                    <dd>{{ is_scalar($id) ? $id : json_encode($id) }}</dd>
                @endforeach
            </dl>
        </div>
    @endif

    @if ($campaign->last_error)
        <div class="alert alert-danger small py-2">{{ $campaign->last_error }}</div>
    @endif

    @if ($blocking)
        <div class="small">
            {{-- Named for what it gates. The strip above counts every step in
                 the flow and this counts only what stops a publish, so the two
                 legitimately disagree: bid strategy and click tracking are
                 asked for but not required. Unlabelled, that read as a bug. --}}
            <div class="text-body-secondary mb-1">Still needed to publish</div>
            <ul class="ps-3 mb-0">
                @foreach ($blocking as $reason)
                    <li>{{ $reason }}</li>
                @endforeach
            </ul>
        </div>
    @else
        <div class="alert alert-success small py-2 mb-0">Ready to publish.</div>
    @endif
@endif
