<?php

namespace App\Controllers;

use App\Libraries\HashLibrary;
use App\Libraries\SendEmail;
use App\Libraries\UserAuth;
use App\Models\TokenModel;
use App\Models\UserModel;
use App\Validation\UserValidation;
use Carbon\Carbon;
use CodeIgniter\Config\Services;
use CodeIgniter\HTTP\ResponseInterface;
use CodeIgniter\RESTful\ResourceController;
use ReflectionException;

class UserController extends ResourceController
{
    protected $helpers = ['form', 'custom'];
    protected $model;
    protected TokenModel $tokenModel;
    protected UserValidation $validation;

    public function __construct()
    {
        $this->model = new UserModel();
        $this->validation = new UserValidation();
        $this->tokenModel = new TokenModel();
    }

    public function index()
    {
        $data = [
            'users' => $this->model->getUsers(),
        ];
        return view("backend/users", $data);
    }

    public function loginForm()
    {
        return view("backend/login");
    }

    public function login()
    {
        $validator = Services::validation($this->validation);
        $validator->setRuleGroup('validateUserOnLogin');

        if ($validator->withRequest($this->request)->run()) {
            $data = $validator->getValidated();
            $user = $this->model->asObject()->where('email', $data['email'])->first();

            if ($user) {
                if ($user->status == 'Active') {
                    if (HashLibrary::verify($data['password'], $user->password)) {
                        UserAuth::setAuth($user);
                        return redirect('admin_home');
                    }
                    return redirect()->back()->withInput()->with('fail', 'Invalid credentials');
                }
                return redirect()->back()->withInput()->with('fail', 'Your account is not active.');
            }
        }
        $data = ['validation' => $validator->getErrors()];
        return view("backend/login", $data);
    }
    public function forgotPasswordForm()
    {
        return view("backend/forgot_password");
    }
    public function sendPasswordResetLink()
    {
        $validator = Services::validation($this->validation);
        $validator->setRuleGroup('validateUserOnForgotPassword');

        if ($validator->withRequest($this->request)->run()) {
            $data = $validator->getValidated();
            $user = $this->model->asObject()->where('email', $data['email'])->first();
            log_message('info', json_encode($user));
            $token = HashLibrary::generateToken();

            $old_token = $this->tokenModel->asObject()
                ->where('email', $user->email)
                ->first();

            if ($old_token) {
                //update existing token
                try {
                    $this->tokenModel->where('email', $user->email)
                        ->set(['token' => $token, 'created_at' => Carbon::now()])
                        ->update();
                } catch (ReflectionException $e) {
                    exit($e->getMessage());
                }
            } else {
                try {
                    $this->tokenModel->insert([
                        'email' => $user->email,
                        'token' => $token,
                        'created_at' => Carbon::now(),
                    ]);
                } catch (ReflectionException $e) {
                    exit($e->getMessage());
                }
            }
            //create action link for sending in email
            $mail_data = [
                'link' => url_to('reset_password', $token),
                'name' => join(' ', [$user->first_name, $user->last_name]),
                'email' => $user->email,
            ];
            $view = Services::renderer();
            $mail_body = $view->setVar('mail_data', $mail_data)
                ->render('templates/forgot-password-email-template');
            if (SendEmail::send($mail_data['email'], $mail_body, 'Reset Password')) {
                return redirect('login')
                    ->with('success', 'We have e-mailed you a link to reset password');
            }
            return redirect('login')
                ->with('fail', 'Something went long.');
        }
        $data = ['validation' => $validator->getErrors()];
        return view("backend/forgot_password", $data);
    }
    public function resetPassword($token)
    {
        $check_token = $this->tokenModel->asObject()
            ->where('token', $token)
            ->first();
        if (!$check_token) {
            return redirect('login')
                ->with('fail', 'Invalid token, request another one.');
        }
        //check token expiry

        if (is_token_expired($check_token->created_at)) {
            //token has expired
            return redirect('login')
                ->with('fail', 'Token expired, request another one.');
        }
        $data = [
            'token' => $token,
        ];
        return view('frontend/change_password', $data);
    }
    public function changeMyPasswordForm()
    {
        return view('backend/change_my_password');
    }
    public function changeMyPassword()
    {

        $validator = Services::validation($this->validation);
        $validator->setRuleGroup('validateUserOnChangePassword');

        if ($validator->withRequest($this->request)->run()) {

            $user = $this->model->getUser(get_user()->id);

            if ($user) {
                $valid_data = $validator->getValidated();
                if (HashLibrary::verify($valid_data['current_password'], $user->password)) {
                    $data = [
                        'password' => HashLibrary::hash($valid_data['new_password']),
                        'id' => (int) $user->id,
                    ];
                    $save = $this->model->save($data);
                    if ($save) {
                        UserAuth::forget();
                        return redirect('login')
                            ->with('success', 'Password changed successfully.');
                    }
                }
                return redirect()->back()
                    ->withInput()
                    ->with('fail', 'Invalid current password.');
            }
            return redirect()->back()
                ->with('fail', 'Something went long.');
        }
        $data = [
            'validation' => $validator->getErrors()
        ];

        return view('backend/change_my_password', $data);
    }
    public function changePassword($token)
    {
        $validator = Services::validation($this->validation);
        $validator->setRuleGroup('validateUserOnSetPassword');

        if ($validator->withRequest($this->request)->run()) {
            $get_token = $this->tokenModel->asObject()->where('token', $token)->first();

            if ($get_token) {
                $valid_data = $validator->getValidated();
                $user = $this->model->asObject()->where('email', $get_token->email)->first();

                if ($user) {
                    try {
                        $data = [
                            'password' => HashLibrary::hash($valid_data['password']),
                            'updated_at' => Carbon::now(),
                            'id' => $user->id,
                        ];
                        $this->model->save($data);

                        //notify user about password reset
                        $mail_data = [
                            'name' => join(' ', [$user->first_name, $user->last_name]),
                            'password' => $valid_data['password'],
                            'email' => $user->email,
                        ];

                        $view = Services::renderer();

                        $mail_body = $view->setVar('mail_data', $mail_data)
                            ->render('templates/password-changed-email-template');

                        if (SendEmail::send($mail_data['email'], $mail_body, 'Password Changed')) {
                            //delete token
                            $this->tokenModel->where('email', $user->email)->delete();

                            return redirect('login')
                                ->with('success', 'Password changed successfully.');
                        }
                        return redirect('login')
                            ->with('fail', 'Something went long.');
                    } catch (ReflectionException $e) {
                        exit($e->getMessage());
                    }
                }
            }
            return redirect()->back()->with('fail', 'Invalid token');
        }
        $data = [
            'token' => $token,
            'validation' => $validator->getErrors()
        ];

        return view('frontend/change_password', $data);
    }
    public function profile()
    {
        return view("backend/profile");
    }


    public function new()
    {
        return view('backend/create_user');
    }
    public function create()
    {
        $validator = Services::validation($this->validation);
        $validator->setRuleGroup('validateUserOnCreation');

        if ($validator->withRequest($this->request)->run()) {
            $data = $validator->getValidated();
            $user = [
                'first_name' => $data['first_name'],
                'last_name' => $data['last_name'],
                'email' => $data['email'],
                'role' => $data['role'] ?? 'Non-Admin',
            ];
            $save = $this->model->save($user);
            if ($save) {
                return redirect()->back()
                    ->with('success', 'User was created successfully. A verification email was sent to ' . $user['email']);
            }
            return redirect()->back()
                ->withInput()
                ->with('fail', 'Something went wrong');
        }
        $data = ['validation' => $validator->getErrors()];
        return view("backend/create_user", $data);
    }
    public function setPasswordForm($token)
    {
        return view('backend/set_password', ['token' => $token]);
    }
    public function setPassword($token)
    {
        $validator = Services::validation($this->validation);
        $validator->setRuleGroup('validateUserOnSetPassword');

        if ($validator->withRequest($this->request)->run()) {
            $data = $validator->getValidated();
            $get_token = $this->tokenModel->asObject()
                ->where('token', $token)
                ->first();
            if (is_token_expired($get_token->created_at)) {
                //token has expired
                return redirect('login')
                    ->with('fail', 'Token is expired.');
            }
            $user = $this->model->asObject()->where('email', $get_token->email)->first();
            if ($user) {
                try {
                    $update = $this->model->save([
                        'id' => $user->id,
                        'password' => HashLibrary::hash($data['password']),
                        'status' => 'Active'
                    ]);

                    if ($update) {
                        $this->tokenModel->where('email', $user->email)->delete();
                        return redirect('login')
                            ->with('success', 'Password was created successfully.');
                    }
                    return redirect()->back()
                        ->withInput()
                        ->with('fail', 'Something went long.');
                } catch (ReflectionException $e) {
                    exit($e->getMessage());
                }
            }
        }
        $data = ['validation' => $validator->getErrors(), 'token' => $token];
        return view("backend/set_password", $data);
    }
    public function show($id = null)
    {
        $user = $this->model->getUser($id);
        return view("backend/user_profile", ['user' => $user]);
    }
    public function delete($id = null)
    {
        $delete = $this->model->delete($id);
        if ($delete) {
            return redirect('users')
                ->with('success', 'User profile deleted successfully.');
        }
        return redirect()->back()
            ->with('fail', 'Failed to delete user profile.');
    }
    public function edit($id = null)
    {
        $user = $this->model->getUser($id);

        return view('backend/edit_user', ['user' => $user]);
    }
    public function update($id = null)
    {
        $validator = Services::validation($this->validation);
        $validator->setRuleGroup('validateUserOnUpdate');

        if ($validator->withRequest($this->request)->run()) {
            $data = $validator->getValidated();
            $user = [
                'first_name' => $data['first_name'],
                'last_name' => $data['last_name'],
                'email' => $data['email'],
                'role' => $data['role'] ?? 'Non-Admin',
                'id' => $data['id']
            ];
            $save = $this->model->save($user);
            if ($save) {
                return redirect()->back()
                    ->with('success', 'User was updated successfully');
            }
            return redirect()->back()
                ->withInput()
                ->with('fail', 'Something went wrong');
        }
        $data = ['validation' => $validator->getErrors(), 'user' => $this->model->getUser($id)];
        return view("backend/edit_user", $data);
    }
    public function activateProfile($id = null)
    {
        $user = $this->model->getUser($id);
        if ($user) {
            if ($user->status == 'Inactive') {
                $change_status = $this->model->save(['id' => (int)$user->id, 'status' => 'Active']);
                if ($change_status) {
                    return redirect()->back()
                        ->with('success', 'User was activated successfully');
                }
                return redirect()->back()
                    ->with('fail', 'Failed to activate user');
            }
        }
        return redirect()->back()
            ->with('fail', 'Something went wrong');
    }
    public function deactivateProfile($id = null)
    {
        $user = $this->model->getUser($id);
        if ($user) {
            if ($user->status == 'Active') {
                $change_status = $this->model->save(['id' => (int)$user->id, 'status' => 'Inactive']);
                if ($change_status) {
                    return redirect()->back()
                        ->with('success', 'User was deactivated successfully');
                }
                return redirect()->back()
                    ->with('fail', 'Failed to deactivate user');
            }
        }
        return redirect()->back()
            ->with('fail', 'Something went wrong');
    }
}
