<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\User;
use DB;
use Input;

    
function generateHash($code){
    $split = str_split($code);
    $hash = "";
    foreach($split as $char){
        $hash .= md5($char);
    }
    return md5($hash);

}

class TwoFactorController extends Controller
{
    public function verify_new(Request $r){
		$myUser = newGetUser(true);

        if(is_null($myUser)){
            if($r->isMethod('get')){
                return abort(404);
            }else{
                return response()->json(['status' => 'err']);
            }
        }

        $google2fa = app('pragmarx.google2fa');
        
        if($myUser->tfa_locked){
            return redirect('/account/locked');
        }

        if($r->isMethod('post')){
    
            if($myUser->tfa_locked){
                return response()->json(['status' => 'err', 'locked' => 1]);
            }

            if($myUser->tfa_enabled){
                return response()->json(['status' => 'err']);
            }

            $code = Input::get('code');

            if(empty($code) || strlen($code) != 6 || !is_numeric($code)){
                return response()->json(['status' => 'err', 'msg' => 'Invalid code.']);
            }

            $valid = $google2fa->verifyKey($myUser->tfa_secret, $code, 3);

            if($valid){
                $myUser->tfa_enabled = 1;
                $myUser->tfa_auth = 1;
                $myUser->tfa_added = floor(microtime(true) * 1000);

                $myUser->save();
                // verified
                return response()->json(['status' => 'done']);
            }else{
                return response()->json(['status' => 'err', 'msg' => 'Wrong code']);
            }
        }


		if(is_null($myUser)){
			return redirect('/');
		}

		if($myUser->tfa_enabled){

            if($myUser->tfa_auth) return redirect('/');
            
            return redirect('/verify');
        }
        $secret;
        if($myUser->tfa_secret == null){
            $secret = $google2fa->generateSecretKey();
        }else{
            $secret = $myUser->tfa_secret;
        }
		$barcode = $google2fa->getQRCodeInline(
			config('app.name'),
			"ID: " . $myUser->id,
			$secret
        );

        $myUser->tfa_secret = $secret;
        $myUser->save();

		return view('register_2fa', ['myUser' => $myUser, 'barcode' => $barcode, 'secret' => $secret]);
		
	}

	public function verify(Request $r){
		$myUser = newGetUser(true);

        if(is_null($myUser)){
            if($r->isMethod('get')){
                return abort(404);
            }else{
                return response()->json(['status' => 'err']);
            }
        }

        $google2fa = app('pragmarx.google2fa');
        
        if($myUser->tfa_locked){
            return redirect('/account/locked');
        }

        $tfa_session = isset($_COOKIE['2fa']) ? $_COOKIE['2fa'] : null;
        if($r->isMethod('post')){
            
            if($myUser->tfa_locked){
                return response()->json(['status' => 'err', 'locked' => 1]);
            }

            if(!$myUser->tfa_enabled){
                return response()->json(['status' => 'err']);
            }

            if(!isset($tfa_session) || strlen($tfa_session) != 24){
                return response()->json(['status' => 'err']);
            }

            if($tfa_session != $myUser->tfa_session){
                return response()->json(['status' => 'err']);
            }

            $code = Input::get('code');

            if(empty($code) || strlen($code) != 6 || !is_numeric($code)){
                return response()->json(['status' => 'err', 'msg' => 'Invalid code.']);
            }
            $microtime = floor(microtime(true) * 1000);

            $attempts = DB::table('twofactorlogs')->where('UserID', $myUser->id)->where('Date', '>', $microtime - 7200000)->limit(5)->orderBy('Date', 'desc')->get(['Status', 'CodeHash', 'Date']);
            
            $hash = generateHash($code);
            
            if($attempts->first() != null && $attempts->first()->CodeHash == $hash){
                return response()->json(['status' => 'err', 'locked' => 0, 'msg' => 'Invalid code.']);
            }
            
            $valid = $google2fa->verifyKey($myUser->tfa_secret, $code, 4);

            if($valid){
                $myUser->tfa_auth = 1;
                $myUser->save();
                
                
                DB::table('twofactorlogs')->insert(['UserID' => $myUser->id, 'Status' => 1, 'Date' => $microtime, 'CodeHash' => $hash]);
                // verified
                return response()->json(['status' => 'done']);
            }else{
                DB::table('twofactorlogs')->insert(['UserID' => $myUser->id, 'Status' => 0, 'Date' => $microtime, 'CodeHash' => $hash]);

                $failed = 1;

                if(count($attempts) >= 4){
                    foreach($attempts as $attempt){
                        if($attempt->Status == 0){
                            $failed++;
                        }
                    }
                }

                if($failed == 5){ // 5 failed attempts; lock account
                    $myUser->tfa_locked = 1;
                    $myUser->save();
                    return response()->json(['status' => 'err', 'locked' => 1]);
                }

                return response()->json(['status' => 'err', 'locked' => 0, 'msg' => 'Invalid code.']);
            }
        }

        if($myUser->tfa_session != $tfa_session){
            setcookie('session', '', time() - 3600, '/', '.'.currentDomain());
			return redirect('/');
        }
		if(is_null($myUser)){
			return redirect('/');
		}

		if(!$myUser->tfa_enabled){
			return redirect('/verify/new');
        }else if($myUser->tfa_auth){
            return redirect('/');
        }

		return view('login_2fa', ['myUser' => $myUser]);
		
    }

    function disable2FA(Request $r){
        
        if(!$r->isMethod('post')){
            return response()->json('error');
        }
        
        $myUser = newGetUser();
        
        if(!$myUser->tfa_enabled){
            return response()->json('error');
        }

        $code = Input::get('code'); // 2fa code to disable it

        if(empty($code)){
            return response()->json('error');
        }

        $google2fa = app('pragmarx.google2fa');
        $valid = $google2fa->verifyKey($myUser->tfa_secret, $code, 4);

        $hash = generateHash($code);


        $microtime = floor(microtime(true) * 1000);
        // get previous code attempts
        $attempts = DB::table('twofactorlogs')->where('UserID', $myUser->id)->where('Date', '>', $microtime - 7200000)->limit(5)->orderBy('Date', 'desc')->get(['Status', 'CodeHash', 'Date']);
        
        
        if($valid){ // valid code, disable 2fa

            $myUser->tfa_enabled = 0;
            $myUser->tfa_secret = null;
            $myUser->tfa_auth = 0;
            $myUser->tfa_added = 0;
            $myUser->save();

            
            DB::table('twofactorlogs')->insert(['UserID' => $myUser->id, 'Status' => 1, 'Date' => $microtime, 'CodeHash' => $hash]);
            
            return response()->json('done');
        }else{ // wrong code

            DB::table('twofactorlogs')->insert(['UserID' => $myUser->id, 'Status' => 0, 'Date' => $microtime, 'CodeHash' => $hash]);

            $failed = 1;

            if(count($attempts) >= 4){
                foreach($attempts as $attempt){
                    if($attempt->Status == 0){
                        $failed++;
                    }
                }
            }

            if($failed == 5){ // 5 failed attempts; lock account
                $myUser->tfa_locked = 1;
                $myUser->save();
                return response()->json(['status' => 'err', 'locked' => 1]);
            }
            
            return response()->json(['status' => 'err', 'locked' => 0, 'msg' => 'Invalid code.']);
        }

    }

    function enable2FA(Request $r){
        
        if(!$r->isMethod('post')){
            return response()->json('error');
        }
        
        $myUser = newGetUser();
        
        if($myUser->tfa_enabled){
            return response()->json('error');
        }

        $myUser->tfa_enabled = 1;
        $myUser->save();
        return response()->json('done');
    }
}
