<?php

namespace App\Services\Store;

use App\Models\Brand;
use App\Models\Client;
use App\Models\Setting;

/**
 * PRC-840 — Brand kararlarının TEK KAYNAĞI (StorePreferenceResolver kardeşi).
 *
 * `ResolveBrand` middleware'i YALNIZ Host→Brand çözer ve `set()` çağırır; başka
 * hiçbir yerde Host okunmaz, brand kararı inline verilmez. Tema, katalog
 * görünürlüğü, dil/para varsayılanı, mail kimliği, müşteri aidiyeti — hepsi
 * buradan okunur. Yeni kod `app('brand')` KULLANMAZ (geriye-uyum alias'ı
 * geçici); `app(BrandContext::class)` kullanır.
 *
 * Request-scoped singleton (AppServiceProvider). Queue/console bağlamında brand
 * request'ten çözülemez — oralarda kimlik client.brand üzerinden çözülür (Faz 4).
 *
 * Tasarım: docs/superpowers/specs/2026-07-04-whitelabel-multibrand-design.md
 */
class BrandContext
{
    private ?Brand $brand = null;

    /** ResolveBrand middleware'inin TEK yazım noktası. */
    public function set(?Brand $brand): void
    {
        $this->brand = $brand;
    }

    public function current(): ?Brand
    {
        return $this->brand;
    }

    public function isBranded(): bool
    {
        return $this->brand !== null;
    }

    /** Aktif tema: brand teması (varsa ve geçerliyse) yoksa null (ana tema kalır). */
    public function theme(): ?string
    {
        $theme = $this->brand?->theme;

        return ($theme !== null && $theme !== '') ? $theme : null;
    }

    /**
     * Görünür ürün grubu id'leri = brand vitrini ∩ reseller izin havuzu (PRC-841).
     * Semantik: null = kısıt yok. Reseller vitrin seçmemişse izinli havuzun
     * tamamı; admin havuz tanımlamamışsa vitrin; ikisi de boşsa tümü. Kesişim
     * runtime'da hesaplanır (form validasyonu atlatılsa bile satış sızmaz).
     *
     * @return int[]|null
     */
    public function visibleProductGroupIds(): ?array
    {
        if (! $this->isBranded()) {
            return null;
        }

        $visible = $this->normalizeIntList($this->brand->visible_product_groups);
        $allowed = $this->normalizeIntList($this->brand->reseller?->allowed_product_groups);

        return $this->intersectRestrictions($visible, $allowed);
    }

    /**
     * Görünür TLD'ler = brand vitrini ∩ reseller izin havuzu — NOKTALI string
     * listesi ('.com', '.com.tr'). null = kısıt yok.
     *
     * @return string[]|null
     */
    public function visibleTlds(): ?array
    {
        if (! $this->isBranded()) {
            return null;
        }

        $normalize = fn ($list) => (is_array($list) && $list !== [])
            ? array_values(array_map(fn ($t) => '.' . ltrim((string) $t, '.'), $list))
            : null;

        $visible = $normalize($this->brand->visible_tlds);
        $allowed = $normalize($this->brand->reseller?->allowed_tlds);

        return $this->intersectRestrictions($visible, $allowed);
    }

    /** @return int[]|null */
    private function normalizeIntList(mixed $list): ?array
    {
        return (is_array($list) && $list !== [])
            ? array_values(array_map('intval', $list))
            : null;
    }

    /**
     * İki kısıt kümesinin kesişimi. null = "kısıt yok" anlamına geldiğinden:
     * ikisi null → null; biri null → diğeri; ikisi dolu → kesişim (boş kesişim
     * boş dizi döner = hiçbir şey görünmez, sessizce genişletilmez).
     */
    private function intersectRestrictions(?array $a, ?array $b): ?array
    {
        if ($a === null) {
            return $b;
        }
        if ($b === null) {
            return $a;
        }

        return array_values(array_intersect($a, $b));
    }

    /** Ürün grubunun bu bağlamda görünür olup olmadığı (guard'lar için). */
    public function productGroupVisible(?int $groupId): bool
    {
        $visible = $this->visibleProductGroupIds();

        return $visible === null || in_array((int) $groupId, $visible, true);
    }

    /** TLD'nin bu bağlamda görünür olup olmadığı (guard'lar için). */
    public function tldVisible(?string $tld): bool
    {
        $visible = $this->visibleTlds();
        if ($visible === null) {
            return true;
        }

        return in_array('.' . ltrim((string) $tld, '.'), $visible, true);
    }

    /** Misafir İLK-geliş dil varsayılanı (a2_iso) — sonrası VISITORDETAIL akışı. */
    public function defaultLanguage(): ?string
    {
        $lang = $this->brand?->default_language;

        return ($lang !== null && $lang !== '') ? strtolower($lang) : null;
    }

    /** Misafir İLK-geliş para birimi varsayılanı (code, örn TRY). */
    public function defaultCurrency(): ?string
    {
        $cur = $this->brand?->default_currency;

        return ($cur !== null && $cur !== '') ? strtoupper($cur) : null;
    }

    /**
     * Mail/görünür kimlik (Faz 4 mail whitelabel'ının tüketeceği tek nokta).
     * Branded değilse global ayarlardan.
     *
     * @return array{site_name: string, from_name: string, logo_url: ?string, base_url: string}
     */
    public function mailIdentity(): array
    {
        if ($this->isBranded()) {
            return [
                'site_name' => $this->brand->name,
                'from_name' => $this->brand->name,
                'logo_url' => $this->brand->logo,
                'base_url' => 'https://' . $this->brand->domain,
            ];
        }

        $siteName = Setting::getValue('site_name', config('app.name'));

        return [
            'site_name' => $siteName,
            'from_name' => $siteName,
            'logo_url' => null,
            'base_url' => rtrim(config('app.url'), '/'),
        ];
    }

    /**
     * PRC-556 — SEO değeri çöz: branded storefront'ta brand'in kendi override'ı
     * (varsa/doluysa), aksi halde global setting. Global settings TEK varsayılan
     * kaynağıdır; brand.seo_overrides içindeki her anahtar opsiyonel → boş/eksik
     * anahtar global'e düşer.
     *
     * array tipli anahtarlar (seo_org_sameas) için setting_array() ile oku;
     * ham getValue+in_array 500 üretir.
     */
    public function seo(string $key, $default = null)
    {
        if ($this->isBranded()) {
            $override = data_get($this->brand->seo_overrides, $key);

            if ($override !== null && $override !== '' && $override !== []) {
                return $override;
            }
        }

        $global = Setting::getValue($key, null);

        return ($global !== null && $global !== '') ? $global : $default;
    }

    /**
     * PRC-556 — Organization sameAs listesi (array). Brand override doluysa onu,
     * yoksa global seo_org_sameas'ı setting_array() üzerinden döner. Yalnız
     * http/https URL'ler geçer (JSON-LD zehirlenmesine karşı).
     *
     * @return string[]
     */
    public function seoSameAs(): array
    {
        $list = null;

        if ($this->isBranded()) {
            $override = data_get($this->brand->seo_overrides, 'seo_org_sameas');
            if (is_array($override) && $override !== []) {
                $list = $override;
            }
        }

        if ($list === null) {
            $list = setting_array('seo_org_sameas', []);
        }

        return array_values(array_filter(
            array_map(fn ($u) => trim((string) $u), $list),
            fn ($u) => $u !== '' && preg_match('#^https?://#i', $u) === 1
        ));
    }

    /**
     * Müşteri bu domain'e mi ait? (Faz 3 login kapısının tek karar noktası.)
     * Ana site: brand_id null müşteriler; branded: yalnız o brand'in müşterileri.
     */
    public function clientBelongsHere(Client $client): bool
    {
        return (int) ($client->brand_id ?? 0) === (int) ($this->brand?->id ?? 0);
    }
}
