<?php

namespace App\Services\Admin;

use App\Models\AddCard;
use App\Models\Administration;
use App\Models\User;
use App\Services\Admin\Contracts\AdminServiceInterface;
use Carbon\Carbon;
use Illuminate\Pagination\LengthAwarePaginator;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Facades\Log;

use App\DTOs\UserFilterDto;

class AdminService implements AdminServiceInterface
{
    public function __construct(
        private readonly \App\Services\User\Contracts\UserServiceInterface $userService
    ) {}
    public function getUserListWithStats(
        string|UserFilterDto $monthOrFilters,
        ?string $search = null,
        ?string $status = '1',
        ?string $role = null,
        ?string $department = null,
        ?string $flagSend = null
    ): array {
        if ($monthOrFilters instanceof UserFilterDto) {
            $filters = $monthOrFilters;
        } else {
            $filters = new UserFilterDto(
                selectedMonth: $monthOrFilters,
                search: $search,
                status: $status,
                role: $role,
                department: $department,
                flagSend: $flagSend
            );
        }
        $selectedMonth = $filters->selectedMonth;
        $startOfMonth  = Carbon::parse($selectedMonth)->startOfMonth();
        $endOfMonth    = Carbon::parse($selectedMonth)->endOfMonth();

        $usersQuery = User::select('user.*')
            ->addSelect([
                'total_received' => Administration::selectRaw('COALESCE(SUM(received), 0)')
                    ->whereColumn('msnv', 'user.msnv')
                    ->whereBetween('date', [$startOfMonth, $endOfMonth]),
                'total_sent' => Administration::selectRaw('COALESCE(SUM(sent), 0)')
                    ->whereColumn('msnv', 'user.msnv')
                    ->whereBetween('date', [$startOfMonth, $endOfMonth])
            ]);

        $activeUsers = (clone $usersQuery)->where('status', config('constants.STATUS_ACTIVE'))->get();

        $maxReceived = $activeUsers->max('total_received') ?? 0;
        $maxSent     = $activeUsers->max('total_sent') ?? 0;

        $topReceivedUsers = $maxReceived > 0 
            ? $activeUsers->filter(fn($u) => $u->total_received == $maxReceived)->values() 
            : collect();

        $topSentUsers = $maxSent > 0 
            ? $activeUsers->filter(fn($u) => $u->total_sent == $maxSent)->values() 
            : collect();

        $topReceivedUser = $topReceivedUsers->first();
        $topSentUser     = $topSentUsers->first();

        $status = $filters->status;
        if (!is_null($status) && $status !== '') {
            $usersQuery->where('status', intval($status));
        }

        $role = $filters->role;
        if (!is_null($role) && $role !== '') {
            $usersQuery->where('role', intval($role));
        }

        $department = $filters->department;
        if (!is_null($department) && $department !== '') {
            $usersQuery->where('departments', $department);
        }

        $flagSend = $filters->flagSend;
        if (!is_null($flagSend) && $flagSend !== '') {
            $usersQuery->where('flag_send', intval($flagSend));
        }

        $search = $filters->search;
        if (!is_null($search) && trim($search) !== '') {
            $search = trim($search);
            $usersQuery->where(function ($q) use ($search) {
                $q->whereRaw('LOWER(msnv) LIKE ?', ['%' . strtolower($search) . '%'])
                  ->orWhereRaw('LOWER(name) LIKE ?', ['%' . strtolower($search) . '%'])
                  ->orWhereRaw('LOWER(mail) LIKE ?', ['%' . strtolower($search) . '%']);
            });
        }
        $users = $usersQuery->paginate(20)->withQueryString();

        return compact('users', 'topReceivedUser', 'topSentUser', 'topReceivedUsers', 'topSentUsers');
    }

    public function getUserTransactions(string $msnv, string $selectedMonth): LengthAwarePaginator
    {
        $startOfMonth = Carbon::parse($selectedMonth)->startOfMonth();
        $endOfMonth   = Carbon::parse($selectedMonth)->endOfMonth();

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

    public function createUser(array $data): void
    {
        User::create([
            'msnv'        => $data['msnv'],
            'name'        => $data['name'],
            'mail'        => $data['mail'],
            'pass'        => md5($data['password']),
            'departments' => $data['departments'],
            'role'        => $data['role'],
            'status'      => config('constants.STATUS_ACTIVE'),
            'card'        => 0,
            'flag_send'   => config('constants.FLAG_SEND_DISABLED'),
            'first_login' => $data['role'] == config('constants.ROLE_ADMIN') ? config('constants.FIRST_LOGIN_FALSE') : config('constants.FIRST_LOGIN_TRUE'),
        ]);
    }

    public function updateUser(string|User $userOrMsnv, array $data): void
    {
        $user = $userOrMsnv instanceof User 
            ? $userOrMsnv 
            : User::where('msnv', $userOrMsnv)->firstOrFail();

        DB::transaction(function () use ($data, $user) {
            // Update profile fields (name & avatar) via UserService
            $this->userService->updateProfile($user, [
                'name' => $data['name'] ?? null,
            ], request()->file('avatar'));

            if (isset($data['mail'])) {
                $user->mail = $data['mail'];
            }

            if (isset($data['departments'])) {
                $user->departments = $data['departments'];
            }

            $newCard = $user->card;
            if (isset($data['num_card'])) {
                $newCard = $user->card + intval($data['num_card']);
            } elseif (isset($data['card'])) {
                $newCard = intval($data['card']);
            }

            $oldCard = intval($user->card);

            if ($newCard > $oldCard) {
                $diff = $newCard - $oldCard;
                $todayRecord = AddCard::where('buyer', $user->msnv)
                    ->where('seller', Auth::user()->msnv)
                    ->whereDate('date', Carbon::today())
                    ->first();
                if ($todayRecord) {
                    $todayRecord->num_card += $diff;
                    $todayRecord->save();
                } else {
                    AddCard::create([
                        'buyer'    => $user->msnv,
                        'num_card' => $diff,
                        'seller'   => Auth::user()->msnv,
                        'date'     => Carbon::today(),
                    ]);
                }
            }

            $user->card        = $newCard;
            $user->role        = isset($data['role']) ? intval($data['role']) : $user->role;
            $user->status      = isset($data['status']) ? intval($data['status']) : $user->status;
            $user->flag_send   = isset($data['flag_send']) ? intval($data['flag_send']) : $user->flag_send;
            $user->first_login = isset($data['first_login']) ? intval($data['first_login']) : $user->first_login;
            $user->save();
        });
    }

    public function deactivateUser(string $msnv): void
    {
        $user = User::where('msnv', $msnv)->firstOrFail();
        $user->status = config('constants.STATUS_INACTIVE');
        $user->save();
    }

    public function resetAllCards(): void
    {
        $activeUserCount = User::where('status', config('constants.STATUS_ACTIVE'))->count();

        User::where('status', config('constants.STATUS_ACTIVE'))->update([
            'card' => 0,
            'flag_send' => config('constants.FLAG_SEND_DISABLED'),
        ]);

        Log::info("Monthly card reset executed successfully for {$activeUserCount} active accounts.");
    }

    public function updateAddCard(int $id, array $data): void
    {
        $addCard = AddCard::findOrFail($id);

        $currentMonth = Carbon::now()->format('Y-m');
        $recordMonth = Carbon::parse($addCard->date)->format('Y-m');

        if ($recordMonth !== $currentMonth) {
            throw new \Exception('Không cho phép chỉnh sửa dữ liệu card của các tháng trước.');
        }

        $newDateMonth = Carbon::parse($data['date'])->format('Y-m');
        if ($newDateMonth !== $currentMonth) {
            throw new \Exception('Chỉ được phép chỉnh sửa dữ liệu của tháng hiện tại.');
        }

        $user = User::where('msnv', $addCard->buyer)->firstOrFail();

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

        DB::transaction(function () use ($addCard, $user, $data, $totalSentThisMonth) {
            $oldNumCard = intval($addCard->num_card);
            $newNumCard = intval($data['num_card']);
            $diff = $newNumCard - $oldNumCard;
            $newCardBalance = $user->card + $diff;
            if ($newCardBalance < 0) {
                throw new \Exception('Số lượng card cập nhật không hợp lệ vì tổng số card của user không được nhỏ hơn 0.');
            }

            if ($newCardBalance < $totalSentThisMonth) {
                throw new \Exception('Số lượng card cập nhật không hợp lệ vì tổng số card của user không được nhỏ hơn số card đã gửi trong tháng (' . $totalSentThisMonth . ' card).');
            }

            $user->card += $diff;
            $user->save();

            $targetRecord = AddCard::where('buyer', $addCard->buyer)
                ->where('seller', $data['seller'])
                ->whereDate('date', $data['date'])
                ->where('id', '!=', $addCard->id)
                ->first();

            if ($targetRecord) {
                $targetRecord->num_card += $newNumCard;
                $targetRecord->save();
                $addCard->delete();
            } else {
                $addCard->num_card = $newNumCard;
                $addCard->seller   = $data['seller'];
                $addCard->date     = $data['date'];
                $addCard->save();
            }
        });
    }

    public function destroyAddCard(int $id): void
    {
        $addCard = AddCard::findOrFail($id);

        $currentMonth = Carbon::now()->format('Y-m');
        $recordMonth = Carbon::parse($addCard->date)->format('Y-m');

        if ($recordMonth !== $currentMonth) {
            throw new \Exception('Không cho phép xóa dữ liệu card của các tháng trước.');
        }

        $user = User::where('msnv', $addCard->buyer)->firstOrFail();

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

        DB::transaction(function () use ($addCard, $user, $totalSentThisMonth) {
            $newCardBalance = $user->card - intval($addCard->num_card);

            if ($newCardBalance < 0) {
                throw new \Exception('Không thể xóa lịch sử cấp phát thẻ này vì tổng số card của user không được nhỏ hơn 0.');
            }

            if ($newCardBalance < $totalSentThisMonth) {
                throw new \Exception('Không thể xóa lịch sử cấp phát thẻ này vì tổng số card của user không được nhỏ hơn số card đã gửi trong tháng (' . $totalSentThisMonth . ' card).');
            }

            $user->card = $newCardBalance;
            $user->save();

            $addCard->delete();
        });
    }

    public function getUserByMsnv(string $msnv): User
    {
        return User::where('msnv', $msnv)->firstOrFail();
    }

    public function getAddCardsHistory(string $buyerMsnv, ?string $selectedMonth = null): \Illuminate\Database\Eloquent\Collection
    {
        $query = AddCard::with('sellerUser')
            ->where('buyer', $buyerMsnv);

        if ($selectedMonth) {
            $startOfMonth = Carbon::parse($selectedMonth)->startOfMonth()->format('Y-m-d');
            $endOfMonth   = Carbon::parse($selectedMonth)->endOfMonth()->format('Y-m-d');
            $query->whereBetween('date', [$startOfMonth, $endOfMonth]);
        }

        return $query->orderBy('date', 'desc')
            ->orderBy('id', 'desc')
            ->get();
    }

    public function getActiveAdmins(): \Illuminate\Database\Eloquent\Collection
    {
        return User::where('role', config('constants.ROLE_ADMIN'))
            ->where('status', config('constants.STATUS_ACTIVE'))
            ->get();
    }
}