<?php

namespace App\Services\User;

use App\Models\Administration;
use App\Models\User;
use App\Services\User\Contracts\UserServiceInterface;
use Carbon\Carbon;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;

class UserService implements UserServiceInterface
{
    public function getDashboard(User $user, string $selectedMonth): LengthAwarePaginator
    {
        $startOfMonth = Carbon::parse($selectedMonth)->startOfMonth();
        $endOfMonth   = Carbon::parse($selectedMonth)->endOfMonth();

        return Administration::with(['senderUser', 'receiverUser'])
            ->where('msnv', $user->msnv)
            ->whereBetween('date', [$startOfMonth, $endOfMonth])
            ->orderBy('date', 'desc')
            ->paginate(10)
            ->withQueryString();
    }

    public function getOtherActiveUsers(User $currentUser): Collection
    {
        return User::where('status', config('constants.STATUS_ACTIVE'))
            ->where('msnv', '!=', $currentUser->msnv)
            ->get();
    }

    public function sendThankcards(User $sender, string $receiverMsnv, int $amount, ?int $templateId = 1): void
    {
        if ($sender->flag_send == config('constants.FLAG_SEND_DISABLED')) {
            throw new \App\Exceptions\ThankCardException(__('messages.error.no_send_permission'));
        }

        $receiver = User::where('msnv', $receiverMsnv)->first();
        if (!$receiver || $receiver->status != config('constants.STATUS_ACTIVE')) {
            throw new \App\Exceptions\ThankCardException(__('messages.error.receiver_invalid'));
        }

        $startOfMonth = Carbon::now()->startOfMonth();
        $endOfMonth   = Carbon::now()->endOfMonth();

        DB::transaction(function () use ($sender, $receiverMsnv, $amount, $startOfMonth, $endOfMonth, $templateId) {
            // Lock sender record to prevent concurrent transaction modifications
            $lockedSender = User::where('id', $sender->id)->lockForUpdate()->first();

            if ($lockedSender->flag_send == config('constants.FLAG_SEND_DISABLED')) {
                throw new \App\Exceptions\ThankCardException(__('messages.error.cannot_send_card'));
            }

            if ($lockedSender->card < $amount) {
                throw new \App\Exceptions\ThankCardException(__('messages.error.cannot_send_card'));
            }

            // Lock & check total sent cards to the receiver this month
            $cardsSentToThisUserThisMonth = Administration::where('msnv', $lockedSender->msnv)
                ->where('receiver', $receiverMsnv)
                ->whereBetween('date', [$startOfMonth, $endOfMonth])
                ->lockForUpdate()
                ->sum('sent');

            if ($cardsSentToThisUserThisMonth + $amount > Administration::MAX_SEND_CARD_PER_MONTH) {
                throw new \App\Exceptions\ThankCardException(
                    __('messages.error.max_send_limit_template', [
                        'max' => Administration::MAX_SEND_CARD_PER_MONTH,
                        'sent' => $cardsSentToThisUserThisMonth
                    ])
                );
            }

            Administration::create([
                'msnv'        => $receiverMsnv,
                'received'    => $amount,
                'sender'      => $lockedSender->msnv,
                'sent'        => 0,
                'receiver'    => null,
                'date'        => Carbon::today(),
                'template_id' => $templateId,
            ]);

            Administration::create([
                'msnv'        => $lockedSender->msnv,
                'received'    => 0,
                'sender'      => null,
                'sent'        => $amount,
                'receiver'    => $receiverMsnv,
                'date'        => Carbon::today(),
                'template_id' => $templateId,
            ]);

            $lockedSender->card -= $amount;
            if ($lockedSender->card <= 0) {
                $lockedSender->card = 0;
                $lockedSender->flag_send = config('constants.FLAG_SEND_DISABLED');
            }
            $lockedSender->save();

            // Synchronize variables back to the original model instance
            $sender->card = $lockedSender->card;
            $sender->flag_send = $lockedSender->flag_send;
        });
    }

    public function updatePassword(User $user, string $newPassword): void
    {
        $user->pass        = md5($newPassword);
        $user->first_login = config('constants.FIRST_LOGIN_FALSE');
        $user->save();
    }

    public function getPersonalStats(User $user, string $selectedMonth): array
    {
        $startOfMonth = Carbon::parse($selectedMonth)->startOfMonth();
        $endOfMonth   = Carbon::parse($selectedMonth)->endOfMonth();

        $receivedCount = (int) Administration::where('msnv', $user->msnv)
            ->whereBetween('date', [$startOfMonth, $endOfMonth])
            ->sum('received');

        $sentCount = (int) Administration::where('msnv', $user->msnv)
            ->whereBetween('date', [$startOfMonth, $endOfMonth])
            ->sum('sent');

        // Calculate current rank (based on received)
        $rankings = User::where('status', config('constants.STATUS_ACTIVE'))
            ->addSelect([
                'score' => Administration::selectRaw('COALESCE(SUM(received), 0)')
                    ->whereColumn('msnv', 'user.msnv')
                    ->whereBetween('date', [$startOfMonth, $endOfMonth])
            ])
            ->get()
            ->filter(fn($u) => $u->score > 0)
            ->sortBy([
                ['score', 'desc'],
                ['msnv', 'asc']
            ])
            ->values();

        $currentRank = 0;
        $prevScore = null;
        $rank = 1;
        $denseRank = 0;

        foreach ($rankings as $item) {
            $score = (int) $item->score;
            if ($score !== $prevScore) {
                $denseRank = $rank;
                $prevScore = $score;
            }
            
            if ($item->msnv == $user->msnv) {
                $currentRank = $denseRank;
                break;
            }
            $rank++;
        }

        return [
            'receivedCount' => $receivedCount,
            'sentCount' => $sentCount,
            'currentRank' => $currentRank
        ];
    }

    public function getRankingList(string $type, string $selectedMonth, User $currentUser): array
    {
        $startOfMonth = Carbon::parse($selectedMonth)->startOfMonth();
        $endOfMonth   = Carbon::parse($selectedMonth)->endOfMonth();

        $column = $type === 'sent' ? 'sent' : 'received';

        $allActiveUsers = User::where('status', config('constants.STATUS_ACTIVE'))
            ->addSelect([
                'score' => Administration::selectRaw('COALESCE(SUM(' . $column . '), 0)')
                    ->whereColumn('msnv', 'user.msnv')
                    ->whereBetween('date', [$startOfMonth, $endOfMonth])
            ])
            ->get();

        $rankedUsers = $allActiveUsers->filter(fn($u) => $u->score > 0)
            ->sortBy([
                ['score', 'desc'],
                ['msnv', 'asc']
            ])
            ->values();

        $prevScore = null;
        $rank = 1;
        $denseRank = 0;

        $rankers = [];
        $currentUserRankItem = null;

        foreach ($rankedUsers as $index => $userItem) {
            $score = (int) $userItem->score;
            if ($score !== $prevScore) {
                $denseRank = $rank;
                $prevScore = $score;
            }
            
            $userItem->rank = $denseRank;
            $userItem->score = $score;

            if ($userItem->msnv == $currentUser->msnv) {
                $currentUserRankItem = $userItem;
            }

            // Include every ranked user (no hard limit)
            $rankers[] = $userItem;
            $rank++;

        }

        if (!$currentUserRankItem) {
            $currentUserModel = $allActiveUsers->first(fn($u) => $u->msnv == $currentUser->msnv);
            if ($currentUserModel) {
                $currentUserRankItem = clone $currentUserModel;
                $currentUserRankItem->score = 0;
                $currentUserRankItem->rank = 0;
            }
        }

        return [
            'rankers' => $rankers,
            'currentUserRankItem' => $currentUserRankItem
        ];
    }

    // Retrieve paginated list of received ThankCards for a user
    public function getReceivedPageData(User $user, string $selectedMonth, int $limit = 12, int $offset = 0): array
    {
        $start = Carbon::parse($selectedMonth)->startOfMonth();
        $end   = Carbon::parse($selectedMonth)->endOfMonth();

        $statsQuery = Administration::where('msnv', $user->msnv)
            ->whereBetween('date', [$start, $end])
            ->where('received', '>', 0);

        $totalCards = (int) $statsQuery->sum('received');
        $totalSenders = (int) $statsQuery->whereNotNull('sender')->distinct('sender')->count('sender');

        $cards = Administration::with('senderUser')
            ->where('msnv', $user->msnv)
            ->whereBetween('date', [$start, $end])
            ->where('received', '>', 0)
            ->orderBy('date', 'desc')
            ->skip($offset)
            ->take($limit)
            ->get();

        $hasMore = Administration::where('msnv', $user->msnv)
            ->whereBetween('date', [$start, $end])
            ->where('received', '>', 0)
            ->skip($offset + $limit)
            ->take(1)
            ->exists();

        return [
            'cards' => $cards,
            'hasMore' => $hasMore,
            'totalCards' => $totalCards,
            'totalSenders' => $totalSenders
        ];
    }

    // Retrieve paginated list of sent ThankCards for a user
    public function getSentPageData(User $user, string $selectedMonth, int $limit = 12, int $offset = 0): array
    {
        $start = Carbon::parse($selectedMonth)->startOfMonth();
        $end   = Carbon::parse($selectedMonth)->endOfMonth();

        $statsQuery = Administration::where('msnv', $user->msnv)
            ->whereBetween('date', [$start, $end])
            ->where('sent', '>', 0);

        $totalCards = (int) $statsQuery->sum('sent');
        $totalReceivers = (int) $statsQuery->whereNotNull('receiver')->distinct('receiver')->count('receiver');

        $cards = Administration::with('receiverUser')
            ->where('msnv', $user->msnv)
            ->whereBetween('date', [$start, $end])
            ->where('sent', '>', 0)
            ->orderBy('date', 'desc')
            ->skip($offset)
            ->take($limit)
            ->get();

        $hasMore = Administration::where('msnv', $user->msnv)
            ->whereBetween('date', [$start, $end])
            ->where('sent', '>', 0)
            ->skip($offset + $limit)
            ->take(1)
            ->exists();

        return [
            'cards' => $cards,
            'hasMore' => $hasMore,
            'totalCards' => $totalCards,
            'totalReceivers' => $totalReceivers
        ];
    }

    // Retrieve limited feed of received ThankCards for activity feed
    public function getReceivedFeed(User $user, string $selectedMonth, int $limit = 12, int $offset = 0): array
    {
        return $this->getReceivedPageData($user, $selectedMonth, $limit, $offset);
    }

    // Retrieve limited feed of sent ThankCards for activity feed
    public function getSentFeed(User $user, string $selectedMonth, int $limit = 12, int $offset = 0): array
    {
        return $this->getSentPageData($user, $selectedMonth, $limit, $offset);
    }

    public function updateProfile(User $user, array $data, ?\Illuminate\Http\UploadedFile $avatarFile = null): void
    {
        if (isset($data['name'])) {
            $user->name = $data['name'];
        }

        if ($avatarFile) {
            $path = $avatarFile->store('avatars', 'public');
            $user->avatar = 'storage/' . $path;
        }

        if (!empty($data['new_password'])) {
            $user->pass        = md5($data['new_password']);
            $user->first_login = config('constants.FIRST_LOGIN_FALSE');
        }

        $user->save();
    }

    public function getSentCardsToUserThisMonth(User $sender, string $receiverMsnv): int
    {
        $startOfMonth = Carbon::now()->startOfMonth();
        $endOfMonth   = Carbon::now()->endOfMonth();

        return (int) Administration::where('msnv', $sender->msnv)
            ->where('receiver', $receiverMsnv)
            ->whereBetween('date', [$startOfMonth, $endOfMonth])
            ->sum('sent');
    }
}