<?php

namespace App\Campaigns;

class PerformanceWindowVerdict
{
    /**
     * @param  array{
     *     window: string,
     *     from: ?string,
     *     to: ?string,
     *     spend: float,
     *     impressions: int,
     *     clicks: int,
     *     cpc: ?float,
     *     ctr: ?float,
     *     buckets: int,
     *     expected_buckets: int,
     *     insufficient_data: bool
     * }  $rollup
     * @param  array<string, mixed>  $context
     * @return array{
     *     window: string,
     *     from: ?string,
     *     to: ?string,
     *     spend: float,
     *     impressions: int,
     *     clicks: int,
     *     cpc: ?float,
     *     ctr: ?float,
     *     buckets: int,
     *     expected_buckets: int,
     *     insufficient_data: bool,
     *     verdict: string,
     *     reason: string
     * }
     */
    public function forWindow(array $rollup, array $context): array
    {
        $verdict = $this->verdict($rollup, $context);

        return [
            ...$rollup,
            'verdict' => $verdict->value,
            'reason' => $this->reason($verdict, $rollup, $context),
        ];
    }

    /**
     * @param  array<string, mixed>  $rollup
     * @param  array<string, mixed>  $context
     */
    private function verdict(array $rollup, array $context): PerformanceVerdict
    {
        $windowKey = (string) $rollup['window'];
        $isHourWindow = str_ends_with($windowKey, 'h');
        $floor = (float) data_get($context, 'rules.zero_click_spend_floor', 10);
        $maxCpc = (float) data_get($context, 'rules.max_cpc', 2);
        $learningDays = (int) data_get($context, 'learning_window_days', 3);
        $maxTestSpend = (float) data_get($context, 'max_test_spend', 40);
        $daysLive = (int) data_get($context, 'days_live', 0);
        $lifetimeSpend = round((float) data_get($context, 'lifetime.spend', $rollup['spend']), 2);
        $budget = data_get($context, 'daily_budget');
        $spend = (float) $rollup['spend'];
        $clicks = (int) $rollup['clicks'];
        $cpc = $rollup['cpc'];
        $platformStatus = strtolower((string) data_get($context, 'platform_status', ''));
        $platformPaused = in_array($platformStatus, ['paused', 'campaign_paused', 'disabled'], true);

        if ($rollup['insufficient_data']) {
            return PerformanceVerdict::NoAction;
        }

        if ($this->syncFailed($context, $rollup)) {
            return PerformanceVerdict::ActionRequired;
        }

        if ($this->deliveryBroken($context)
            && $spend < 0.01
            && $daysLive >= 1
            && is_numeric($budget)
            && (float) $budget > 0) {
            return PerformanceVerdict::ActionRequired;
        }

        if ($this->platformLearning($context) && $isHourWindow) {
            return PerformanceVerdict::NoAction;
        }

        if ($daysLive < $learningDays || $lifetimeSpend < $maxTestSpend) {
            return PerformanceVerdict::NoAction;
        }

        if ($platformPaused && ! $isHourWindow && $spend >= 0.01 && $clicks > 0) {
            $cpcOk = $cpc === null || $cpc <= $maxCpc;

            if ($cpcOk && $spend < $floor * 2) {
                return PerformanceVerdict::Start;
            }
        }

        if ($spend >= $floor && $clicks === 0) {
            return PerformanceVerdict::Pause;
        }

        if ($clicks > 0 && $cpc !== null && $cpc > $maxCpc && $spend >= $floor) {
            return PerformanceVerdict::Pause;
        }

        if ($platformPaused) {
            return PerformanceVerdict::NoAction;
        }

        return PerformanceVerdict::Continue;
    }

    /**
     * @param  array<string, mixed>  $rollup
     * @param  array<string, mixed>  $context
     */
    private function reason(PerformanceVerdict $verdict, array $rollup, array $context): string
    {
        $window = strtoupper((string) $rollup['window']);
        $spend = $this->money((float) $rollup['spend']);
        $clicks = (int) $rollup['clicks'];
        $cpc = $rollup['cpc'] === null ? 'n/a' : $this->money((float) $rollup['cpc']);

        return match ($verdict) {
            PerformanceVerdict::NoAction => $rollup['insufficient_data']
                ? sprintf('%s window has no stored metrics yet.', $window)
                : sprintf(
                    '%s: %s spend, %d clicks. Still in learning or platform learning — no change yet.',
                    $window,
                    $spend,
                    $clicks,
                ),
            PerformanceVerdict::Continue => sprintf(
                '%s: %s spend, %d clicks, CPC %s. Metrics look acceptable.',
                $window,
                $spend,
                $clicks,
                $cpc,
            ),
            PerformanceVerdict::Pause => sprintf(
                '%s: %s spend, %d clicks, CPC %s. Suggestion: pause this campaign.',
                $window,
                $spend,
                $clicks,
                $cpc,
            ),
            PerformanceVerdict::Stop => sprintf(
                '%s: %s spend, %d clicks. Stop spend immediately.',
                $window,
                $spend,
                $clicks,
            ),
            PerformanceVerdict::Start => sprintf(
                '%s: %s spend, %d clicks while platform is paused. Consider enabling.',
                $window,
                $spend,
                $clicks,
            ),
            PerformanceVerdict::ActionRequired => sprintf(
                '%s: delivery or sync looks broken (%s). Fix before judging performance.',
                $window,
                $this->actionRequiredDetail($context),
            ),
        };
    }

    /**
     * @param  array<string, mixed>  $context
     * @param  array<string, mixed>  $rollup
     */
    private function syncFailed(array $context, array $rollup): bool
    {
        if (! (bool) data_get($context, 'sync_failed', false)) {
            return false;
        }

        return (float) $rollup['spend'] < 0.01
            && (int) $rollup['impressions'] === 0
            && (int) $rollup['clicks'] === 0;
    }

    /**
     * @param  array<string, mixed>  $context
     */
    private function deliveryBroken(array $context): bool
    {
        foreach (['delivery_status', 'platform_status'] as $field) {
            $value = strtolower((string) data_get($context, $field, ''));

            if ($value === '') {
                continue;
            }

            if (str_contains($value, 'disapproved')
                || str_contains($value, 'with_issues')
                || str_contains($value, 'limited')
                || str_contains($value, 'not_delivering')
                || str_contains($value, 'not delivering')
                || str_contains($value, 'not_eligible')) {
                return true;
            }
        }

        return false;
    }

    /**
     * @param  array<string, mixed>  $context
     */
    private function platformLearning(array $context): bool
    {
        $delivery = strtoupper((string) data_get($context, 'delivery_status', ''));

        if ($delivery === 'LEARNING') {
            return true;
        }

        $learningStatus = strtoupper((string) data_get($context, 'campaign_profile.platform_learning.status', ''));

        return $learningStatus === 'LEARNING';
    }

    /**
     * @param  array<string, mixed>  $context
     */
    private function actionRequiredDetail(array $context): string
    {
        if ((bool) data_get($context, 'sync_failed', false)) {
            return (string) data_get($context, 'sync_error', 'last sync failed');
        }

        $status = trim((string) data_get($context, 'delivery_status', ''));

        if ($status !== '') {
            return $status;
        }

        return trim((string) data_get($context, 'platform_status', 'delivery looks broken')) ?: 'delivery looks broken';
    }

    private function money(float $amount): string
    {
        return '$'.number_format($amount, 2);
    }
}
