<?php

namespace App\Http\Controllers\AiPortal;

use App\Models\AiProvider;
use App\Services\AuditLogger;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Facades\Http;

/**
 * Per-purpose AI provider config — admin CRUD over the ai_providers table.
 * Each row binds an API key + model to one or more purposes (ticket_draft,
 * server_audit, server_chat, anomaly_detect, default).
 *
 * Sensitive: api_key is encrypted at rest (model cast) and only returned
 * masked. Test endpoint actually sends a tiny ping to verify the key works.
 */
class ProviderController extends Controller
{
    public function index(Request $request)
    {
        abort_unless($request->user()->isAdminOrAbove(), 403);

        $rows = AiProvider::orderByDesc('updated_at')->get()
            ->map(fn ($p) => $this->present($p));

        return response()->json([
            'providers' => $rows,
            'available_purposes' => AiProvider::PURPOSES,
            'available_providers' => [
                'anthropic'         => ['label' => 'Anthropic Claude', 'sample_models' => ['claude-opus-4-7', 'claude-sonnet-4-6', 'claude-haiku-4-5']],
                'openai'            => ['label' => 'OpenAI',           'sample_models' => ['gpt-4o', 'gpt-4o-mini', 'o1-mini']],
                'google'            => ['label' => 'Google Gemini',    'sample_models' => ['gemini-1.5-pro', 'gemini-1.5-flash']],
                'openai_compatible' => ['label' => 'OpenAI-compatible (Together / Groq / OpenRouter / Ollama)',
                                        'sample_models' => ['meta-llama/Llama-3.3-70B', 'mistral-large-latest']],
            ],
        ]);
    }

    public function store(Request $request)
    {
        abort_unless($request->user()->isAdminOrAbove(), 403);

        $data = $request->validate([
            'label'    => ['required', 'string', 'max:128'],
            'provider' => ['required', 'in:anthropic,openai,google,openai_compatible'],
            'auth_type'=> ['nullable', 'in:api_key,oauth'],
            'model'    => ['required', 'string', 'max:128'],
            'api_key'  => ['required', 'string', 'min:8'],
            'base_url' => ['nullable', 'url', 'max:255'],
            'purposes' => ['required', 'array', 'min:1'],
            'purposes.*' => ['string', 'in:' . implode(',', array_keys(AiProvider::PURPOSES))],
            'enabled'  => ['nullable', 'boolean'],
        ]);

        $row = AiProvider::create([
            ...$data,
            'auth_type'  => $data['auth_type'] ?? 'api_key',
            'enabled'    => $data['enabled'] ?? true,
            'created_by' => $request->user()->id,
        ]);

        AuditLogger::log('user', (string) $request->user()->id, $request->user()->email,
            'ai.provider_added', 'ai_provider', (string) $row->id,
            ['provider' => $data['provider'], 'auth_type' => $row->auth_type,
             'model' => $data['model'], 'purposes' => $data['purposes']], $request);

        return response()->json($this->present($row), 201);
    }

    public function update(Request $request, AiProvider $provider)
    {
        abort_unless($request->user()->isAdminOrAbove(), 403);

        $data = $request->validate([
            'label'    => ['nullable', 'string', 'max:128'],
            'provider' => ['nullable', 'in:anthropic,openai,google,openai_compatible'],
            'auth_type'=> ['nullable', 'in:api_key,oauth'],
            'model'    => ['nullable', 'string', 'max:128'],
            // Empty string = "leave existing key untouched", per the masked-display flow.
            'api_key'  => ['nullable', 'string'],
            'base_url' => ['nullable', 'string', 'max:255'],
            'purposes' => ['nullable', 'array', 'min:1'],
            'purposes.*' => ['string', 'in:' . implode(',', array_keys(AiProvider::PURPOSES))],
            'enabled'  => ['nullable', 'boolean'],
        ]);

        // Drop empty api_key so the existing one is preserved.
        if (array_key_exists('api_key', $data) && trim((string) $data['api_key']) === '') {
            unset($data['api_key']);
        }

        $provider->fill(array_filter($data, fn ($v) => $v !== null));
        $provider->save();

        AuditLogger::log('user', (string) $request->user()->id, $request->user()->email,
            'ai.provider_updated', 'ai_provider', (string) $provider->id,
            ['changed' => array_keys($data)], $request);

        return response()->json($this->present($provider->fresh()));
    }

    public function destroy(Request $request, AiProvider $provider)
    {
        abort_unless($request->user()->isAdminOrAbove(), 403);

        AuditLogger::log('user', (string) $request->user()->id, $request->user()->email,
            'ai.provider_removed', 'ai_provider', (string) $provider->id,
            ['provider' => $provider->provider, 'label' => $provider->label], $request);

        $provider->delete();
        return response()->noContent();
    }

    /**
     * Send a tiny request to the configured provider to verify the key
     * works. Records the result on the row so the UI can show last-tested
     * status without re-pinging on every render.
     */
    public function test(Request $request, AiProvider $provider)
    {
        abort_unless($request->user()->isAdminOrAbove(), 403);

        $start = microtime(true);
        $result = $this->ping($provider);
        $latency = (int) ((microtime(true) - $start) * 1000);

        $provider->update([
            'last_tested_at'   => now(),
            'last_test_ok'     => $result['ok'],
            'last_test_result' => substr(($result['ok'] ? 'OK · ' : 'FAIL · ') . ($result['msg'] ?? ''), 0, 250),
        ]);

        AuditLogger::log('user', (string) $request->user()->id, $request->user()->email,
            'ai.provider_tested', 'ai_provider', (string) $provider->id,
            ['ok' => $result['ok'], 'latency_ms' => $latency], $request);

        return response()->json([
            'ok'         => $result['ok'],
            'msg'        => $result['msg'],
            'latency_ms' => $latency,
            'provider'   => $this->present($provider->fresh()),
        ]);
    }

    private function http(): \Illuminate\Http\Client\PendingRequest
    {
        $c = Http::timeout(15);
        if (! config('hsap.ai.ssl_verify', true)) $c = $c->withoutVerifying();
        return $c;
    }

    private function ping(AiProvider $p): array
    {
        try {
            switch ($p->provider) {
                case 'anthropic':
                    // OAuth subscription tokens use Bearer + the oauth-beta
                    // header; API keys use x-api-key.
                    $headers = $p->auth_type === 'oauth'
                        ? ['authorization' => 'Bearer ' . $p->api_key,
                           'anthropic-beta' => 'oauth-2025-04-20',
                           'anthropic-version' => '2023-06-01',
                           'content-type' => 'application/json']
                        : ['x-api-key' => $p->api_key,
                           'anthropic-version' => '2023-06-01',
                           'content-type' => 'application/json'];

                    // OAuth subscription mode requires the system prompt to
                    // open with the Claude Code identity string — Anthropic
                    // uses it as a legitimacy fingerprint and 429s every
                    // request without it. Same shape as Router::callAnthropic.
                    $payload = [
                        'model'      => $p->model,
                        'max_tokens' => 16,
                        'messages'   => [['role' => 'user', 'content' => 'ping']],
                    ];
                    if ($p->auth_type === 'oauth') {
                        $payload['system'] = [[
                            'type' => 'text',
                            'text' => "You are Claude Code, Anthropic's official CLI for Claude.",
                        ]];
                    }

                    $r = $this->http()
                        ->withHeaders($headers)
                        ->post('https://api.anthropic.com/v1/messages', $payload);
                    if ($r->successful()) return ['ok' => true,
                        'msg' => 'Reached Anthropic via ' . ($p->auth_type === 'oauth' ? 'subscription' : 'API key') . ' — model accepted'];
                    return ['ok' => false, 'msg' => 'HTTP ' . $r->status() . ' · ' . substr($r->body(), 0, 120)];

                case 'openai':
                case 'openai_compatible':
                    $base = rtrim($p->base_url ?: 'https://api.openai.com', '/');
                    $r = $this->http()
                        ->withToken($p->api_key)
                        ->post($base . '/v1/chat/completions', [
                            'model' => $p->model, 'max_tokens' => 16,
                            'messages' => [['role' => 'user', 'content' => 'ping']],
                        ]);
                    if ($r->successful()) return ['ok' => true, 'msg' => 'Reached ' . $base . ', model accepted'];
                    return ['ok' => false, 'msg' => 'HTTP ' . $r->status() . ' · ' . substr($r->body(), 0, 120)];

                case 'google':
                    // Map shorthand model names to real Gemini ids — same as Router.
                    $model = $p->model;
                    if (! str_starts_with($model, 'gemini-')) {
                        $model = str_contains($model, 'pro') ? 'gemini-1.5-pro' : 'gemini-1.5-flash';
                    }
                    $r = $this->http()
                        ->post("https://generativelanguage.googleapis.com/v1beta/models/{$model}:generateContent?key={$p->api_key}", [
                            'contents' => [['parts' => [['text' => 'ping']]]],
                        ]);
                    if ($r->successful()) return ['ok' => true, 'msg' => 'Reached Google AI, model accepted'];
                    return ['ok' => false, 'msg' => 'HTTP ' . $r->status() . ' · ' . substr($r->body(), 0, 120)];
            }
            return ['ok' => false, 'msg' => 'Unsupported provider type'];
        } catch (\Throwable $e) {
            return ['ok' => false, 'msg' => substr($e->getMessage(), 0, 200)];
        }
    }

    private function present(AiProvider $p): array
    {
        return [
            'id'               => $p->id,
            'label'            => $p->label,
            'provider'         => $p->provider,
            'auth_type'        => $p->auth_type ?: 'api_key',
            'model'            => $p->model,
            'api_key_masked'   => $p->maskedKey(),
            'base_url'         => $p->base_url,
            'oauth_account'    => $p->oauth_account,
            'purposes'         => $p->purposes,
            'enabled'          => $p->enabled,
            'last_tested_at'   => $p->last_tested_at?->toIso8601String(),
            'last_test_ok'     => $p->last_test_ok,
            'last_test_result' => $p->last_test_result,
            'created_at'       => $p->created_at?->toIso8601String(),
            'updated_at'       => $p->updated_at?->toIso8601String(),
        ];
    }
}
