<?php

namespace App\Http\Controllers\Api;

use App\Support\Help\HelpRepository;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Controller;
use Illuminate\Support\Arr;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Str;
use Laravel\Sanctum\PersonalAccessToken;
use RuntimeException;

class HelpController extends Controller
{
    public function __construct(private readonly HelpRepository $repository) {}

    public function index(Request $request): JsonResponse
    {
        [$audience, $locale] = $this->resolveContext($request);

        $articles = $this->getArticles($audience, $locale)
            ->map(fn ($article) => Arr::only($article, config('help.list_fields')))
            ->values();

        return response()->json([
            'data' => $articles,
        ]);
    }

    public function show(Request $request, string $slug): JsonResponse
    {
        [$audience, $locale] = $this->resolveContext($request);

        $article = $this->getArticle($audience, $locale, $slug);

        abort_if(! $article, 404, 'Help article not found.');

        return response()->json([
            'data' => $article,
        ]);
    }

    /**
     * @return array{string, string}
     */
    private function resolveContext(Request $request): array
    {
        $this->attemptTokenAuthentication($request);

        $audience = Str::of($request->string('audience', 'guest'))->lower()->value();
        $locale = Str::of($request->string('locale', config('help.default_locale')))->lower()->value();

        if ($audience === 'admin' && ! $request->user()) {
            abort(401, 'Authentication required for admin help content.');
        }

        if (! in_array($audience, config('help.audiences', []), true)) {
            abort(400, 'Invalid audience supplied.');
        }

        return [$audience, $locale];
    }

    private function attemptTokenAuthentication(Request $request): void
    {
        if ($request->user()) {
            return;
        }

        $bearer = $request->bearerToken();

        if (! $bearer) {
            return;
        }

        $token = PersonalAccessToken::findToken($bearer);

        if (! $token) {
            return;
        }

        $user = $token->tokenable;

        if (! $user) {
            return;
        }

        if (method_exists($user, 'withAccessToken')) {
            $user->withAccessToken($token);
        }

        Auth::setUser($user);
        $request->setUserResolver(fn () => $user);
    }

    private function getArticles(string $audience, string $locale)
    {
        try {
            return $this->repository->list($audience, $locale);
        } catch (RuntimeException $e) {
            $fallback = config('help.fallback_locale');

            if ($locale === $fallback) {
                throw $e;
            }

            return $this->repository->list($audience, $fallback);
        }
    }

    private function getArticle(string $audience, string $locale, string $slug): ?array
    {
        try {
            $article = $this->repository->find($audience, $locale, $slug);
        } catch (RuntimeException $e) {
            $fallback = config('help.fallback_locale');

            if ($locale !== $fallback) {
                return $this->repository->find($audience, $fallback, $slug);
            }

            throw $e;
        }

        return $article;
    }
}
