<?php

namespace App\Services\FirstReport;

use App\Models\Client;
use App\Models\GeneratedReport;
use App\Models\PlatformConnection;
use App\Models\ReportTemplate;
use App\Services\Ai\ReportAiInsightGenerator;
use App\Services\Integrations\Preview\DataPreviewService;
use App\Services\Mail\ReportMailer;
use App\Services\Quality\DataQualityService;
use App\Services\Reports\ReportGenerator;
use App\Services\Reports\ReportTemplateDefaults;
use App\Services\Sync\PlatformSyncManager;
use Carbon\Carbon;
use Illuminate\Support\Arr;
use Throwable;

class FirstReportWizardService
{
    public function __construct(
        private readonly DataPreviewService $previewService,
        private readonly PlatformSyncManager $syncManager,
        private readonly DataQualityService $qualityService,
        private readonly ReportGenerator $reportGenerator,
        private readonly ReportTemplateDefaults $templateDefaults,
        private readonly ReportAiInsightGenerator $insightGenerator,
        private readonly ReportMailer $mailer,
    ) {
    }

    public function defaultRange(): array
    {
        return [
            now()->subDays(7)->toDateString(),
            now()->subDay()->toDateString(),
        ];
    }

    public function dashboardPayload(Client $client, ?string $from = null, ?string $to = null): array
    {
        [$from, $to] = $this->normalizeRange($from, $to);
        $client->load([
            'platformConnections.secureCredential',
            'reportSchedules.template',
            'generatedReports' => fn ($query) => $query->latest()->limit(5),
        ]);

        $connections = $client->platformConnections->sortBy(['platform', 'connection_name'])->values();
        $channels = $this->channelsFromConnections($connections->all());
        $type = $this->reportTypeForRange($from, $to);
        $template = $this->recommendedTemplate($client, $type, $channels);
        $latestReport = $client->generatedReports->first();

        $connectionRows = $connections->map(fn (PlatformConnection $connection) => $this->connectionStatusRow($connection))->values()->all();
        $previewOk = collect($connectionRows)->where('last_preview_ok', true)->count();
        $qualityOk = collect($connectionRows)->where('last_quality_status', 'ok')->count();

        return [
            'from' => $from,
            'to' => $to,
            'report_type' => $type,
            'channels' => $channels,
            'connections' => $connectionRows,
            'template' => $template,
            'templates' => ReportTemplate::query()
                ->where(function ($query) use ($client) {
                    $query->whereNull('client_id')->orWhere('client_id', $client->id);
                })
                ->active()
                ->orderBy('sort_order')
                ->orderBy('name')
                ->get(),
            'latest_report' => $latestReport,
            'readiness' => [
                'connection_count' => $connections->count(),
                'credential_count' => $connections->whereNotNull('secure_credential_id')->count(),
                'active_count' => $connections->where('status', 'active')->count(),
                'preview_ok' => $previewOk,
                'quality_ok' => $qualityOk,
                'has_template' => (bool) $template,
                'has_schedule' => $client->reportSchedules->isNotEmpty(),
                'score' => $this->readinessScore($connections->count(), $connections->whereNotNull('secure_credential_id')->count(), $previewOk, $qualityOk, (bool) $template),
            ],
        ];
    }

    public function preview(Client $client, string $from, string $to, int $limit = 10, array $connectionIds = []): array
    {
        [$from, $to] = $this->normalizeRange($from, $to);
        $connections = $this->connections($client, $connectionIds);
        $items = [];

        foreach ($connections as $connection) {
            try {
                $items[] = $this->previewService->preview($connection, $from, $to, $limit)->toArray() + [
                    'connection_id' => $connection->id,
                    'connection_name' => $connection->connection_name,
                ];
            } catch (Throwable $exception) {
                report($exception);
                $items[] = [
                    'ok' => false,
                    'connection_id' => $connection->id,
                    'connection_name' => $connection->connection_name,
                    'platform' => $connection->platform,
                    'from' => $from,
                    'to' => $to,
                    'message' => 'Önizleme başarısız.',
                    'error' => $exception->getMessage(),
                    'totals' => [],
                    'rows' => [],
                    'diagnostics' => [],
                    'duration_ms' => 0,
                    'previewed_at' => now()->toIso8601String(),
                ];
            }
        }

        return $this->wrapStepResult('preview', $from, $to, $items);
    }

    public function sync(Client $client, string $from, string $to, array $connectionIds = []): array
    {
        [$from, $to] = $this->normalizeRange($from, $to);
        $connections = $this->connections($client, $connectionIds);
        $items = [];

        foreach ($connections as $connection) {
            try {
                $result = $this->syncManager->syncConnection($connection, $from, $to);
                $items[] = $result->toArray() + [
                    'connection_id' => $connection->id,
                    'connection_name' => $connection->connection_name,
                    'platform' => $connection->platform,
                ];
            } catch (Throwable $exception) {
                report($exception);
                $items[] = [
                    'ok' => false,
                    'connection_id' => $connection->id,
                    'connection_name' => $connection->connection_name,
                    'platform' => $connection->platform,
                    'message' => 'Senkronizasyon başarısız.',
                    'error' => $exception->getMessage(),
                    'counts' => [],
                    'total_rows' => 0,
                    'duration_ms' => 0,
                ];
            }
        }

        return $this->wrapStepResult('sync', $from, $to, $items);
    }

    public function quality(Client $client, string $from, string $to, bool $save = true): array
    {
        [$from, $to] = $this->normalizeRange($from, $to);
        return $this->qualityService->analyze($client->id, null, null, $from, $to, $save);
    }

    public function generateReport(Client $client, array $data, ?int $userId = null): GeneratedReport
    {
        [$from, $to] = $this->normalizeRange($data['date_from'] ?? null, $data['date_to'] ?? null);
        $channels = $this->templateDefaults->normalizeChannels($data['channels'] ?? []);
        $type = $data['report_type'] ?? $this->reportTypeForRange($from, $to);
        $templateId = ! empty($data['report_template_id']) ? (int) $data['report_template_id'] : null;

        return $this->reportGenerator->createManualReport(
            $client,
            $type,
            Carbon::parse($from),
            Carbon::parse($to),
            $channels,
            $userId,
            $templateId,
        );
    }

    public function generateAi(GeneratedReport $report, bool $forceLocal = false, bool $regenerateFiles = true): GeneratedReport
    {
        $report = $this->insightGenerator->generate($report, $forceLocal);

        if ($regenerateFiles && $report->ai_status === 'completed') {
            $report = $this->reportGenerator->generate($report);
        }

        return $report;
    }

    public function sendTest(GeneratedReport $report, ?string $to = null, array $options = [])
    {
        return $this->mailer->sendReport($report, [
            'recipients' => $to ?: $this->defaultRecipients($report->client),
            'cc' => null,
            'bcc' => null,
            'subject' => $options['subject'] ?? null,
            'note' => $options['note'] ?? 'İlk rapor sihirbazı üzerinden test gönderimidir.',
            'include_pdf' => (bool) ($options['include_pdf'] ?? true),
            'include_excel' => (bool) ($options['include_excel'] ?? false),
            'include_csv' => (bool) ($options['include_csv'] ?? false),
            'include_html' => (bool) ($options['include_html'] ?? true),
        ]);
    }

    public function runAll(Client $client, array $data, ?int $userId = null): array
    {
        [$from, $to] = $this->normalizeRange($data['date_from'] ?? null, $data['date_to'] ?? null);
        $connectionIds = array_map('intval', $data['connection_ids'] ?? []);
        $steps = [];

        if (! empty($data['run_preview'])) {
            $steps['preview'] = $this->preview($client, $from, $to, (int) ($data['limit'] ?? 10), $connectionIds);
        }

        if (! empty($data['run_sync'])) {
            $steps['sync'] = $this->sync($client, $from, $to, $connectionIds);
        }

        if (! empty($data['run_quality'])) {
            $steps['quality'] = $this->quality($client, $from, $to, true);
        }

        $report = null;
        if (! empty($data['run_report'])) {
            $report = $this->generateReport($client, $data, $userId);
            $steps['report'] = [
                'ok' => $report->status === 'completed',
                'report_id' => $report->id,
                'status' => $report->status,
                'error' => $report->error_message,
            ];
        }

        if ($report && ! empty($data['run_ai'])) {
            $report = $this->generateAi($report, ! empty($data['force_local_ai']), true);
            $steps['ai'] = [
                'ok' => $report->ai_status === 'completed',
                'status' => $report->ai_status,
                'provider' => $report->ai_provider,
                'error' => $report->ai_error_message,
            ];
        }

        if ($report && ! empty($data['send_test'])) {
            $emailLog = $this->sendTest($report, $data['test_recipient'] ?? null, $data);
            $steps['mail'] = [
                'ok' => $emailLog->status === 'sent',
                'status' => $emailLog->status,
                'error' => $emailLog->error_message,
            ];
        }

        return [
            'from' => $from,
            'to' => $to,
            'report' => $report,
            'steps' => $steps,
            'ok' => collect($steps)->every(fn ($step) => Arr::get($step, 'ok', true) !== false),
        ];
    }

    public function normalizeRange(?string $from, ?string $to): array
    {
        [$defaultFrom, $defaultTo] = $this->defaultRange();
        $fromDate = Carbon::parse($from ?: $defaultFrom)->startOfDay();
        $toDate = Carbon::parse($to ?: $defaultTo)->startOfDay();

        if ($toDate->lt($fromDate)) {
            $toDate = $fromDate->copy();
        }

        return [$fromDate->toDateString(), $toDate->toDateString()];
    }

    public function reportTypeForRange(string $from, string $to): string
    {
        $days = Carbon::parse($from)->diffInDays(Carbon::parse($to)) + 1;

        return match (true) {
            $days <= 2 => 'daily',
            $days <= 10 => 'weekly',
            $days <= 45 => 'monthly',
            default => 'custom',
        };
    }

    /** @param array<int, PlatformConnection> $connections */
    private function channelsFromConnections(array $connections): array
    {
        return collect($connections)
            ->pluck('platform')
            ->intersect(['google_ads', 'meta_ads', 'search_console', 'ga4'])
            ->unique()
            ->values()
            ->all() ?: ['google_ads', 'meta_ads', 'search_console', 'ga4'];
    }

    private function recommendedTemplate(Client $client, string $type, array $channels): ?ReportTemplate
    {
        return ReportTemplate::query()
            ->where(function ($query) use ($client) {
                $query->whereNull('client_id')->orWhere('client_id', $client->id);
            })
            ->where('type', $type)
            ->active()
            ->orderByRaw('client_id IS NULL')
            ->orderBy('sort_order')
            ->first()
            ?: ReportTemplate::active()->orderBy('sort_order')->first();
    }

    private function connectionStatusRow(PlatformConnection $connection): array
    {
        $preview = $connection->meta['last_preview_result'] ?? $connection->meta['last_preview'] ?? null;
        $quality = $connection->meta['last_quality_check'] ?? null;
        $sync = $connection->meta['last_sync_result'] ?? null;

        return [
            'id' => $connection->id,
            'platform' => $connection->platform,
            'platform_label' => $this->platformLabel($connection->platform),
            'name' => $connection->connection_name,
            'account_id' => $connection->account_id,
            'status' => $connection->status,
            'has_credential' => (bool) $connection->secure_credential_id,
            'last_sync_at' => $connection->last_sync_at?->format('Y-m-d H:i'),
            'last_error' => $connection->last_error,
            'last_preview_ok' => (bool) Arr::get($preview, 'ok', false),
            'last_preview_at' => Arr::get($preview, 'previewed_at'),
            'last_quality_status' => Arr::get($quality, 'status'),
            'last_quality_score' => Arr::get($quality, 'score'),
            'last_quality_label' => Arr::get($quality, 'status_label'),
            'last_sync_ok' => (bool) Arr::get($sync, 'ok', false),
            'last_sync_rows' => Arr::get($sync, 'total_rows'),
        ];
    }

    private function connections(Client $client, array $ids = [])
    {
        return PlatformConnection::query()
            ->with(['client', 'secureCredential'])
            ->where('client_id', $client->id)
            ->when(! empty($ids), fn ($query) => $query->whereIn('id', $ids))
            ->orderBy('platform')
            ->orderBy('connection_name')
            ->get();
    }

    private function wrapStepResult(string $step, string $from, string $to, array $items): array
    {
        return [
            'step' => $step,
            'from' => $from,
            'to' => $to,
            'ok' => collect($items)->every(fn ($item) => (bool) Arr::get($item, 'ok', false)),
            'success_count' => collect($items)->where('ok', true)->count(),
            'error_count' => collect($items)->where('ok', false)->count(),
            'items' => $items,
            'ran_at' => now()->toIso8601String(),
        ];
    }

    private function readinessScore(int $connections, int $credentials, int $previewOk, int $qualityOk, bool $hasTemplate): int
    {
        $score = 0;
        if ($connections > 0) { $score += 20; }
        if ($credentials > 0) { $score += 20; }
        if ($previewOk > 0) { $score += 20; }
        if ($qualityOk > 0) { $score += 25; }
        if ($hasTemplate) { $score += 15; }

        return min(100, $score);
    }

    private function defaultRecipients(?Client $client): ?string
    {
        if (! $client) {
            return null;
        }

        if (is_array($client->report_recipients) && ! empty($client->report_recipients)) {
            return implode(',', $client->report_recipients);
        }

        return $client->email;
    }

    private function platformLabel(string $platform): string
    {
        return match ($platform) {
            'google_ads' => 'Google Ads',
            'meta_ads' => 'Meta Ads',
            'search_console' => 'Search Console',
            'ga4' => 'GA4',
            default => strtoupper(str_replace('_', ' ', $platform)),
        };
    }
}
