<?php

namespace App\Services\Sync;

use App\Models\MetaAdsetSnapshot;
use App\Models\MetaAdSnapshot;
use App\Models\MetaCampaignSnapshot;
use App\Models\PlatformConnection;
use App\Services\Credentials\CredentialVault;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Str;

class MetaAdsSyncService
{
    public function __construct(private readonly CredentialVault $vault) {}

    public function sync(PlatformConnection $connection, string $from, string $to): SyncResult
    {
        $started = microtime(true);

        try {
            $payload = $this->vault->payload($connection->secureCredential);
            $token = $payload['access_token'] ?? null;
            $adAccountId = $payload['ad_account_id'] ?? $connection->account_id;

            if (blank($token)) {
                return SyncResult::failed('Meta access token eksik.');
            }
            if (blank($adAccountId)) {
                return SyncResult::failed('Meta reklam hesabı ID eksik.');
            }

            $account = str_starts_with((string) $adAccountId, 'act_') ? $adAccountId : 'act_'.$adAccountId;
            $campaigns = $this->syncLevel($connection, $account, $token, 'campaign', $from, $to);
            $adsets = $this->syncLevel($connection, $account, $token, 'adset', $from, $to);
            $ads = $this->syncLevel($connection, $account, $token, 'ad', $from, $to);
            $duration = (int) round((microtime(true) - $started) * 1000);

            return SyncResult::success('Meta Ads verileri senkronize edildi.', [
                'campaigns' => $campaigns,
                'adsets' => $adsets,
                'ads' => $ads,
            ], $duration);
        } catch (\Throwable $e) {
            $duration = (int) round((microtime(true) - $started) * 1000);

            return SyncResult::failed('Meta Ads senkronizasyonu başarısız.', $e->getMessage(), [], $duration);
        }
    }

    private function syncLevel(PlatformConnection $connection, string $account, string $token, string $level, string $from, string $to): int
    {
        $rows = $this->fetchInsights($account, $token, $level, $from, $to);
        $count = 0;

        foreach ($rows as $row) {
            $date = $row['date_start'] ?? $from;
            $common = $this->commonPayload($connection, $row, $date);

            if ($level === 'campaign' && ! empty($row['campaign_id'])) {
                MetaCampaignSnapshot::updateOrCreate(
                    ['platform_connection_id' => $connection->id, 'date' => $date, 'campaign_id' => $row['campaign_id']],
                    array_merge($common, [
                        'campaign_name' => $row['campaign_name'] ?? null,
                        'objective' => $row['objective'] ?? null,
                    ])
                );
                $count++;
            }

            if ($level === 'adset' && ! empty($row['adset_id'])) {
                MetaAdsetSnapshot::updateOrCreate(
                    ['platform_connection_id' => $connection->id, 'date' => $date, 'adset_id' => $row['adset_id']],
                    array_merge($common, [
                        'campaign_id' => $row['campaign_id'] ?? null,
                        'campaign_name' => $row['campaign_name'] ?? null,
                        'adset_name' => $row['adset_name'] ?? null,
                    ])
                );
                $count++;
            }

            if ($level === 'ad' && ! empty($row['ad_id'])) {
                MetaAdSnapshot::updateOrCreate(
                    ['platform_connection_id' => $connection->id, 'date' => $date, 'ad_id' => $row['ad_id']],
                    array_merge($common, [
                        'campaign_id' => $row['campaign_id'] ?? null,
                        'campaign_name' => $row['campaign_name'] ?? null,
                        'adset_id' => $row['adset_id'] ?? null,
                        'adset_name' => $row['adset_name'] ?? null,
                        'ad_name' => $row['ad_name'] ?? null,
                    ])
                );
                $count++;
            }
        }

        return $count;
    }

    private function fetchInsights(string $account, string $token, string $level, string $from, string $to): array
    {
        $version = config('services.meta.graph_version', env('META_GRAPH_VERSION', 'v23.0'));
        $endpoint = "https://graph.facebook.com/{$version}/{$account}/insights";
        $fields = match ($level) {
            'campaign' => 'campaign_id,campaign_name,objective,spend,impressions,reach,clicks,inline_link_clicks,cpc,cpm,ctr,actions,action_values,date_start,date_stop',
            'adset' => 'campaign_id,campaign_name,adset_id,adset_name,spend,impressions,reach,clicks,inline_link_clicks,cpc,cpm,ctr,actions,action_values,date_start,date_stop',
            default => 'campaign_id,campaign_name,adset_id,adset_name,ad_id,ad_name,spend,impressions,reach,clicks,inline_link_clicks,cpc,cpm,ctr,actions,action_values,date_start,date_stop',
        };

        $params = [
            'fields' => $fields,
            'level' => $level,
            'time_increment' => 1,
            'time_range' => json_encode(['since' => $from, 'until' => $to]),
            'limit' => 200,
            'access_token' => $token,
        ];

        $rows = [];
        $next = $endpoint;
        while ($next) {
            $response = Http::timeout(90)->get($next, $params);
            $params = [];
            if (! $response->successful()) {
                throw new \RuntimeException('Meta Insights sorgusu başarısız: '.Str::limit($response->body(), 1000));
            }
            $json = $response->json();
            $rows = array_merge($rows, $json['data'] ?? []);
            $next = Arr::get($json, 'paging.next');
        }

        return $rows;
    }

    private function commonPayload(PlatformConnection $connection, array $row, string $date): array
    {
        $actions = $row['actions'] ?? [];
        $values = $row['action_values'] ?? [];

        return [
            'client_id' => $connection->client_id,
            'date' => $date,
            'spend' => (float) ($row['spend'] ?? 0),
            'impressions' => (int) ($row['impressions'] ?? 0),
            'reach' => (int) ($row['reach'] ?? 0),
            'clicks' => (int) ($row['clicks'] ?? 0),
            'inline_link_clicks' => (int) ($row['inline_link_clicks'] ?? 0),
            'cpc' => (float) ($row['cpc'] ?? 0),
            'cpm' => (float) ($row['cpm'] ?? 0),
            'ctr' => (float) ($row['ctr'] ?? 0),
            'conversions' => $this->sumActions($actions, ['offsite_conversion', 'onsite_conversion', 'purchase', 'lead', 'complete_registration']),
            'leads' => $this->sumActions($actions, ['lead', 'onsite_conversion.lead_grouped', 'offsite_conversion.fb_pixel_lead']),
            'purchases' => $this->sumActions($actions, ['purchase', 'offsite_conversion.fb_pixel_purchase', 'omni_purchase']),
            'purchase_value' => $this->sumActions($values, ['purchase', 'offsite_conversion.fb_pixel_purchase', 'omni_purchase']),
            'raw_json' => json_encode($row, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        ];
    }

    private function sumActions(array $actions, array $needles): float
    {
        $sum = 0.0;
        foreach ($actions as $action) {
            $type = (string) ($action['action_type'] ?? '');
            foreach ($needles as $needle) {
                if ($type === $needle || str_contains($type, $needle)) {
                    $sum += (float) ($action['value'] ?? 0);
                    break;
                }
            }
        }

        return $sum;
    }
}
