<?php

namespace App\Services;

use App\Exceptions\ApiHttpException;
use App\Facades\AccountsService;
use App\Facades\WatchlistsService;
use App\Models\User;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
use App\Mailables\RegisterUser;
use App\Models\Result;
use Exception;
use Illuminate\Auth\Events\Verified;
use Illuminate\Support\Facades\Mail;
use Illuminate\Auth\Events\Registered;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Storage;
use Intervention\Image\Drivers\Imagick\Driver;
use Intervention\Image\ImageManager;

class UserService
{
    /**
     * Get the user list for the backoffice grid
     *
     * @param $page
     * @param $rowCount
     * @param $sort
     * @param $search
     *
     * @return array
     */
    public function getUserGridList($page, $rowCount, $sort, $search)
    {
        Paginator::currentPageResolver( function () use ($page) {
            return $page;
        });

        if (!empty($search)) {
            $query = User::where(function ($query) use ($search) {
                $query->orWhere('first_name', 'like', "%{$search}%")
                    ->orWhere('last_name', 'like', "%{$search}%")
                    ->orWhere('email', 'like', "%{$search}%");
            });
        } else {
            $query = User::query();
        }

        if (empty($sort)) {
            $rows = $query->paginate($rowCount);
        } else {
            foreach ($sort as $field => $direction) {
                if(empty($direction) || !Str::is(strtolower($direction),"desc")){
                    $orderDirection = "asc";
                } else {
                    $orderDirection = $direction;
                }
                $rows = $query->orderBy($field, strtolower($orderDirection))->paginate($rowCount);
                break;
            }
        }

        return [
            "total"             => $rows->total(),
            "current"           => $rows->currentPage(),
            "rowCount"          => $rowCount,
            "rows"              => $rows->getCollection()
        ];
    }

    public function generateUserCode($userId)
    {
        $user = User::findOrFail($userId);

        if(empty($user)){
            throw new ModelNotFoundException('User not found');
        }

        if ($user->validation_code_expires_at && $user->validation_code_expires_at->isFuture()) {
            return true;
        }

        $user->verification_code = str_pad(random_int(1, 999999), 6, "0", STR_PAD_LEFT);
        $user->validation_code_expires_at = now()->addMinutes(3);
        $user->save();

        event(new Registered($user));

        return false;
    }

    public function checkUserCode($data): ?User
    {
        $user = User::where('email', $data['email'])->first();

        if(empty($user)){
            throw new ModelNotFoundException('User not found');
        }

        if ($user->verification_code === $data['code'] && $user->validation_code_expires_at->isFuture()) {
            $user->status = User::STATUS_ACTIVE;
            $user->verification_code = null;
            $user->validation_code_expires_at = null;
            $user->email_verified_at = now();
            $user->registration_step = User::REGISTRATION_STEP_NAME_SETUP;

            $user->save();
            return $user;
        }
        return null;
    }

    public function createUser($data)
    {
        DB::beginTransaction();
        try {
            $user = new User();

            if (!empty($data['firstName'])) {
                $user->first_name = $data['firstName'];
            }

            if (!empty($data['lastName'])) {
                $user->last_name = $data['lastName'];
            }

            $user->email = $data['email'];
            $user->password = Hash::make($data['password']);
            $user->registration_step = User::REGISTRATION_STEP_EMAIL_VERIFICATION;
            $user->registration_plan_option_id = $data['planOptionId'] ?? null;

            if (!empty($data['newsletter'])) {
                $user->newsletter = $data['newsletter'];
            }
            $user->status = User::STATUS_PENDING_EMAIL_CONFIRMATION;
            $user->save();

            WatchlistsService::createDefaultWatchlists($user);

            if (!empty($data['invitationToken'])) {
                $member = AccountsService::acceptInvitation($user, $data['invitationToken']);
                $user->status = User::STATUS_ACTIVE;
                $user->save();
            } else {
                $this->generateUserCode($user->id);
            }

            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }

        return $user;
    }

    public function updateUserPasswordForRegistrationRetry(User $user, $data)
    {
        $user->password = Hash::make($data['password']);
        $user->is_legacy_password = false;
        $user->save();

        $this->generateUserCode($user->id);

        return $user;
    }

    public function updateUser(User $user, $data)
    {
        DB::beginTransaction();
        try {
            $user->first_name = $data['firstName'];
            $user->last_name = $data['lastName'];

            if ($user->registration_step === User::REGISTRATION_STEP_NAME_SETUP) {
                $user->registration_step = !empty($user->registration_plan_option_id)
                    ? User::REGISTRATION_STEP_ACCOUNT_SETUP
                    : User::REGISTRATION_STEP_PROFILE_SETUP;
            }

            $user->save();
            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
        return $user;
    }

    public function updateUserFullProfile(User $user, $data)
    {
        if ($user->email !== $data['email']) {
            $emailExists = User::where('email', $data['email'])->exists();
            if ($emailExists) {
                throw new ApiHttpException(409, trans('frontend.exception_email_already_exists'));
            }
        }

        DB::beginTransaction();
        try {
            $user->first_name = $data['firstName'];
            $user->last_name = $data['lastName'];
            $user->email = $data['email'];
            $user->job_profile = $data['jobProfile'];
            $user->profileRole()->associate($data['userProfileRoleId']);
            $user->profileSpecialisation()->associate($data['userProfileSpecialisationId']);

            $user->save();
            DB::commit();

            return $user;
        } catch (Exception $e) {
            DB::rollBack();
        }

    }

    public function list($data): Array
    {
        $query = User::select('users.*');

        $orderDir = $data['orderDir'] ? $data['orderDir'] : 'asc';

        switch ($data['orderBy']) {
            default:
                # name
                $query->orderBy('users.first_name', $orderDir)
                    ->orderBy('users.last_name', $orderDir);
                break;
        }

        $perPage = $data['perPage'] ?? 12;
        $page = $data['page'] ?? 1;

        $users = $query->paginate($perPage, ['*'], 'page', $page);

        $paginationData = [
            'currentPage' =>  intval($users->currentPage()),
            'lastPage' => intval($users->lastPage()),
            'perPage' =>  intval($users->perPage()),
            'total' =>  intval($users->total()),
            'count' =>  intval($users->count()),
            'hasMorePages' => $users->hasMorePages(),
            'nextPageUrl' => $users->nextPageUrl(),
            'prevPageUrl' => $users->previousPageUrl(),
        ];

        return [
            'users' => $users,
            'pagination' => $paginationData,
        ];
    }

    public function update(User $user, Array $data): User
    {
        $user->first_name = $data['firstName'];
        $user->last_name = $data['lastName'];

        $user->save();

        return $user;
    }

    public function updateUserProfile(User $user, Array $data): User
    {
        $user->job_profile = $data['jobProfile'];
        $user->profileRole()->associate($data['userProfileRoleId']);
        $user->profileSpecialisation()->associate($data['userProfileSpecialisationId']);

        if (!empty($data['registrationStep']) && $data['registrationStep'] === User::REGISTRATION_STEP_COMPLETED) {
            $user->registration_step = User::REGISTRATION_STEP_COMPLETED;
        }

        $user->save();
        return $user;
    }

    public function getUser($userId)
    {
        $user = User::find($userId);
        return $user;
    }

    public function changePassword ($data, $userData)
    {
        if (!Hash::check($data['oldPassword'], $userData->password)) {
            return false;
        }

        $userData->password = Hash::make($data['password']);
        $userData->is_legacy_password = false;
        $userData->save();

        return true;
    }

    public function verifyEmail($userId)
    {
        $user = User::find($userId);
        if (! $user->hasVerifiedEmail()) {
            $user->markEmailAsVerified();

            if ($user->status == User::STATUS_PENDING_EMAIL_CONFIRMATION) {
                $user->status = User::STATUS_ACTIVE;
                $user->save();

                Mail::to(config('mail.mail_to_admin'))->send(new RegisterUser());
            }

            event(new Verified($user));
        }
    }

    public function uploadProfileImage(User $user, ?UploadedFile $image): bool
    {
        DB::beginTransaction();
        try {
            $profileImagePath = config('path.internal.profile_images');

            if (!Storage::exists($profileImagePath)) {
                Storage::makeDirectory($profileImagePath);
            }

            if ($user->profile_image) {
                Storage::delete("{$profileImagePath}/{$user->profile_image}");
                $user->profile_image = null;
            }

            if ($image) {
                $imageName = Str::random() . '.' . $image->extension();
                $manager = new ImageManager(Driver::class);
                $img = $manager->read($image);

                $img->save(Storage::path("{$profileImagePath}/{$imageName}"), 90);

                $user->profile_image = $imageName;
            }

            $user->save();
            DB::commit();
            return true;
        } catch (Exception $e) {
            DB::rollBack();
            throw $e;
        }
    }

    public function getUserJuryMembers(User $user)
    {
        $resultJuryMembers = Result::whereHas('juryGroups.juryGroupMembers', function ($query) use ($user) {
            $query->where('user_id', $user->id);
        })
        ->with(['juryGroups' => function ($query) use ($user) {
            $query->whereHas('juryGroupMembers', function ($q) use ($user) {
                $q->where('user_id', $user->id);
            });
        }])
        ->get();

        return $resultJuryMembers;
    }

    public function getUserResultWinners(User $user)
    {
        $resultWinners = Result::whereHas('winners.entities.authors', function ($query) use ($user) {
            $query->where('user_id', $user->id);
        })->orWhereHas('winners.entities.contributors', function ($query) use ($user) {
            $query->where('user_id', $user->id);
        })
        ->with(['winners' => function ($query) use ($user) {
            $query->whereHas('entities.authors', function ($q) use ($user) {
                $q->where('user_id', $user->id);
            })->orWhereHas('entities.contributors', function ($q) use ($user) {
                $q->where('user_id', $user->id);
            });
        }])
        ->get();

        return $resultWinners;
    }
}
