<?php

namespace App\Services;

use App\Models\Invoice;
use App\Models\InvoiceBilling;
use App\Models\InvoiceBillingJob;
use App\Modules\Billing\BillingModuleFactory;
use App\Notifications\BillingFailureNotification;
use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Notification;

class InvoiceBillingService
{
    /**
     * Faturalandırma işini işle
     */
    public function processBillingJob(InvoiceBillingJob $job): bool
    {
        try {
            $module = $job->billingModule;
            $invoice = $job->invoice;

            // PRC-673 — muhasebe-dışı fatura (0.00 tutar / manuel override) bu
            // legacy motorda da hiçbir modüle sevk edilmez. Tek karar: guard.
            if ($invoice && app(\App\Services\Billing\AccountingExclusionGuard::class)->excludes($invoice)) {
                $job->update([
                    'status' => 'cancelled',
                    'error_message' => 'Accounting-excluded (0.00 total / manual override) — legal dispatch skipped',
                    'processed_at' => now(),
                ]);
                return false;
            }

            $job->update(['status' => InvoiceBillingJob::STATUS_PROCESSING]);

            if (! $module || ! $module->is_active) {
                throw new \Exception(__('admin/common.messages_module_not_active'));
            }

            // Modül tipine göre işlem yap
            switch ($module->type) {
                case InvoiceBilling::TYPE_E_INVOICE:
                    $result = $this->processEInvoice($job, $module, $invoice);
                    break;
                case InvoiceBilling::TYPE_E_ARCHIVE:
                    $result = $this->processEArchive($job, $module, $invoice);
                    break;
                default:
                    throw new \Exception(__('admin/common.messages_unsupported_module_type', ['type' => $module->type]));
            }

            if ($result) {
                $job->update([
                    'status' => InvoiceBillingJob::STATUS_SUCCESS,
                    'processed_at' => now(),
                    'response_data' => $result,
                ]);

                // Modül son senkronizasyon tarihini güncelle
                $module->update([
                    'last_sync_at' => now(),
                    'last_error' => null,
                ]);

                return true;
            }

            return false;

        } catch (\Exception $e) {
            $this->handleBillingError($job, $e->getMessage());

            return false;
        }
    }

    /**
     * E-Fatura işlemi
     */
    protected function processEInvoice(InvoiceBillingJob $job, InvoiceBilling $module, Invoice $invoice): array
    {
        try {
            // Modül sınıfını oluştur
            $billingModule = BillingModuleFactory::createFromModel($module);

            // Fatura verilerini hazırla
            $data = $this->prepareInvoiceData($invoice, $module);

            // Modül üzerinden E-Fatura oluştur
            $result = $billingModule->createEInvoice($data);

            return $result;

        } catch (\Exception $e) {
            Log::error('E-Fatura işleme hatası: '.$e->getMessage());
            throw $e;
        }
    }

    /**
     * E-Arşiv Fatura işlemi
     */
    protected function processEArchive(InvoiceBillingJob $job, InvoiceBilling $module, Invoice $invoice): array
    {
        try {
            // Modül sınıfını oluştur
            $billingModule = BillingModuleFactory::createFromModel($module);

            // Fatura verilerini hazırla
            $data = $this->prepareInvoiceData($invoice, $module);

            // Modül üzerinden E-Arşiv Fatura oluştur
            $result = $billingModule->createEArchive($data);

            return $result;

        } catch (\Exception $e) {
            Log::error('E-Arşiv Fatura işleme hatası: '.$e->getMessage());
            throw $e;
        }
    }

    // PRC-1066: callBillingApi() kaldırıldı — ÖLÜ KOD (hiçbir caller yok) +
    // paralel/legacy transport (Bearer api_key hardcode + ham gövde-log leak).
    // Aktif yol modül üzerinden gider: $billingModule->createEInvoice/createEArchive().

    /**
     * Fatura verilerini hazırla
     *
     * PRC-889 — `public`: ProcessInvoiceBillingJob::handle() bu metodu farklı
     * sınıf kapsamından çağırıyor. `protected` iken PHP Error fırlıyor,
     * handle()'ın catch(\Throwable)'ı yutuyor ve iş sessizce FAILED oluyordu
     * (modül hiç çağrılmadan). Görünürlük genişletildi.
     */
    public function prepareInvoiceData(Invoice $invoice, InvoiceBilling $module): array
    {
        // Kanonik şekil için ilişkiler yüklü olmalı (items + client). N+1 ve
        // "ilişki yüklenmemiş → boş kalem" tuzağını önler.
        $invoice->loadMissing(['items', 'client', 'client.country', 'client.city']);

        // KANONİK FATURA VERİ ŞEKLİ (InvoiceBillingService emit eder;
        // UblTrInvoiceBuilder + tüm billing modülleri tüketir — HERKES BUNA UYAR).
        // Mevcut çağıranları bozmamak için legacy alanlar da korunur; yalnız
        // yeni kanonik alanlar EKLENİR.
        $data = [
            // --- kanonik header ---
            'invoice_id' => $invoice->id,
            'invoice_number' => $invoice->invoice_number,
            'issue_date' => optional($invoice->created_at)->format('Y-m-d'),
            // Kanonik ISO kod ('TRY') — currency ilişki modeli DEĞİL string döner
            // (UBL DocumentCurrencyCode + entegratörler ISO bekler).
            'currency' => is_object($invoice->currency)
                ? ($invoice->currency->code ?? 'TRY')
                : ((string) ($invoice->currency ?: 'TRY')),
            'subtotal' => (float) $invoice->subtotal,
            'tax_amount' => (float) $invoice->tax_amount,
            'tax_rate' => (float) $invoice->tax_rate,
            'discount_amount' => (float) $invoice->discount_amount,
            'total_amount' => (float) $invoice->total_amount,

            // --- legacy alanlar (geriye-uyum; mevcut createEInvoice çağıranları) ---
            'amount' => $invoice->amount,
            'status' => $invoice->status,
            'due_date' => $invoice->due_date,
            'created_at' => $invoice->created_at,
        ];

        // --- kanonik kalemler (invoice->items ilişkisinden) ---
        $data['items'] = $invoice->items->map(function ($item) {
            return [
                'description' => (string) $item->description,
                'quantity' => (float) $item->quantity,
                'unit_price' => (float) $item->unit_price,
                'tax_rate' => (float) $item->tax_rate,
                'tax_amount' => (float) $item->tax_amount,
                'line_total' => (float) $item->line_total,
            ];
        })->all();

        // --- kanonik müşteri (VKN/TCKN = tax_id; ad-soyad + firma; adres) ---
        if ($invoice->client) {
            $client = $invoice->client;

            $addressParts = array_filter([$client->address1, $client->address2]);

            $data['client'] = [
                'id' => $client->id,
                // tax_number = VKN (kurumsal) / TCKN (bireysel). Client'ta tax_id.
                'tax_number' => $client->tax_id,
                // title = fatura ünvanı: firma varsa firma, yoksa ad-soyad.
                'title' => $client->company_name
                    ?: trim("{$client->first_name} {$client->last_name}"),
                'first_name' => $client->first_name,
                'last_name' => $client->last_name,
                'company_name' => $client->company_name,
                // Şemada ayrı vergi dairesi kolonu yok — kanonik alan opsiyonel.
                'tax_office' => null,
                'address' => implode(' ', $addressParts) ?: null,
                'city' => optional($client->city)->name,
                'country' => optional($client->country)->common_name ?: 'Türkiye',
                'email' => $client->email,
            ];
        }

        // Modül ayarlarına göre veri eşleştirme (yalnız mapping tanımlıysa;
        // mapping kanonik şekli EZER — legacy davranış korunur).
        if (! empty($module->mapping_fields)) {
            $data = $this->applyFieldMapping($data, $module->mapping_fields);
        }

        return $data;
    }

    /**
     * Alan eşleştirmesini uygula
     */
    protected function applyFieldMapping(array $data, array $mapping): array
    {
        $mappedData = [];

        foreach ($mapping as $targetField => $sourcePath) {
            $value = $this->getFieldValue($data, $sourcePath);
            if ($value !== null) {
                $mappedData[$targetField] = $value;
            }
        }

        return $mappedData;
    }

    /**
     * Nested field değerini al
     */
    protected function getFieldValue(array $data, string $path)
    {
        $keys = explode('.', $path);
        $current = $data;

        foreach ($keys as $key) {
            if (! isset($current[$key])) {
                return null;
            }
            $current = $current[$key];
        }

        return $current;
    }

    /**
     * Faturalandırma hatasını işle
     */
    protected function handleBillingError(InvoiceBillingJob $job, string $errorMessage): void
    {
        $job->update([
            'status' => InvoiceBillingJob::STATUS_FAILED,
            'error_message' => $errorMessage,
            'processed_at' => now(),
        ]);

        // Modül hata durumunu güncelle
        $job->billingModule->update([
            'status' => InvoiceBilling::STATUS_ERROR,
            'last_error' => $errorMessage,
        ]);

        // Hata bildirimi gönder
        if (setting('billing_notify_on_failure', true)) {
            $this->sendFailureNotification($job, $errorMessage);
        }

        // Başarısız işleri tekrar dene
        if (setting('billing_retry_failed_jobs', true)) {
            $this->retryFailedJob($job);
        }
    }

    /**
     * Hata bildirimi gönder
     */
    protected function sendFailureNotification(InvoiceBillingJob $job, string $errorMessage): void
    {
        $email = setting('billing_failure_notification_email');

        if (empty($email)) {
            return;
        }

        try {
            Notification::route('mail', $email)
                ->notify(new BillingFailureNotification($job, $errorMessage));
        } catch (\Exception $e) {
            Log::error('Hata bildirimi gönderilemedi: '.$e->getMessage());
        }
    }

    /**
     * Başarısız işi tekrar dene
     */
    protected function retryFailedJob(InvoiceBillingJob $job): void
    {
        $maxAttempts = setting('billing_max_retry_attempts', 3);
        $attempts = InvoiceBillingJob::where('invoice_id', $job->invoice_id)
            ->where('invoice_billing_id', $job->invoice_billing_id)
            ->count();

        if ($attempts < $maxAttempts) {
            // Yeni deneme işi oluştur
            InvoiceBillingJob::create([
                'invoice_billing_id' => $job->invoice_billing_id,
                'invoice_id' => $job->invoice_id,
                'status' => InvoiceBillingJob::STATUS_PENDING,
                'type' => $job->type,
                'request_data' => $job->request_data,
                'response_data' => [],
                'error_message' => null,
                'external_id' => null,
                'external_reference' => null,
                'processed_at' => null,
            ]);
        }
    }

    /**
     * Modül test et
     */
    public function testModule(InvoiceBilling $module): array
    {
        try {
            // Modül sınıfını oluştur
            $billingModule = BillingModuleFactory::createFromModel($module);

            // Modül üzerinden test yap
            $result = $billingModule->test();

            if ($result['success']) {
                // Test başarılı
                $module->update([
                    'status' => InvoiceBilling::STATUS_ACTIVE,
                    'last_error' => null,
                    'last_sync_at' => now(),
                ]);
            } else {
                // Test başarısız
                $module->update([
                    'status' => InvoiceBilling::STATUS_ERROR,
                    'last_error' => $result['message'],
                ]);
            }

            return $result;

        } catch (\Exception $e) {
            // Test başarısız
            $module->update([
                'status' => InvoiceBilling::STATUS_ERROR,
                'last_error' => $e->getMessage(),
            ]);

            return [
                'success' => false,
                'message' => __('admin/common.messages_module_test_failed').': '.$e->getMessage(),
            ];
        }
    }

    /**
     * Manuel faturalandırma
     */
    public function manualBilling(InvoiceBilling $module, array $invoiceIds): array
    {
        $results = [];
        $successCount = 0;
        $errorCount = 0;

        foreach ($invoiceIds as $invoiceId) {
            try {
                $invoice = Invoice::findOrFail($invoiceId);

                // Manuel faturalandırma işi oluştur
                $job = InvoiceBillingJob::create([
                    'invoice_billing_id' => $module->id,
                    'invoice_id' => $invoice->id,
                    'status' => InvoiceBillingJob::STATUS_PENDING,
                    'type' => InvoiceBillingJob::TYPE_MANUAL,
                    'request_data' => $this->prepareInvoiceData($invoice, $module),
                    'response_data' => [],
                    'error_message' => null,
                    'external_id' => null,
                    'external_reference' => null,
                    'processed_at' => null,
                ]);

                // İşi hemen işle
                if ($this->processBillingJob($job)) {
                    $successCount++;
                    $results[] = [
                        'invoice_id' => $invoiceId,
                        'status' => 'success',
                        'message' => __('admin/common.messages_billing_successful'),
                    ];
                } else {
                    $errorCount++;
                    $results[] = [
                        'invoice_id' => $invoiceId,
                        'status' => 'error',
                        'message' => 'Faturalandırma başarısız',
                    ];
                }

            } catch (\Exception $e) {
                $errorCount++;
                $results[] = [
                    'invoice_id' => $invoiceId,
                    'status' => 'error',
                    'message' => 'Hata: '.$e->getMessage(),
                ];
            }
        }

        return [
            'success' => $errorCount === 0,
            'total' => count($invoiceIds),
            'success_count' => $successCount,
            'error_count' => $errorCount,
            'results' => $results,
        ];
    }
}
