<?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::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): void
    {
        if ($sender->flag_send == config('constants.FLAG_SEND_DISABLED')) {
            throw new \RuntimeException(__('messages.error.no_send_permission'));
        }

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

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

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

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

        if ($sender->card < $amount) {
            throw new \RuntimeException(__('messages.error.not_enough_cards'));
        }

        DB::transaction(function () use ($sender, $receiverMsnv, $amount) {
            Administration::create([
                'msnv'     => $receiverMsnv,
                'received' => $amount,
                'sender'   => $sender->msnv,
                'sent'     => 0,
                'receiver' => null,
                'date'     => Carbon::today(),
            ]);

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

            $sender->card -= $amount;
            $sender->save();
        });
    }

    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;
            }

            if ($index < 4) {
                $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
        ];
    }
}