<?php

namespace App\Services\AI;

use App\Campaigns\PerformanceAdvice;
use App\Campaigns\PerformanceVerdict;
use Illuminate\Contracts\JsonSchema\JsonSchema;
use Illuminate\Support\Facades\Log;
use Throwable;

use function Laravel\Ai\agent;

class CampaignPerformanceAdvisor
{
    private const INSTRUCTIONS = <<<'TEXT'
You explain the performance verdict for one advertising campaign.
You cannot pause, enable, publish, or change budget. You only return JSON.
Use only the snapshot. Copy spend, clicks, and impressions from it; never invent numbers.
Weight the final_verdict and window verdicts when writing the reason.
Use last_7_hours to describe what happened recently.
Use previous_7_days, lifetime, and trend only to say whether performance is improving or declining.
When affiliate.matched is true, you may cite affiliate.7d net_revenue, orders, and clicks as downstream affiliate metrics — never call them Meta clicks.
Do not change the verdict — PHP already chose final_verdict in the snapshot.
Never invent conversion numbers; only use affiliate fields when present in the snapshot.
Reason: one or two sentences, under 240 characters, telling the user what to do.
If final_verdict is pause, stop, or action_required, do not soften the recommendation.
TEXT;

    /**
     * @param  array<string, mixed>  $snapshot
     */
    public function explain(array $snapshot, PerformanceAdvice $php): PerformanceAdvice
    {
        if ($this->shouldSkip($snapshot, $php)) {
            return $php;
        }

        try {
            $response = agent(
                instructions: self::INSTRUCTIONS,
                schema: fn (JsonSchema $schema) => [
                    'reason' => $schema->string()->required(),
                    'confidence' => $schema->string()->required(),
                ],
            )->prompt(
                "Snapshot:\n".json_encode($snapshot, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE),
                provider: TextProviderFailover::providers(),
            );

            $payload = $response->toArray();

            return $this->accepted($payload, $snapshot, $php)
                ?? $this->rejected($php, 'Advisor output was discarded.');
        } catch (Throwable $exception) {
            Log::warning('Campaign performance advisor failed', [
                'platform' => $snapshot['platform'] ?? null,
                'campaign' => $snapshot['campaign'] ?? null,
                'error' => $exception->getMessage(),
            ]);

            return new PerformanceAdvice(
                status: $php->status,
                reason: $php->reason,
                source: 'php',
                lastError: mb_substr($exception->getMessage(), 0, 2000),
            );
        }
    }

    /**
     * @param  array<string, mixed>  $snapshot
     */
    private function shouldSkip(array $snapshot, PerformanceAdvice $php): bool
    {
        if (! config('campaign-performance.llm_enabled', true)) {
            return true;
        }

        if ($php->status === PerformanceVerdict::NoAction
            && config('campaign-performance.skip_llm_while_learning', true)) {
            return true;
        }

        $platformStatus = strtolower((string) ($snapshot['platform_status'] ?? ''));
        $spend = (float) data_get($snapshot, 'last_7_days.spend', 0);

        return config('campaign-performance.skip_llm_when_paused_zero_spend', true)
            && in_array($platformStatus, ['paused', 'campaign_paused'], true)
            && $spend < 0.01;
    }

    /**
     * @param  array<string, mixed>  $payload
     * @param  array<string, mixed>  $snapshot
     */
    private function accepted(array $payload, array $snapshot, PerformanceAdvice $php): ?PerformanceAdvice
    {
        $reason = trim((string) ($payload['reason'] ?? ''));
        $confidence = strtolower(trim((string) ($payload['confidence'] ?? '')));

        if ($reason === '' || strlen($reason) > 500) {
            return null;
        }

        if ($this->inventsNumbers($reason, $snapshot)) {
            return null;
        }

        if (! in_array($confidence, ['low', 'medium', 'high'], true)) {
            $confidence = 'medium';
        }

        return new PerformanceAdvice(
            status: $php->status,
            reason: mb_substr($reason, 0, 500),
            source: 'llm',
            confidence: $confidence,
            model: TextProviderFailover::providers()[0] ?? 'llm',
        );
    }

    private function rejected(PerformanceAdvice $php, string $error): PerformanceAdvice
    {
        return new PerformanceAdvice(
            status: $php->status,
            reason: $php->reason,
            source: 'php',
            lastError: $error,
        );
    }

    /**
     * @param  array<string, mixed>  $snapshot
     */
    private function inventsNumbers(string $reason, array $snapshot): bool
    {
        $json = json_encode($snapshot, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?: '{}';

        preg_match_all('/\$?\d+(?:\.\d+)?/', $reason, $matches);

        foreach ($matches[0] as $raw) {
            $plain = ltrim((string) $raw, '$');

            if (str_contains($json, $plain)) {
                continue;
            }

            if (is_numeric($plain) && str_contains($json, (string) (0 + $plain))) {
                continue;
            }

            return true;
        }

        return false;
    }
}
