<?php

namespace App\Http\Controllers\Api;

use App\Models\Form;
use App\Models\User;
use App\Models\QrCode;
use GuzzleHttp\Client;
use App\Models\Frontend;
use App\Models\Merchant;
use App\Constants\Status;
use App\Lib\FormProcessor;
use App\Models\DeviceToken;
use App\Models\Transaction;
use Illuminate\Http\Request;
use App\Models\NotificationLog;
use App\Rules\FileTypeValidate;
use App\Lib\GoogleAuthenticator;
use App\Models\SetupUtilityBill;
use App\Models\AdminNotification;
use App\Models\TransactionCharge;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Hash;
use Intervention\Image\ImageManager;
use Illuminate\Support\Facades\Validator;
use Intervention\Image\Drivers\Gd\Driver;

class UserController extends Controller
{
    public function dashboard()
    {
        $notify[]   = 'Dashboard';
        $user       = auth()->user();
        $merchants  = Merchant::active()->where('is_featured', Status::YES)->get();
        $allUtility = SetupUtilityBill::active()->get();
        $appBanners = Frontend::where('data_keys', 'app_banner.element')->get();

        return response()->json([
            'remark'  => 'dashboard',
            'status'  => 'success',
            'message' => ['success' => $notify],
            'data'    => [
                'user'                => $user,
                'all_utility'         => $allUtility,
                'app_banners'         => $appBanners,
                'merchants'           => $merchants,
                'latest_transactions' => Transaction::where('user_id', $user->id)
                    ->where('user_type', 'USER')
                    ->orderBy('id', 'DESC')
                    ->with('receiverUser', 'receiverAgent', 'receiverMerchant', 'mobileRecharge', 'bankTransfer', 'utilityBill', 'donationFor', 'relatedTransaction.user')
                    ->limit(10)
                    ->get(),
            ],
        ]);
    }

    public function userDataSubmit(Request $request)
    {
        $user = auth()->user();
        if ($user->profile_complete == Status::YES) {
            $notify[] = 'You\'ve already completed your profile';
            return responseError('already_completed', $notify);
        }

        $validator = Validator::make($request->all(), [
            'username' => 'required|unique:users|min:6',
            'reference' => 'nullable',
        ]);

        if ($validator->fails()) return responseError('validation_error', $validator->errors());

        if (preg_match("/[^a-z0-9_]/", trim($request->username))) {
            $notify[] = 'No special character, space or capital letters in username';
            return responseError('validation_error', $notify);
        }

        $user->username = $request->username;
        $user->address  = $request->address;
        $user->city     = $request->city;
        $user->state    = $request->state;
        $user->zip      = $request->zip;

        $user->profile_complete = Status::YES;
        $user->save();

        $notify[] = 'Profile completed successfully';
        return responseSuccess('profile_completed', $notify, ['user' => $user]);
    }

    public function kycForm()
    {
        if (auth()->user()->kv == Status::KYC_PENDING) {
            $notify[] = 'Your KYC is under review';
            return responseError('under_review', $notify);
        }
        if (auth()->user()->kv == Status::KYC_VERIFIED) {
            $notify[] = 'You are already KYC verified';
            return responseError('already_verified', $notify);
        }
        $form     = Form::where('act', 'user_kyc')->first();
        $notify[] = 'KYC field is below';
        return responseSuccess('kyc_form', $notify, ['form' => $form->form_data]);
    }

    public function kycSubmit(Request $request)
    {
        $form = Form::where('act', 'user_kyc')->first();
        if (!$form) {
            $notify[] = 'Invalid KYC request';
            return responseError('invalid_request', $notify);
        }
        $formData       = $form->form_data;
        $formProcessor  = new FormProcessor();
        $validationRule = $formProcessor->valueValidation($formData);

        $validator = Validator::make($request->all(), $validationRule);

        if ($validator->fails()) {
            return responseError('validation_error', $validator->errors());
        }

        $user = auth()->user();
        foreach (@$user->kyc_data ?? [] as $kycData) {
            if ($kycData->type == 'file') {
                fileManager()->removeFile(getFilePath('verify') . '/' . $kycData->value);
            }
        }
        $userData = $formProcessor->processFormData($request, $formData);

        $user->kyc_data             = $userData;
        $user->kyc_rejection_reason = null;
        $user->kv                   = Status::KYC_PENDING;
        $user->save();

        $notify[] = 'KYC data submitted successfully';
        return responseSuccess('kyc_submitted', $notify);
    }

    public function depositHistory(Request $request)
    {
        $deposits = auth()->user()->deposits();
        if ($request->search) {
            $deposits = $deposits->where('trx', $request->search);
        }
        $deposits = $deposits->with(['gateway'])->orderBy('id', 'desc')->paginate(getPaginate());
        $notify[] = 'Deposit data';
        return responseSuccess('deposits', $notify, ['deposits' => $deposits]);
    }

    public function transactions(Request $request)
    {
        $times        = ['7days', '15days', '30days', '365days'];
        $remarks      = Transaction::where('user_type', 'USER')->distinct('remark')->pluck('remark')->toArray();
        $transactions = Transaction::where('user_type', 'USER')->where('user_id', auth()->id());

        if ($request->search) {
            $transactions = $transactions->where('trx', $request->search);
        }

        if ($request->type) {
            $type         = $request->type == 'plus' ? '+' : '-';
            $transactions = $transactions->where('trx_type', $type);
        }

        if ($request->remark) {
            $transactions = $transactions->where('remark', $request->remark);
        }

        $transactions = $transactions->orderBy('id', 'desc')
            ->with('receiverUser', 'receiverAgent', 'receiverMerchant', 'mobileRecharge', 'bankTransfer', 'utilityBill', 'donationFor', 'relatedTransaction.user')
            ->paginate(getPaginate());

        $notify[] = 'Transactions data';

        return responseSuccess('transactions', $notify, [
            'transactions' => $transactions,
            'remarks'      => $remarks,
            'times'        => $times
        ]);
    }

    public function submitProfile(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'firstname' => 'required',
            'lastname'  => 'required',
            'image'     => ['image', new FileTypeValidate(['jpg', 'jpeg', 'png'])],
        ], [
            'firstname.required' => 'The first name field is required',
            'lastname.required'  => 'The last name field is required',
        ]);

        if ($validator->fails()) {
            return responseError('validation_error', $validator->errors());
        }

        $user = auth()->user();

        if ($request->hasFile('image')) {
            try {
                $old         = $user->image;
                $user->image = fileUploader($request->image, getFilePath('userProfile'), getFileSize('userProfile'), $old);
            } catch (\Exception $exp) {
                $notify[] = 'Couldn\'t upload your image';
                return responseError('validation_error', $notify);
            }
        }

        $user->firstname = $request->firstname;
        $user->lastname  = $request->lastname;

        $user->address = $request->address;
        $user->city    = $request->city;
        $user->state   = $request->state;
        $user->zip     = $request->zip;

        $user->save();

        $notify[] = 'Profile updated successfully';
        return responseSuccess('profile_updated', $notify);
    }

    public function submitPassword(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'current_password' => 'required',
            'password'         => ['required', 'confirmed', 'digits:4'],
        ], [
            'current_password.required' => 'Current pin is required.',
            'password.required'         => 'New pin is required.',
        ]);

        if ($validator->fails()) {
            return responseError('validation_error', $validator->errors());
        }

        $user = auth()->user();
        if (Hash::check($request->current_password, $user->password)) {
            $password       = Hash::make($request->password);
            $user->password = $password;
            $user->save();
            $notify[] = 'Pin changed successfully';
            return responseSuccess('password_changed', $notify);
        } else {
            $notify[] = 'The pin doesn\'t match!';
            return responseError('validation_error', $notify);
        }
    }

    public function addDeviceToken(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'token' => 'required',
        ]);

        if ($validator->fails()) {
            return responseError('validation_error', $validator->errors());
        }

        $deviceToken = DeviceToken::where('token', $request->token)->first();

        if ($deviceToken) {
            $notify[] = 'Token already exists';
            return responseError('token_exists', $notify);
        }

        $deviceToken            = new DeviceToken();
        $deviceToken->user_id   = auth()->user()->id;
        $deviceToken->user_type = 'USER';
        $deviceToken->token     = $request->token;
        $deviceToken->is_app    = Status::YES;
        $deviceToken->save();

        $notify[] = 'Token saved successfully';
        return responseError('token_saved', $notify);
    }

    public function show2faForm()
    {
        $ga        = new GoogleAuthenticator();
        $user      = auth()->user();
        $secret    = $ga->createSecret();
        $qrCodeUrl = $ga->getQRCodeGoogleUrl($user->username . '@' . gs('site_name'), $secret);
        $notify[]  = '2FA Qr';
        return responseSuccess('2fa_qr', $notify, [
            'secret'      => $secret,
            'qr_code_url' => $qrCodeUrl,
        ]);
    }

    public function create2fa(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'secret' => 'required',
            'code'   => 'required',
        ]);

        if ($validator->fails()) {
            return responseError('validation_error', $validator->errors());
        }

        $user     = auth()->user();
        $response = verifyG2fa($user, $request->code, $request->secret);
        if ($response) {
            $user->tsc = $request->secret;
            $user->ts  = Status::ENABLE;
            $user->save();

            $notify[] = 'Google authenticator activated successfully';
            return responseSuccess('2fa_qr', $notify);
        } else {
            $notify[] = 'Wrong verification code';
            return responseError('wrong_verification', $notify);
        }
    }

    public function disable2fa(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'code' => 'required',
        ]);
        return responseError('validation_error', $validator->errors());

        $user     = auth()->user();
        $response = verifyG2fa($user, $request->code);
        if ($response) {
            $user->tsc = null;
            $user->ts  = Status::DISABLE;
            $user->save();
            $notify[] = 'Two factor authenticator deactivated successfully';
            return responseSuccess('2fa_qr', $notify);
        } else {
            $notify[] = 'Wrong verification code';
            return responseError('wrong_verification', $notify);
        }
    }

    public function pushNotifications()
    {
        $notifications = NotificationLog::where('user_id', auth()->id())->where('sender', 'firebase')->orderBy('id', 'desc')->paginate(getPaginate());
        $notify[]      = 'Push notifications';
        return responseSuccess('notifications', $notify, [
            'notifications' => $notifications,
        ]);
    }

    public function pushNotificationsRead($id)
    {
        $notification = NotificationLog::where('user_id', auth()->id())->where('sender', 'firebase')->find($id);
        if (!$notification) {
            $notify[] = 'Notification not found';
            return responseError('notification_not_found', $notify);
        }

        $notify[] = 'Notification marked as read successfully';
        $notification->user_read = 1;
        $notification->save();
        return responseSuccess('notification_read', $notify);
    }

    public function userInfo()
    {
        $notify[] = 'User information';
        return responseSuccess('user_info', $notify, [
            'user' => auth()->user(),
        ]);
    }

    public function accountDelete(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'password' => 'required',
        ]);

        if ($validator->fails()) {
            return responseError('validation_error', $validator->errors());
        }

        $user = auth()->user();

        if (!Hash::check($request->password, $user->password)) {
            $notify[] = 'Invalid Pin';
            return responseError('invalid_password', $notify);
        }

        $user->is_deleted = Status::YES;
        $user->save();

        $user->tokens()->delete();

        $adminNotification            = new AdminNotification();
        $adminNotification->user_id   = $user->id;
        $adminNotification->user_type = 'USER';
        $adminNotification->title     = $user->username . ' deleted his account.';
        $adminNotification->click_url = urlPath('admin.users.detail', $user->id);
        $adminNotification->save();

        $notify[] = 'Account deleted successfully';
        return responseSuccess('account_deleted', $notify);
    }

    public function trxLimit()
    {
        $transactionCharges = TransactionCharge::get();
        $user               = auth()->user();

        $usedLimit['send_money']    = $user->trxLimit('send_money');
        $usedLimit['cash_in']       = $user->trxLimit('cash_in');
        $usedLimit['cash_out']      = $user->trxLimit('cash_out');
        $usedLimit['bank_transfer'] = $user->trxLimit('bank_transfer');

        $transactionCharges->transform(function ($trxCharge) use ($usedLimit) {
            if (array_key_exists($trxCharge->slug, $usedLimit)) {
                $trxCharge->daily_used   = $usedLimit[$trxCharge->slug]['daily'];
                $trxCharge->monthly_used = $usedLimit[$trxCharge->slug]['monthly'];
            }

            return $trxCharge;
        });

        $notify[] = 'Transaction limit and charge';
        return responseSuccess('trx_charge', $notify, [
            'transaction_charges' => $transactionCharges
        ]);
    }

    public function notificationSettings()
    {
        $notify[] = 'Notification settings';
        return responseSuccess('notification_settings', $notify, [
            'user' => auth()->user()
        ]);
    }

    public function notificationSettingsUpdate(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'en'                          => 'required',
            'pn'                          => 'required',
            'is_allow_promotional_notify' => 'required',
        ]);

        if ($validator->fails()) {
            return responseError('validation_error', $validator->errors());
        }

        $user                              = auth()->user();
        $user->en                          = $request->en ? Status::ENABLE : Status::DISABLE;
        $user->pn                          = $request->pn ? Status::ENABLE : Status::DISABLE;
        $user->is_allow_promotional_notify = $request->is_allow_promotional_notify ? Status::YES : Status::NO;
        $user->save();

        $notify[] = 'Notification settings updated successfully';
        return responseSuccess('notification_settings_update', $notify);
    }

    public function removePromotionalNotificationImage(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'image' => 'required',
        ]);

        $image = $request->image;

        if ($validator->fails()) {
            return responseError('validation_error', $validator->errors());
        }

        if (file_exists($image)) {
            unlink($image);
        }

        $notify[] = 'Promotional notification image deleted successfully';
        return responseSuccess('remove_promotional_notification_image', $notify);
    }

    public function qrCode()
    {
        $notify[]   = 'QR Code';
        $user       = auth()->user();
        $qrCode     = $user->createQrCode();
        $uniqueCode = $qrCode->unique_code;
        $qrCode     = cryptoQR($uniqueCode);

        return responseSuccess('qr_code', $notify, [
            'qr_code' => $qrCode
        ]);
    }

    public function qrCodeDownload()
    {

        $user    = auth()->user();
        $qrCode  = $user->qrCode()->first();
        $general = gs();

        $file     = cryptoQR($qrCode->unique_code);
        $filename = 'user_qr_code_' . $user->id . '.jpg';

        $template = 'default.png';
        if ($general->qr_code_template) {
            $template = $general->qr_code_template;
        }

        $manager  = new ImageManager(new Driver());
        $template = $manager->read('assets/images/qr_code_template/' . $template);

        $client       = new Client();
        $response     = $client->get($file);
        $imageContent = $response->getBody()->getContents();

        $qrCode = $manager->read($imageContent)->cover(2000, 2000);
        $template->place($qrCode, 'center');
        $image = $template->encode();

        $headers = [
            'Content-Type'        => 'image/jpeg',
            'Content-Disposition' => 'attachment; filename=' . $filename,
        ];

        return response()->stream(function () use ($image) {
            echo $image;
        }, 200, $headers);
    }

    public function qrCodeRemove(Request $request)
    {

        $validator = Validator::make($request->all(), [
            'file_name' => 'required',
        ]);

        if ($validator->fails()) {
            return responseError('validation_error', $validator->errors());
        }

        $file = getFilePath('temporary') . '/' . $request->file_name;

        if (file_exists($file)) {
            unlink($file);
            $notify[] = 'QR code removed successfully';
            return responseSuccess('qr_code_remove', $notify);
        }
    }

    public function qrCodeScan(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'code' => 'required',
        ]);

        if ($validator->fails()) {
            return responseError('validation_error', $validator->errors());
        }

        $data = QrCode::where('unique_code', $request->code)->first();
        if (!$data) {
            $notify[] = 'QR code doesn\'t match';
            return responseError('validation_error', $notify);
        }

        if ($data->user_type == 'USER' && $data->user_id == auth()->user('user')->id) {
            $notify[] = 'Can\'t transact because it\'s your QR code.';
            return responseError('validation_error', $notify);
        }

        $notify[] =  'QR code scan';
        return responseSuccess('qr_code_scan', $notify, [
            'user_type'          => $data->user_type,
            'user_data'          => $data->getUser,
            'transaction_charge' => TransactionCharge::get(),
        ]);
    }

    public function validatePin(Request $request)
    {
        $validator = Validator::make($request->all(), [
            'pin' => 'required',
        ]);

        if ($validator->fails()) {
            return responseError('validation_error', $validator->errors());
        }

        $user = auth()->user();

        if (!Hash::check($request->pin, $user->password)) {
            $notify[] = 'Provided PIN does not correct';
            return responseError('validation_error', $notify);
        }

        $notify[] = 'PIN validation';
        return responseSuccess('valid_pin', $notify);
    }

}
