<?php

namespace App\Modules\Billing;

use Illuminate\Support\Facades\Http;
use Illuminate\Support\Facades\Log;

abstract class BaseBillingModule implements BillingModuleInterface
{
    protected array $settings;

    protected array $fieldMappings;

    /**
     * PRC-1066: Modül, yenilenen OAuth token'ı InvoiceBilling.settings'e geri
     * yazamaz (modele referansı yok). Factory createFromModel'de bir callback
     * bağlar; persistTokens() bunu tetikler. Statik create() yolunda null kalır
     * (persist no-op, yalnız in-memory) — kabul edilebilir.
     *
     * @var callable|null  fn(array $delta): void
     */
    protected $tokenPersister = null;

    public function __construct(array $settings = [], array $fieldMappings = [])
    {
        $this->settings = array_merge($this->getDefaultSettings(), $settings);
        $this->fieldMappings = array_merge($this->getDefaultFieldMappings(), $fieldMappings);
    }

    /**
     * Token-yenileme persist callback'i bağla (Factory kurar).
     */
    public function onTokenRefresh(callable $cb): static
    {
        $this->tokenPersister = $cb;

        return $this;
    }

    /**
     * Yenilenen ayarları (OAuth token vb.) hem in-memory güncelle hem — callback
     * varsa — InvoiceBilling.settings'e persist et.
     */
    protected function persistTokens(array $delta): void
    {
        $this->settings = array_merge($this->settings, $delta);
        if ($this->tokenPersister) {
            ($this->tokenPersister)($delta);
        }
    }

    /**
     * İstek base URL'i. OAuth modülleri (Parasut) company_id path segmentini
     * eklemek için override eder.
     */
    protected function baseUrl(): string
    {
        return rtrim($this->settings['api_url'] ?? '', '/');
    }

    /**
     * Auth header'ları. Default statik api_key Bearer; OAuth modülleri
     * (Parasut/Logo) access_token kullanmak için override eder.
     */
    protected function authHeaders(): array
    {
        return ['Authorization' => 'Bearer '.($this->settings['api_key'] ?? '')];
    }

    /**
     * Her istekten önce çalışır (token tazeleme hook'u). Base no-op; OAuth
     * modülleri ensureToken() çağırır.
     */
    protected function beforeRequest(): void
    {
        // no-op
    }

    /**
     * HTTP isteği yap
     */
    protected function makeRequest(string $endpoint, array $data = [], string $method = 'POST'): array
    {
        try {
            $this->beforeRequest();

            $url = $this->baseUrl().'/'.ltrim($endpoint, '/');

            $response = Http::withHeaders(array_merge([
                'Content-Type' => 'application/json',
                'Accept' => 'application/json',
            ], $this->authHeaders()))->timeout(30)->$method($url, $data);

            if (! $response->successful()) {
                // PRC-1066: ham gövdeyi mesaja/log'a GÖMME (sağlayıcı yanıtı
                // hassas veri içerebilir + DB error_message'a sızar). Yalnız status.
                throw new \Exception(__('admin/modules.base_api_error', ['status' => $response->status(), 'body' => '']));
            }

            return $response->json() ?? [];

        } catch (\Exception $e) {
            Log::error("Billing module API hatası ({$this->getName()}): status/exception", [
                'module' => $this->getName(),
                'exception' => get_class($e),
            ]);
            throw $e;
        }
    }

    /**
     * GET isteği yap
     */
    protected function get(string $endpoint, array $params = []): array
    {
        return $this->makeRequest($endpoint, $params, 'GET');
    }

    /**
     * POST isteği yap
     */
    protected function post(string $endpoint, array $data = []): array
    {
        return $this->makeRequest($endpoint, $data, 'POST');
    }

    /**
     * PUT isteği yap
     */
    protected function put(string $endpoint, array $data = []): array
    {
        return $this->makeRequest($endpoint, $data, 'PUT');
    }

    /**
     * DELETE isteği yap
     */
    protected function delete(string $endpoint): array
    {
        return $this->makeRequest($endpoint, [], 'DELETE');
    }

    /**
     * SOAP transport helper (foundation — PRC-1067).
     *
     * TR e-fatura sağlayıcılarının önemli bir kısmı (İzibiz, Uyumsoft, Turkkep,
     * Foriba/Sovos vb.) SOAP/WSDL konuşur; REST metodları bunlara yetmez. Bu
     * helper PHP native SoapClient'ı sarmalar:
     *   - stdClass yanıtı düz array'e normalize eder (json round-trip),
     *   - SoapFault'u yakalar → ['success'=>false,'error'=>...] döndürür,
     *   - trace=false varsayılan (imza/parola/SESSION_ID SOAP trace'ine düşmesin).
     *
     * GÜVENLİK: yanıt gövdesi, credential, SESSION_ID veya SoapFault detail'i
     * ASLA loglanmaz — yalnız exception sınıfı damgalanır. TLS verify açık kalır
     * (SoapClient default; stream_context ile kapatılmaz).
     *
     * @param  string  $wsdl         WSDL URL'i
     * @param  string  $method       SOAP operasyon adı
     * @param  array   $args         Operasyon argümanları (tek param dizi)
     * @param  array   $soapHeaders  \SoapHeader nesneleri (auth header için)
     * @param  array   $options      SoapClient ek/override opsiyonları
     * @return array{success?: bool, error?: string}  Başarıda normalize edilmiş
     *                                                 yanıt array'i; hatada
     *                                                 success=false + error.
     */
    protected function callSoap(string $wsdl, string $method, array $args, array $soapHeaders = [], array $options = []): array
    {
        try {
            $client = new \SoapClient($wsdl, array_merge([
                'trace' => false,          // imza/parola trace'e düşmesin
                'exceptions' => true,
                'cache_wsdl' => WSDL_CACHE_MEMORY,
                'connection_timeout' => 30,
            ], $options));

            if (! empty($soapHeaders)) {
                $client->__setSoapHeaders($soapHeaders);
            }

            $result = $client->$method($args);

            // stdClass ağacını düz array'e normalize et.
            return json_decode(json_encode($result), true) ?? [];

        } catch (\SoapFault $e) {
            // faultstring hassas olabilir; loglamada YALNIZ tip damgalanır.
            Log::error("Billing module SOAP hatası ({$this->getName()}): SoapFault", [
                'module' => $this->getName(),
                'method' => $method,
                'exception' => get_class($e),
            ]);

            return [
                'success' => false,
                'error' => $e->getMessage(),
            ];
        } catch (\Throwable $e) {
            Log::error("Billing module SOAP hatası ({$this->getName()}): exception", [
                'module' => $this->getName(),
                'method' => $method,
                'exception' => get_class($e),
            ]);

            return [
                'success' => false,
                'error' => $e->getMessage(),
            ];
        }
    }

    /**
     * SOAP auth header helper (İzibiz/Uyumsoft WS-Security benzeri header'lar
     * için). $data ham dizi veya SoapVar olabilir; mustUnderstand=false.
     *
     * @param  string  $ns    Header namespace URI'si
     * @param  string  $name  Header eleman adı
     * @param  mixed   $data  Header gövdesi (dizi/SoapVar/skaler)
     */
    protected function soapHeader(string $ns, string $name, $data): \SoapHeader
    {
        return new \SoapHeader($ns, $name, $data);
    }

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

        foreach ($this->fieldMappings 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;
    }

    /**
     * Varsayılan alan eşleştirmeleri
     */
    protected function getDefaultFieldMappings(): array
    {
        return [
            'client_tax_number' => 'client.tax_number',
            'client_name' => 'client.company_name',
            'client_address' => 'client.address',
            'invoice_number' => 'invoice_number',
            'invoice_date' => 'created_at',
            'total_amount' => 'amount',
        ];
    }

    /**
     * Modül ayarlarını getir
     */
    public function getSettings(): array
    {
        return $this->settings;
    }

    /**
     * Alan eşleştirmelerini getir
     */
    public function getFieldMappings(): array
    {
        return $this->fieldMappings;
    }

    /**
     * Modül durumunu kontrol et
     */
    public function getStatus(): array
    {
        try {
            $response = $this->get('status');

            return [
                'status' => 'active',
                'message' => __('admin/modules.base_status_active'),
                'details' => $response,
            ];
        } catch (\Exception $e) {
            return [
                'status' => 'error',
                'message' => 'Modül hatası: '.$e->getMessage(),
                'details' => null,
            ];
        }
    }

    /**
     * Modül istatistiklerini getir
     */
    public function getStatistics(): array
    {
        try {
            $response = $this->get('statistics');

            return [
                'success' => true,
                'data' => $response,
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Fatura listesini getir
     */
    public function getInvoices(array $filters = []): array
    {
        try {
            $response = $this->get('invoices', $filters);

            return [
                'success' => true,
                'data' => $response,
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Ürün listesini getir
     */
    public function getProducts(array $filters = []): array
    {
        try {
            $response = $this->get('products', $filters);

            return [
                'success' => true,
                'data' => $response,
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Ürün ekle/güncelle
     */
    public function setProduct(array $productData): array
    {
        try {
            $response = $this->post('products', $productData);

            return [
                'success' => true,
                'data' => $response,
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Ürün ara
     */
    public function searchProducts(string $query, array $filters = []): array
    {
        try {
            $filters['q'] = $query;
            $response = $this->get('products/search', $filters);

            return [
                'success' => true,
                'data' => $response,
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Müşteri listesini getir
     */
    public function getCustomers(array $filters = []): array
    {
        try {
            $response = $this->get('customers', $filters);

            return [
                'success' => true,
                'data' => $response,
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Müşteri ekle/güncelle
     */
    public function setCustomer(array $customerData): array
    {
        try {
            $response = $this->post('customers', $customerData);

            return [
                'success' => true,
                'data' => $response,
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Müşteri ara
     */
    public function searchCustomers(string $query, array $filters = []): array
    {
        try {
            $filters['q'] = $query;
            $response = $this->get('customers/search', $filters);

            return [
                'success' => true,
                'data' => $response,
            ];
        } catch (\Exception $e) {
            return [
                'success' => false,
                'message' => $e->getMessage(),
            ];
        }
    }

    /**
     * Fatura durumunu sorgula — senkron modüller için varsayılan.
     * Asenkron modüller (GIB e-fatura kuyruğu vb.) bu metodu ezerek
     * gerçek durum sorgusu yapar.
     *
     * @return array{success: bool, status: 'pending'|'approved'|'rejected'}
     */
    public function checkInvoiceStatus(string $externalId, array $params = []): array
    {
        return ['success' => true, 'status' => 'approved'];
    }
}
