<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api;

use App\Http\Controllers\Controller;
use App\Models\Artwork;
use App\Models\User;
use Carbon\CarbonInterface;
use App\Services\ThumbnailPresenter;
use App\Support\UsernamePolicy;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Pagination\Cursor;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use UnexpectedValueException;

/**
 * ProfileApiController
 * JSON API endpoints for Profile page v2 tabs.
 */
final class ProfileApiController extends Controller
{
    /**
     * GET /api/profile/{username}/artworks
     * Returns cursor-paginated artworks for the profile page tabs.
     * Supports: sort=latest|trending|rising|views|favs, cursor=...
     */
    public function artworks(Request $request, string $username): JsonResponse
    {
        $user = $this->resolveUser($username);
        if (! $user) {
            return response()->json(['error' => 'User not found'], 404);
        }

        $isOwner = Auth::check() && Auth::id() === $user->id;
        $sort    = $request->input('sort', 'latest');

        $query = Artwork::with([
                'user:id,name,username,level,rank',
                'user.profile:user_id,avatar_hash',
            'group:id,name,slug,avatar_path',
                'stats:artwork_id,views,downloads,favorites',
                'categories' => function ($query) {
                    $query->select('categories.id', 'categories.content_type_id', 'categories.parent_id', 'categories.name', 'categories.slug', 'categories.sort_order')
                        ->with(['contentType:id,slug,name']);
                },
            ])
            ->where('user_id', $user->id)
            ->whereNull('artworks.deleted_at');

        if (! $isOwner) {
            $query->where('artworks.is_public', true)
                ->where('artworks.is_approved', true)
                ->whereNotNull('artworks.published_at');
        }

        $query = $this->applyArtworkSort($query, $sort);

        $perPage = 24;
        $cursor = Cursor::fromEncoded($request->input('cursor'));

        try {
            $paginator = (clone $query)->cursorPaginate($perPage, ['*'], 'cursor', $cursor);
        } catch (UnexpectedValueException) {
            $originalCursor = $request->query('cursor');
            $request->query->remove('cursor');

            try {
                $paginator = (clone $query)->cursorPaginate($perPage, ['*'], 'cursor', null);
            } finally {
                if ($originalCursor !== null) {
                    $request->query->set('cursor', $originalCursor);
                }
            }
        }

        $data = collect($paginator->items())
            ->map(fn (Artwork $art) => $this->mapArtworkCardPayload($art))
            ->values();

        return response()->json([
            'data'        => $data,
            'next_cursor' => $paginator->nextCursor()?->encode(),
            'has_more'    => $paginator->hasMorePages(),
        ]);
    }

    /**
     * GET /api/profile/{username}/favourites
     * Returns cursor-paginated favourites for the profile.
     */
    public function favourites(Request $request, string $username): JsonResponse
    {
        $favouriteTable = $this->resolveFavouriteTable();
        if ($favouriteTable === null) {
            return response()->json(['data' => [], 'next_cursor' => null, 'has_more' => false]);
        }

        $user = $this->resolveUser($username);
        if (! $user) {
            return response()->json(['error' => 'User not found'], 404);
        }

        $perPage = 24;
        $offset = max(0, (int) base64_decode((string) $request->input('cursor', ''), true));

        $favIds = DB::table($favouriteTable . ' as af')
            ->join('artworks as a', 'a.id', '=', 'af.artwork_id')
            ->where('af.user_id', $user->id)
            ->whereNull('a.deleted_at')
            ->where('a.is_public', true)
            ->where('a.is_approved', true)
            ->whereNotNull('a.published_at')
            ->orderByDesc('af.created_at')
            ->orderByDesc('af.artwork_id')
            ->offset($offset)
            ->limit($perPage + 1)
            ->pluck('a.id');

        $hasMore = $favIds->count() > $perPage;
        $favIds  = $favIds->take($perPage);

        if ($favIds->isEmpty()) {
            return response()->json(['data' => [], 'next_cursor' => null, 'has_more' => false]);
        }

        $indexed = Artwork::with([
                'user:id,name,username,level,rank',
                'user.profile:user_id,avatar_hash',
            'group:id,name,slug,avatar_path',
                'stats:artwork_id,views,downloads,favorites',
                'categories' => function ($query) {
                    $query->select('categories.id', 'categories.content_type_id', 'categories.parent_id', 'categories.name', 'categories.slug', 'categories.sort_order')
                        ->with(['contentType:id,slug,name']);
                },
            ])
            ->whereIn('id', $favIds)
            ->get()
            ->keyBy('id');

        $data = $favIds
            ->filter(fn ($id) => $indexed->has($id))
            ->map(fn ($id) => $this->mapArtworkCardPayload($indexed[$id]))
            ->values();

        return response()->json([
            'data'        => $data,
            'next_cursor' => $hasMore ? base64_encode((string) ($offset + $perPage)) : null,
            'has_more'    => $hasMore,
        ]);
    }

    /**
     * GET /api/profile/{username}/stats
     * Returns profile statistics.
     */
    public function stats(Request $request, string $username): JsonResponse
    {
        $user = $this->resolveUser($username);
        if (! $user) {
            return response()->json(['error' => 'User not found'], 404);
        }

        $stats = null;
        if (Schema::hasTable('user_statistics')) {
            $stats = DB::table('user_statistics')->where('user_id', $user->id)->first();
        }

        $followerCount = 0;
        if (Schema::hasTable('user_followers')) {
            $followerCount = DB::table('user_followers')->where('user_id', $user->id)->count();
        }

        return response()->json([
            'stats'          => $stats,
            'follower_count' => $followerCount,
        ]);
    }

    private function resolveUser(string $username): ?User
    {
        $normalized = UsernamePolicy::normalize($username);
        return User::query()->whereRaw('LOWER(username) = ?', [$normalized])->first();
    }

    private function resolveFavouriteTable(): ?string
    {
        foreach (['artwork_favourites', 'user_favorites', 'artworks_favourites', 'favourites'] as $table) {
            if (Schema::hasTable($table)) {
                return $table;
            }
        }

        return null;
    }

    private function applyArtworkSort(Builder $query, string $sort): Builder
    {
        $statsColumn = match ($sort) {
            'trending' => 'profile_artwork_stats.ranking_score',
            'rising'   => 'profile_artwork_stats.heat_score',
            'views'    => 'profile_artwork_stats.views',
            'favs'     => 'profile_artwork_stats.favorites',
            default    => null,
        };

        if ($statsColumn !== null) {
            return $query
                ->leftJoin('artwork_stats as profile_artwork_stats', 'profile_artwork_stats.artwork_id', '=', 'artworks.id')
                ->select('artworks.*')
                ->selectRaw('COALESCE(' . $statsColumn . ', 0) as cursor_sort_value')
                ->orderByDesc('cursor_sort_value')
                ->orderByDesc('published_at')
                ->orderByDesc('id');
        }

        return $query
            ->orderByDesc('published_at')
            ->orderByDesc('id');
    }

    /**
     * @return array<string, mixed>
     */
    private function mapArtworkCardPayload(Artwork $art): array
    {
        $present = ThumbnailPresenter::present($art, 'md');
        $category = $art->categories->first();
        $contentType = $category?->contentType;
        $stats = $art->stats;
        $group = $art->group;
        $isGroupPublisher = $group !== null;
        $displayName = $isGroupPublisher ? ($group->name ?? 'Skinbase') : ($art->user?->name ?? 'Skinbase');
        $username = $isGroupPublisher ? null : ($art->user?->username ?? null);
        $avatarUrl = $isGroupPublisher ? $group->avatarUrl() : ($art->user?->profile?->avatar_url ?? null);
        $profileUrl = $isGroupPublisher
            ? $group->publicUrl()
            : ($username ? '/@' . $username : null);
        $publisherType = $isGroupPublisher ? 'group' : 'user';

        return [
            'id' => $art->id,
            'name' => $art->title,
            'thumb' => $present['url'],
            'thumb_srcset' => $present['srcset'] ?? $present['url'],
            'width' => $art->width,
            'height' => $art->height,
            'username' => $username,
            'uname' => $displayName,
            'avatar_url' => $avatarUrl,
            'profile_url' => $profileUrl,
            'published_as_type' => $publisherType,
            'publisher' => [
                'type' => $publisherType,
                'id' => $isGroupPublisher ? (int) $group->id : (int) ($art->user?->id ?? 0),
                'name' => $displayName,
                'username' => $username ?? '',
                'avatar_url' => $avatarUrl,
                'profile_url' => $profileUrl,
            ],
            'user_id' => $art->user_id,
            'author_level' => $isGroupPublisher ? 0 : (int) ($art->user?->level ?? 1),
            'author_rank' => $isGroupPublisher ? '' : (string) ($art->user?->rank ?? 'Newbie'),
            'content_type' => $contentType?->name,
            'content_type_slug' => $contentType?->slug,
            'category' => $category?->name,
            'category_slug' => $category?->slug,
            'views' => (int) ($stats?->views ?? $art->view_count ?? 0),
            'downloads' => (int) ($stats?->downloads ?? 0),
            'likes' => (int) ($stats?->favorites ?? $art->favourite_count ?? 0),
            'published_at' => $this->formatIsoDate($art->published_at),
        ];
    }

    private function formatIsoDate(mixed $value): ?string
    {
        if ($value instanceof CarbonInterface) {
            return $value->toISOString();
        }

        if ($value instanceof \DateTimeInterface) {
            return $value->format(DATE_ATOM);
        }

        return is_string($value) ? $value : null;
    }
}
