<?php

declare(strict_types=1);

namespace App\Http\Controllers;

use App\Domain\Blog\AccessDecision;
use App\Domain\Blog\ArticleAccess;
use App\Domain\Seo\SeoPresenter;
use App\Models\Article;
use App\Models\Category;
use App\Models\Comment;
use App\Models\Link;
use App\Models\Tag;
use App\Settings\BlogSettings;
use App\Settings\GeneralSettings;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\View\View;

class BlogController extends Controller
{
    public function index(Request $request, GeneralSettings $settings, SeoPresenter $seo): View|RedirectResponse
    {
        // /index.php is often rewritten to / by PHP built-in server / nginx try_files.
        $action = $request->query('action');
        if ($action === 'tags' && $request->filled('item')) {
            return redirect('/tag/'.rawurlencode((string) $request->query('item')), 301);
        }

        if (is_string($action) && $action !== '') {
            return match ($action) {
                'login' => app(AuthController::class)->showLogin($settings),
                'reg' => app(AuthController::class)->showRegister($settings),
                'profile' => app(AuthController::class)->showProfile($settings),
                'links' => $this->links(),
                'comments' => $this->comments(),
                'tagslist' => $this->tagsList(),
                'search' => $this->search($request),
                'show' => $this->show($request, (int) $request->query('id'), $settings, $seo),
                'tags' => $this->tagsList(),
                default => $this->indexWithoutAction($request, $settings, $seo),
            };
        }

        return $this->indexWithoutAction($request, $settings, $seo);
    }

    protected function indexWithoutAction(Request $request, GeneralSettings $settings, SeoPresenter $seo): View
    {
        $cid = $request->integer('cid') ?: null;
        $setdate = $request->query('setdate');
        $perPage = max(1, (int) app(BlogSettings::class)->posts_per_page);

        $query = Article::query()
            ->with(['category', 'user', 'tags'])
            ->visible()
            ->published()
            ->orderByDesc('stick')
            ->orderByDesc('published_at');

        if ($cid) {
            $query->where('category_id', $cid);
        }

        if (is_string($setdate) && preg_match('/^\d{6}$/', $setdate)) {
            $year = (int) substr($setdate, 0, 4);
            $month = (int) substr($setdate, 4, 2);
            $query->whereYear('published_at', $year)->whereMonth('published_at', $month);
        }

        $articles = $query->paginate($perPage)->withQueryString();

        if ($cid) {
            $category = Category::query()->find($cid);
            if ($category !== null) {
                return view('theme::category', [
                    'category' => $category,
                    'articles' => $articles,
                    'categories' => Category::query()->orderBy('display_order')->get(),
                    'settings' => $settings,
                    'seo' => $seo->forCategory($category),
                ]);
            }
        }

        return view('theme::home', [
            'articles' => $articles,
            'categories' => Category::query()->orderBy('display_order')->get(),
            'settings' => $settings,
            'seo' => $seo->forHome(),
        ]);
    }

    public function bySlug(string $slug, GeneralSettings $settings, SeoPresenter $seo): View|RedirectResponse
    {
        $article = Article::query()
            ->visible()
            ->published()
            ->where('slug', $slug)
            ->firstOrFail();

        return redirect('/show-'.$article->id.'.shtml', 301);
    }

    public function show(Request $request, int $id, GeneralSettings $settings, SeoPresenter $seo, ArticleAccess $access): View|RedirectResponse
    {
        $article = Article::query()
            ->with(['category', 'user', 'tags', 'comments' => fn ($q) => $q->visible()->orderBy('published_at')])
            ->visible()
            ->published()
            ->findOrFail($id);

        $unlocked = (array) $request->session()->get('unlocked_articles', []);
        $passwordError = null;

        if (filled($article->read_password) && ! in_array($article->id, $unlocked, true)) {
            if ($request->isMethod('post') && hash_equals((string) $article->read_password, (string) $request->input('password'))) {
                $unlocked[] = $article->id;
                $request->session()->put('unlocked_articles', $unlocked);
            } elseif ($request->isMethod('post')) {
                $passwordError = __('frontend.article.password_error');
            }
        }

        $decision = $access->resolve($article, $request->user(), $unlocked);

        if ($decision->status === AccessDecision::NEED_PASSWORD) {
            return view('theme::password', [
                'article' => $article,
                'categories' => Category::query()->orderBy('display_order')->get(),
                'settings' => $settings,
                'seo' => $seo->forArticle($article),
                'error' => $passwordError,
            ]);
        }

        if (! $decision->isAllow()) {
            $article->increment('views');

            return view('theme::paywall', [
                'article' => $article,
                'access' => $decision,
                'bodyHtml' => $access->publicHtml($article, $decision),
                'categories' => Category::query()->orderBy('display_order')->get(),
                'settings' => $settings,
                'seo' => $seo->forArticle($article),
            ]);
        }

        $article->increment('views');

        return view('theme::article', [
            'article' => $article,
            'categories' => Category::query()->orderBy('display_order')->get(),
            'settings' => $settings,
            'seo' => $seo->forArticle($article),
        ]);
    }

    public function archives(Request $request, ?string $date = null): View
    {
        $request->merge(['setdate' => $date === 'all' ? null : $date]);

        return $this->index($request, app(GeneralSettings::class), app(SeoPresenter::class));
    }

    public function category(Request $request, int $cid, GeneralSettings $settings, SeoPresenter $seo): View
    {
        $category = Category::query()->findOrFail($cid);
        $perPage = max(1, (int) app(BlogSettings::class)->posts_per_page);

        $articles = Article::query()
            ->with(['category', 'user', 'tags'])
            ->visible()
            ->published()
            ->where('category_id', $category->id)
            ->orderByDesc('stick')
            ->orderByDesc('published_at')
            ->paginate($perPage)
            ->withQueryString();

        return view('theme::category', [
            'category' => $category,
            'articles' => $articles,
            'categories' => Category::query()->orderBy('display_order')->get(),
            'settings' => $settings,
            'seo' => $seo->forCategory($category),
        ]);
    }

    public function tagsList(): View
    {
        $tags = Tag::query()->orderByDesc('use_count')->paginate(100);

        return view('theme::tags', [
            'tags' => $tags,
            'categories' => Category::query()->orderBy('display_order')->get(),
            'settings' => app(GeneralSettings::class),
        ]);
    }

    public function tag(Request $request, ?string $name = null): View
    {
        $name = $name ?: (string) $request->query('item', '');
        $tag = Tag::query()->where('name', $name)->firstOrFail();

        $articles = $tag->articles()
            ->visible()
            ->published()
            ->orderByDesc('published_at')
            ->paginate(10);

        return view('theme::tag', [
            'tag' => $tag,
            'articles' => $articles,
            'categories' => Category::query()->orderBy('display_order')->get(),
            'settings' => app(GeneralSettings::class),
        ]);
    }

    public function comments(): View
    {
        $comments = Comment::query()
            ->with('article')
            ->visible()
            ->orderByDesc('published_at')
            ->paginate(20);

        return view('theme::comments', [
            'comments' => $comments,
            'categories' => Category::query()->orderBy('display_order')->get(),
            'settings' => app(GeneralSettings::class),
        ]);
    }

    public function search(Request $request): View
    {
        $q = trim((string) $request->query('keywords', $request->input('keywords', '')));

        $articles = Article::query()
            ->visible()
            ->published()
            ->when($q !== '', function ($query) use ($q) {
                $query->where(function ($inner) use ($q) {
                    $inner->where('title', 'like', "%{$q}%")
                        ->orWhere('content', 'like', "%{$q}%")
                        ->orWhere('description', 'like', "%{$q}%");
                });
            })
            ->orderByDesc('published_at')
            ->paginate(10)
            ->withQueryString();

        return view('theme::search', [
            'articles' => $articles,
            'keywords' => $q,
            'categories' => Category::query()->orderBy('display_order')->get(),
            'settings' => app(GeneralSettings::class),
        ]);
    }

    public function links(): View
    {
        return view('theme::links', [
            'links' => Link::query()->where('visible', true)->orderBy('display_order')->get(),
            'categories' => Category::query()->orderBy('display_order')->get(),
            'settings' => app(GeneralSettings::class),
        ]);
    }
}
