<?php

namespace App\Campaigns;

use App\Models\CampaignPerformanceHourStat;
use App\Models\CampaignPerformanceProfile;
use App\Models\CampaignPerformanceStat;
use Illuminate\Support\Facades\Log;
use RuntimeException;
use Throwable;

class CampaignPerformanceProfileSync
{
    public function __construct(
        private readonly MetaPerformanceProfileReader $meta,
        private readonly GooglePerformanceProfileReader $google,
    ) {}

    /**
     * @param  array{
     *     platform: string,
     *     user_id: int,
     *     external_campaign_id: string,
     *     local_campaign_id: ?int,
     *     local_google_ads_campaign_id: ?int
     * }  $candidate
     */
    public function sync(array $candidate): CampaignPerformanceProfile
    {
        try {
            // Meta is a case, not the default. A platform with no reader here was
            // being handed to Graph, which then answers about an object that was
            // never on Meta; see CampaignPerformanceSync::readerFor().
            $payload = match (strtolower((string) $candidate['platform'])) {
                'google' => $this->google->read($candidate),
                'meta' => $this->meta->read($candidate),
                default => throw new RuntimeException(sprintf(
                    'No performance profile reader for [%s].',
                    $candidate['platform'],
                )),
            };
        } catch (Throwable $exception) {
            Log::warning('Campaign performance profile sync failed', [
                'platform' => $candidate['platform'],
                'external_campaign_id' => $candidate['external_campaign_id'],
                'error' => $exception->getMessage(),
            ]);

            $payload = [
                'name' => null,
                'objective' => null,
                'daily_budget' => null,
                'currency' => null,
                'bid_strategy' => null,
                'platform_status' => null,
                'delivery_status' => null,
                'delivery_detail' => null,
                'regions' => [],
                'languages' => [],
                'platform_learning' => null,
                'platform_payload' => null,
                'account_id' => null,
                'started_at' => null,
                'last_error' => mb_substr($exception->getMessage(), 0, 2000),
            ];
        }

        $profile = CampaignPerformanceProfile::query()
            ->where('platform', $candidate['platform'])
            ->where('external_campaign_id', $candidate['external_campaign_id'])
            ->first() ?? new CampaignPerformanceProfile;

        $name = filled($payload['name'] ?? null)
            ? (string) $payload['name']
            : ($profile->name ?: null);

        $profile->fill([
            'user_id' => $candidate['user_id'],
            'platform' => $candidate['platform'],
            'external_campaign_id' => $candidate['external_campaign_id'],
            'local_campaign_id' => $candidate['local_campaign_id'],
            'local_google_ads_campaign_id' => $candidate['local_google_ads_campaign_id'],
            'name' => $name,
            'objective' => $payload['objective'] ?? $profile->objective,
            'daily_budget' => $payload['daily_budget'] ?? $profile->daily_budget,
            'currency' => $payload['currency'] ?? $profile->currency,
            'bid_strategy' => $payload['bid_strategy'] ?? $profile->bid_strategy,
            'platform_status' => $payload['platform_status'] ?? $profile->platform_status,
            'delivery_status' => $payload['delivery_status'] ?? $profile->delivery_status,
            'delivery_detail' => $payload['delivery_detail'] ?? $profile->delivery_detail,
            'regions' => $payload['regions'] ?? $profile->regions ?? [],
            'languages' => $payload['languages'] ?? $profile->languages ?? [],
            'platform_learning' => $payload['platform_learning'] ?? $profile->platform_learning,
            'platform_payload' => $payload['platform_payload'] ?? $profile->platform_payload,
            'account_id' => $payload['account_id'] ?? $profile->account_id,
            'started_at' => $payload['started_at'] ?? $profile->started_at,
            'fetched_at' => now(),
            'last_error' => $payload['last_error'] ?? null,
        ]);
        $profile->save();

        $this->applyDeliveryStatus($candidate, $profile);

        return $profile;
    }

    /**
     * @param  array{
     *     platform: string,
     *     external_campaign_id: string
     * }  $candidate
     */
    private function applyDeliveryStatus(array $candidate, CampaignPerformanceProfile $profile): void
    {
        if (! filled($profile->delivery_status) && ! filled($profile->platform_status)) {
            return;
        }

        $attributes = array_filter([
            'delivery_status' => $profile->delivery_status,
            'platform_status' => $profile->platform_status,
        ], fn (mixed $value): bool => is_string($value) && $value !== '');

        if ($attributes === []) {
            return;
        }

        CampaignPerformanceStat::query()
            ->where('platform', $candidate['platform'])
            ->where('external_campaign_id', $candidate['external_campaign_id'])
            ->whereNull('last_error')
            ->update($attributes);

        CampaignPerformanceHourStat::query()
            ->where('platform', $candidate['platform'])
            ->where('external_campaign_id', $candidate['external_campaign_id'])
            ->whereNull('last_error')
            ->update($attributes);
    }
}
