<?php

namespace App\Http\Controllers\Agency\Mail;

use App\Http\Controllers\Controller;
use App\Models\Customer\Customer;
use App\Models\Mail\MailAccount;
use App\Models\Mail\MailFolder;
use App\Models\Mail\MailMessage;
use App\Services\Mail\MailArchiveService;
use App\Services\Mail\MailExportService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

/**
 * Posta yedekleme ve dışa aktarma.
 *
 * Kapsam üç biçimde verilebilir: seçili iletiler, bir klasör/hesap ya da
 * tüm hesaplar; tarih aralığı ve arama terimiyle daraltılabilir. Dışa
 * aktarmadan hemen önce kapsamdaki iletilerin ham hâli yedeklenir, böylece
 * çıktı her zaman kaybı olmayan kaynaktan üretilir.
 */
class MailExportController extends Controller
{
    public function __construct(
        private MailExportService $exporter,
        private MailArchiveService $archives,
    ) {}

    private function agencyId(): int
    {
        return (int) (Auth::user()?->currentAgencyId() ?? 0);
    }

    /** Kullanıcının erişebildiği hesaplar: acente geneli + kendi hesapları. */
    private function accountIds(): array
    {
        $userId = (int) Auth::id();

        return MailAccount::where('agency_id', $this->agencyId())
            ->where(fn ($q) => $q->whereNull('user_id')->orWhere('user_id', $userId))
            ->pluck('id')->map(fn ($v) => (int) $v)->all();
    }

    // ─────────────────────────────────────────── seçenekler

    /** Dışa aktarma penceresini besleyen veri: biçimler + yedek durumu. */
    public function options(): JsonResponse
    {
        $accountIds = $this->accountIds();

        return response()->json([
            'formats' => collect(MailExportService::formats())
                ->map(fn ($meta, $key) => [
                    'key' => $key,
                    'label' => $meta['label'],
                    'note' => $meta['note'],
                    'icon' => $meta['icon'],
                    'ext' => $meta['ext'],
                    'max' => $meta['max'],
                ])->values(),
            'backup' => $this->archives->summary($accountIds),
            'max' => MailExportService::MAX_MESSAGES,
        ]);
    }

    // ─────────────────────────────────────────── yedekleme

    /** Kapsamdaki iletileri veritabanına yedekler (ham MIME + gövde + ekler). */
    public function backup(Request $request): JsonResponse
    {
        $request->validate([
            'scope' => 'nullable|in:selection,folder,account,all',
            'limit' => 'nullable|integer|min:1|max:5000',
        ]);

        $query = $this->scopedQuery($request);
        $limit = (int) $request->input('limit', 500);

        // En eski yedeklenmemişten başla: kaldığı yerden devam edebilsin
        $messages = (clone $query)
            ->whereDoesntHave('archive', fn ($q) => $q->where('is_rebuilt', false))
            ->orderBy('mail_messages.id')
            ->limit($limit)
            ->with(['account', 'folder', 'attachments'])
            ->get();

        try {
            $stats = $this->archives->archiveMany($messages);
        } finally {
            $this->archives->closeConnections();
        }

        $summary = $this->archives->summary($this->accountIds());

        return response()->json([
            'ok' => true,
            'message' => $stats['archived'].' ileti yedeklendi'
                .($stats['failed'] ? ', '.$stats['failed'].' tanesi alınamadı' : '').'.',
            'stats' => $stats,
            'backup' => $summary,
            'has_more' => $summary['pending'] > 0,
        ]);
    }

    // ─────────────────────────────────────────── dışa aktarma

    public function export(Request $request): BinaryFileResponse|JsonResponse
    {
        $request->validate([
            'format' => 'required|string',
            'scope' => 'nullable|in:selection,folder,account,all,customer',
        ]);

        $format = (string) $request->input('format');
        if (! MailExportService::supports($format)) {
            return response()->json(['ok' => false, 'message' => 'Desteklenmeyen biçim.'], 422);
        }

        // Sınır biçime göre değişir: ham metni doğrudan akıtan MBOX çok sayıda
        // iletiyi kaldırır, tüm belgeyi belleğe kuran PDF kaldıramaz.
        $limit = MailExportService::limitFor($format);
        $scoped = $this->scopedQuery($request);
        $available = (clone $scoped)->count();

        $messages = $scoped
            ->with(['account:id,name,email', 'folder:id,name,type', 'attachments', 'archive'])
            ->orderBy('mail_messages.date_at')
            ->limit($limit)
            ->get();

        if ($messages->isEmpty()) {
            return response()->json(['ok' => false, 'message' => 'Seçilen kapsamda ileti yok.'], 422);
        }

        if ($available > $limit) {
            return response()->json([
                'ok' => false,
                'message' => sprintf(
                    'Kapsamda %s ileti var; %s biçimi tek seferde en çok %s ileti taşıyabilir. '
                    .'Tarih aralığını daraltın ya da MBOX / CSV gibi daha yüksek sınırlı bir biçim seçin.',
                    number_format($available, 0, ',', '.'),
                    MailExportService::formats()[$format]['label'],
                    number_format($limit, 0, ',', '.')
                ),
            ], 422);
        }

        // Çıktı kaybı olmayan kaynaktan üretilsin: eksik yedekler tamamlanır
        try {
            $this->archives->ensureArchived($messages);
        } catch (\Throwable) {
            // Sunucuya ulaşılamazsa saklanan alanlardan yeniden kurulur
        } finally {
            $this->archives->closeConnections();
        }

        try {
            $result = $this->exporter->export($messages->fresh(['account', 'folder', 'attachments', 'archive']), $format, $this->baseName($request));
        } catch (\Throwable $e) {
            return response()->json(['ok' => false, 'message' => $e->getMessage()], 422);
        }

        return response()->download($result['path'], $result['filename'], [
            'Content-Type' => $result['mime'],
        ])->deleteFileAfterSend(true);
    }

    // ─────────────────────────────────────────── kapsam

    /** İstekteki kapsam ve süzgeçlere göre ileti sorgusu kurar. */
    private function scopedQuery(Request $request)
    {
        $accountIds = $this->accountIds();

        $query = MailMessage::query()
            ->whereIn('mail_messages.account_id', $accountIds ?: [0])
            ->visible();

        switch ($request->input('scope', 'all')) {
            case 'selection':
                $ids = collect($request->input('ids', []))->map(fn ($v) => (int) $v)->filter();
                $query->whereIn('mail_messages.id', $ids->isEmpty() ? [0] : $ids->all());
                break;

            case 'folder':
                $folder = MailFolder::whereIn('account_id', $accountIds)->find((int) $request->input('folder_id'));
                abort_if(! $folder, 404, 'Klasör bulunamadı.');
                $query->where('mail_messages.folder_id', $folder->id);
                break;

            case 'account':
                $accountId = (int) $request->input('account_id');
                abort_unless(in_array($accountId, $accountIds, true), 404, 'E-posta hesabı bulunamadı.');
                $query->where('mail_messages.account_id', $accountId);
                break;

            case 'customer':
                $customer = Customer::where('agency_id', $this->agencyId())->find((int) $request->input('customer_id'));
                abort_if(! $customer, 404, 'Müşteri bulunamadı.');
                $query->where('mail_messages.customer_id', $customer->id);
                break;
        }

        // Birleşik klasör tipi (tüm hesaplarda Gelen Kutusu gibi)
        if ($request->filled('unified')) {
            $folderIds = MailFolder::whereIn('account_id', $accountIds)
                ->where('type', (string) $request->input('unified'))->pluck('id');
            $query->whereIn('mail_messages.folder_id', $folderIds);
        }

        if ($request->filled('from_date')) {
            $query->whereDate('mail_messages.date_at', '>=', $request->date('from_date'));
        }
        if ($request->filled('to_date')) {
            $query->whereDate('mail_messages.date_at', '<=', $request->date('to_date'));
        }

        if ($term = trim((string) $request->input('q'))) {
            MailMessage::applySearch($query, $term);
        }

        return $query;
    }

    /** Dosya adının gövdesi — kapsamı okunur biçimde anlatır. */
    private function baseName(Request $request): string
    {
        return match ($request->input('scope', 'all')) {
            'selection' => 'secili-postalar',
            'folder' => 'klasor-'.(MailFolder::find((int) $request->input('folder_id'))?->name ?: 'posta'),
            'account' => 'hesap-'.(MailAccount::find((int) $request->input('account_id'))?->name ?: 'posta'),
            'customer' => 'musteri-'.(Customer::find((int) $request->input('customer_id'))?->full_name ?: 'yazisma'),
            default => 'tum-postalar',
        };
    }
}
