<?php

namespace App\Campaigns\Platforms;

use App\Models\Campaign;
use App\Models\TikTokConnection;

class TikTokRules extends UnsupportedPlatformRules
{
    public function key(): string
    {
        return 'tiktok';
    }

    public function name(): string
    {
        return 'TikTok';
    }

    public function objectives(): array
    {
        return ['SALES', 'LEAD_GENERATION', 'TRAFFIC', 'REACH'];
    }

    public function objectiveFor(string $outcome): ?string
    {
        return match (strtoupper($outcome)) {
            'SALES' => 'SALES',
            'LEADS', 'LEAD_GENERATION' => 'LEAD_GENERATION',
            'TRAFFIC' => 'TRAFFIC',
            'AWARENESS', 'REACH' => 'REACH',
            default => null,
        };
    }

    public function describeObjective(string $objective): array
    {
        return match (strtoupper($objective)) {
            'SALES' => ['label' => 'Sales', 'hint' => 'Drive website purchases and conversions'],
            'LEAD_GENERATION' => ['label' => 'Lead generation', 'hint' => 'Collect leads from interested customers'],
            'TRAFFIC' => ['label' => 'Traffic', 'hint' => 'Drive visits to your landing page'],
            'REACH' => ['label' => 'Reach', 'hint' => 'Show the campaign to more people'],
            default => ['label' => $objective, 'hint' => ''],
        };
    }

    public function bidStrategyFor(string $intent, ?string $objective = null): ?string
    {
        return match (strtoupper($intent)) {
            'AUTOMATIC' => 'BID_TYPE_NO_BID',
            'TARGET_COST', 'BID_CAP' => 'BID_TYPE_CUSTOM',
            default => null,
        };
    }

    public function bidStrategies(): array
    {
        return ['BID_TYPE_NO_BID', 'BID_TYPE_CUSTOM'];
    }

    public function bidStrategyOptions(): array
    {
        return [
            [
                'value' => 'BID_TYPE_NO_BID',
                'label' => 'Highest volume',
                'hint' => 'Let TikTok bid automatically for the most results.',
                'sends' => 'Use automatic bidding',
            ],
            [
                'value' => 'BID_TYPE_CUSTOM',
                'label' => 'Cost or bid cap',
                'hint' => 'Set a target cost or maximum bid amount.',
                'sends' => 'Use a custom bid',
            ],
        ];
    }

    public function bidStrategyNeedsTarget(string $strategy): bool
    {
        return strtoupper($strategy) === 'BID_TYPE_CUSTOM';
    }

    public function placements(): array
    {
        return ['tiktok'];
    }

    public function placementsFor(string $objective): array
    {
        return $this->placements();
    }

    public function optimizationGoal(string $objective): ?string
    {
        return match (strtoupper($objective)) {
            'SALES' => 'CONVERT',
            'LEAD_GENERATION' => 'LEAD_GENERATION',
            'TRAFFIC' => 'CLICK',
            'REACH' => 'REACH',
            default => null,
        };
    }

    public function conversionEvent(string $objective): ?string
    {
        return match (strtoupper($objective)) {
            'SALES' => 'Purchase',
            'LEAD_GENERATION' => 'SubmitForm',
            default => null,
        };
    }

    public function requiresPixel(string $objective): bool
    {
        return $this->conversionEvent($objective) !== null;
    }

    public function requiresCreativeAsset(?Campaign $campaign = null): bool
    {
        return true;
    }

    public function trackingOptions(): array
    {
        return [[
            'value' => 'utm_source=tiktok&utm_medium=paid_social',
            'label' => 'Standard TikTok tagging',
            'hint' => 'Records TikTok as the source of paid traffic.',
            'badge' => 'Suggested',
            'sends' => 'Use standard TikTok UTM tagging',
        ]];
    }

    public function defaultAdAccountId(): ?string
    {
        $connection = auth()->user()
            ? TikTokConnection::query()->where('user_id', auth()->id())->first()
            : null;

        return $connection?->advertiser_id ?: config('services.tiktok.advertiser_id');
    }

    public function budgetModes(): array
    {
        return [[
            'value' => 'adset:daily',
            'label' => 'Daily',
            'hint' => 'Spend this much every day until it is stopped.',
            'sends' => 'Use a daily TikTok ad-group budget',
        ]];
    }

    public function copyLimits(): array
    {
        return ['headline' => 100, 'description' => 1000, 'primary_text' => 1000];
    }

    public function callsToAction(): array
    {
        return ['LEARN_MORE', 'SHOP_NOW', 'SIGN_UP', 'CONTACT_US', 'DOWNLOAD'];
    }

    public function additionalProblems(Campaign $campaign): array
    {
        $connection = auth()->user()
            ? TikTokConnection::query()->where('user_id', auth()->id())->first()
            : null;

        if ($connection === null || blank($connection->access_token)) {
            return ['TikTok Ads is not connected for this user'];
        }

        if (blank($connection->advertiser_id ?: $campaign->ad_account_id)) {
            return ['Select a TikTok advertiser before publishing'];
        }

        if (blank($connection->identity_type) || blank($connection->identity_id)) {
            return ['Authorize a TikTok identity before publishing'];
        }

        return array_diff(
            (array) ($campaign->placements ?? []),
            $this->placements(),
        ) === [] ? [] : ['TikTok supports the TikTok placement only'];
    }

    public function steps(): array
    {
        return [
            ['key' => 'objective', 'label' => 'objective', 'tool' => 'choose_objective', 'offers' => true,
                'ask' => 'What should this TikTok campaign optimise for?'],
            ['key' => 'identity', 'label' => 'TikTok identity', 'tool' => 'choose_tik_tok_identity', 'offers' => true,
                'ask' => 'Which TikTok identity should the ad use?'],
            ['key' => 'pixel', 'label' => 'TikTok pixel', 'tool' => 'choose_pixel', 'offers' => true,
                'ask' => 'Which TikTok pixel should conversions be attributed to?'],
            ['key' => 'bidding', 'label' => 'bid strategy', 'tool' => 'set_bidding', 'offers' => true,
                'ask' => 'How should TikTok bid?'],
            ['key' => 'targeting', 'label' => 'countries and placement', 'tool' => 'set_targeting', 'offers' => true,
                'ask' => 'Where should this TikTok campaign run?'],
        ];
    }

    public function isConfigured(): bool
    {
        $connection = auth()->user()
            ? TikTokConnection::query()->where('user_id', auth()->id())->first()
            : null;

        return filled($connection?->access_token) && filled($connection?->advertiser_id);
    }

    public function editableAfterPublish(): array
    {
        return [];
    }

    /**
     * TikTok's own minimum, which it enforces on create.
     *
     * TikTok sets two: a campaign daily budget of at least 50.00, and an ad
     * group daily budget of at least 20.00. This product holds the budget on
     * the ad set by default, so the ad group figure is the one that applies to
     * almost every campaign it builds.
     *
     * A campaign-level budget therefore needs 50.00 rather than this, which is
     * a distinction to make when TikTok gets a publisher and the budget level
     * becomes something that reaches the API. Until then nothing here can
     * publish, so the stricter figure would only refuse drafts.
     *
     * Published in USD. TikTok states its minimums per currency and they are
     * not conversions of each other, so confirm against the current rate card
     * before the first live campaign.
     */
    public function minimumDailyBudget(): float
    {
        return 20.00;
    }

    public function supportedAssetRatios(): array
    {
        return ['9:16', '1:1', '16:9'];
    }
}
