<?php

namespace App;

use Carbon\Carbon;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Support\Facades\DB;
use Mtc\ContentManager\Models\ContentCategory;
use Mtc\ContentManager\Models\ContentTag;
use Mtc\MercuryDataModels\Page;
use Mtc\MercuryDataModels\ResourceView;

class PageRepository
{
    /**
     * @param string|null $type
     * @param int $limit
     * @param string $orderBy
     * @param array|string|null $filters
     * @param $paginated
     * @return LengthAwarePaginator|Collection
     */
    private const TRENDING_DAYS = 30;

    public function listing(
        ?string $type,
        int $limit = 6,
        string $orderBy = 'published_at',
        array|string|null $filters = [],
        $paginated = false,
        array $excludeIds = []
    ) {
        $query = Page::query()
            ->with(['primaryMediaUse.media', 'categories', 'tags'])
            ->active()
            ->where(fn($query) => $query->whereNull('published_at')->orWhere('published_at', '<=', Carbon::now()))
            ->where('category', $type)
            ->when(
                !empty($excludeIds),
                fn(Builder $query) => $query->whereNotIn('pages.id', $excludeIds)
            )
            ->when(
                $filters['category'] ?? null,
                fn(Builder $query) => $query->whereHas(
                    'categories',
                    fn(Builder $indexQuery) => $indexQuery->where('slug', $filters['category'])
                )
            )
            ->when(
                $filters['tag'] ?? null,
                fn(Builder $query) => $query->whereHas(
                    'tags',
                    fn(Builder $indexQuery) => $indexQuery->where('slug', $filters['tag'])
                )
            );

        $this->applyOrdering($query, $orderBy);

        if ($paginated) {
            return $query->paginate($limit);
        } else {
            return $query->take($limit)->get();
        }
    }

    /**
     * Apply ordering to the query based on orderBy parameter
     */
    private function applyOrdering(Builder $query, string $orderBy): void
    {
        match ($orderBy) {
            'popular' => $query
                ->addSelect([
                    'total_view_count' => ResourceView::query()
                        ->select(DB::raw('COALESCE(SUM(hits), 0)'))
                        ->whereColumn('viewable_id', 'pages.id')
                        ->where('viewable_type', 'page')
                ])
                ->orderByDesc('total_view_count'),

            'trending' => $query
                ->addSelect([
                    'trending_view_count' => ResourceView::query()
                        ->select(DB::raw('COALESCE(SUM(hits), 0)'))
                        ->whereColumn('viewable_id', 'pages.id')
                        ->where('viewable_type', 'page')
                        ->where('date', '>=', Carbon::now()->subDays(self::TRENDING_DAYS))
                ])
                ->orderByDesc('trending_view_count'),

            default => $query->orderByDesc($orderBy),
        };
    }

    public function trending(
        string $type,
        int $pageCount = 5,
        ?int $days = null,
        ?string $pageSlug = null,
        string $filterType = 'author',
        bool $includeSlug = false
    ): Collection {
        $days = $days ?? self::TRENDING_DAYS;

        $authorId = null;
        if ($filterType === 'author' && $pageSlug !== null) {
            $authorId = Page::query()
                ->where('slug', $pageSlug)
                ->value('author_id');
        }

        $subquery = ResourceView::query()
            ->select(DB::raw('COALESCE(SUM(hits), 0)'))
            ->whereColumn('viewable_id', 'pages.id')
            ->where('viewable_type', 'page')
            ->when(
                $days > 0,
                fn($q) => $q->where('date', '>=', Carbon::now()->subDays($days))
            );

        return Page::query()
            ->with(['primaryMediaUse.media', 'categories', 'tags'])
            ->active()
            ->where(fn($q) => $q->whereNull('published_at')->orWhere('published_at', '<=', Carbon::now()))
            ->where('category', $type)
            ->when($pageSlug && !$includeSlug, fn($q) => $q->where('slug', '!=', $pageSlug))
            ->when($authorId, fn($q) => $q->where('author_id', $authorId))
            ->addSelect(['trending_view_count' => $subquery])
            ->orderByDesc('trending_view_count')
            ->take($pageCount)
            ->get();
    }

    /**
     * @param string|null $type
     * @return Collection
     */
    public function getCategories(?string $type): Collection
    {
        return ContentCategory::query()
            ->whereHas(
                'pages',
                fn(Builder $query) => $query->where('category', $type)->active()
            )
            ->orderBy('name')
            ->get();
    }

    /**
     * @param string|null $type
     * @return Collection
     */
    public function getTags(?string $type): Collection
    {
        return ContentTag::query()
            ->whereHas(
                'pages',
                fn(Builder $query) => $query->where('category', $type)->active()
            )
            ->orderBy('name')
            ->get();
    }

    public function latestArticle(?string $type): ?Page
    {
        return Page::query()
            ->with(['primaryMediaUse.media', 'categories', 'tags'])
            ->active()
            ->where(fn($query) => $query->whereNull('published_at')->orWhere('published_at', '<=', Carbon::now()))
            ->where('category', $type)
            ->orderByDesc('published_at')
            ->first();
    }

    /**
     * @param string|null $type
     * @param int $limit
     * @param array|null $filters
     * @return Collection
     */
    public function featured(?string $type, int $limit = 3, ?array $filters = [])
    {
        return Page::query()
            ->with(['primaryMediaUse.media', 'categories', 'tags'])
            ->active()
            ->where('featured', 1)
            ->where('category', $type)
            ->when(
                $filters['category'] ?? null,
                fn(Builder $query) => $query->whereHas(
                    'categories',
                    fn(Builder $indexQuery) => $indexQuery->where('slug', $filters['category'])
                )
            )
            ->when(
                $filters['tag'] ?? null,
                fn(Builder $query) => $query->whereHas(
                    'tags',
                    fn(Builder $indexQuery) => $indexQuery->where('slug', $filters['tag'])
                )
            )
            ->inRandomOrder()
            ->take($limit)
            ->get();
    }
}
