<?php

declare(strict_types=1);

namespace App\Services\Ai;

use App\Models\Tenant;

/**
 * Single source of truth for "where does the OpenAI API key for this
 * tenant live?".
 *
 * The codebase had three near-identical 14-line `resolveApiKey`
 * methods in LeadEnrichmentService, AiEmailComposerService, and
 * LeadNextActionService.  Any change to the priority order (e.g.
 * adding a new settings key) had to land in three places — and
 * subtly diverged a couple of times in earlier review cycles.
 *
 * Lookup priority (first non-empty wins):
 *   1. tenants.settings.ai.openai_api_key   (preferred per-tenant key)
 *   2. tenants.settings.openai.api_key      (older nested layout)
 *   3. tenants.settings.openai_api_key      (flat legacy key)
 *   4. AiSettings::openai_api_key           (super-admin GLOBAL DB key)
 *   5. config('services.openai.api_key')    (env fallback)
 *   6. config('ai.openai.api_key')          (env OPENAI_API_KEY)
 */
final class TenantOpenAiKeyResolver
{
    /**
     * Resolve the API key for the given tenant id.  Convenience
     * wrapper for callers that already have the id but not the model.
     */
    public function resolveForId(?int $tenantId): ?string
    {
        if (! $tenantId) {
            return $this->scriptFallback();
        }

        $tenant = Tenant::find($tenantId);
        return $tenant ? $this->resolve($tenant) : $this->scriptFallback();
    }

    public function resolve(Tenant $tenant): ?string
    {
        $settings = (array) ($tenant->settings ?? []);
        $tenantKey = data_get($settings, 'ai.openai_api_key')
            ?? data_get($settings, 'openai.api_key')
            ?? data_get($settings, 'openai_api_key');

        if (is_string($tenantKey) && $tenantKey !== '') {
            return $tenantKey;
        }

        return $this->scriptFallback();
    }

    private function scriptFallback(): ?string
    {
        // 1. DB-backed GLOBAL key set by the super-admin in Script Settings.
        //    AiSettings is WRITTEN to the tenant_id = NULL row (the SA panel has
        //    no bound tenant). It must be READ directly from that row: going
        //    through app(AiSettings::class) inside a tenant-bound request (which
        //    is exactly where every AI feature runs) resolves via the
        //    tenant-scoped settings repository — WHERE tenant_id = <this tenant>
        //    — which never matches the global NULL row, so Spatie would fill the
        //    PHP default (null) and the global key would be silently dead. The
        //    direct query also sidesteps the class-keyed settings cache.
        try {
            $payload = \Illuminate\Support\Facades\DB::table('settings')
                ->whereNull('tenant_id')
                ->where('group', 'ai')
                ->where('name', 'openai_api_key')
                ->value('payload');

            if (is_string($payload)) {
                $globalKey = json_decode($payload, true); // Spatie stores the value JSON-encoded.
                if (is_string($globalKey) && $globalKey !== '') {
                    return $globalKey;
                }
            }
        } catch (\Throwable) {
            // settings table absent (pre-migration) — use the env fallback below.
        }

        // 2. Env-driven fallback. services.openai.api_key is only defined when
        //    config/services.php carries an `openai` block; ai.openai.api_key
        //    is the OPENAI_API_KEY env var and is the historical script key.
        $key = config('services.openai.api_key') ?? config('ai.openai.api_key');
        return is_string($key) && $key !== '' ? $key : null;
    }
}
