<?php

namespace AuthStack\Driver\AUDGeneric\App\Controllers;

use DB;
use Exception;
use Validator;
use GuzzleHttp\Client;
use Illuminate\Http\Response;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use AuthStack\Driver\AUDGeneric\Driver;
use GuzzleHttp\Exception\GuzzleException;
use AuthStack\Http\Controllers\Controller;
use AuthStack\Driver\Errors\NoAuthSourceError;
use AuthStack\Driver\AUDGeneric\App\Services\Token;
use AuthStack\Driver\AUDGeneric\App\Services\Email;
use AuthStack\Driver\AUDGeneric\App\Requests\PasswordReset;
use AuthStack\Driver\AUDGeneric\App\Requests\UpdatePassword;
use AuthStack\Driver\AUDGeneric\App\Requests\PasswordResetFromSP;
use AuthStack\Driver\AUDGeneric\App\Requests\PasswordResetWithToken;

class AUDPassword extends Controller
{
    protected $driver;

    public function __construct()
    {
        $this->driver = new Driver();
    }

    /**
     * @return Response
     */
    public function passwordInit()
    {
        return response()->view('aud::password.forgot', [], 200);
    }

    /**
     * @param PasswordReset $request
     * @return Response
     * @throws NoAuthSourceError
     */
    public function passwordReset(PasswordReset $request)
    {
        $connId = $this->driver->getAuthSourceId();
        $user = DB::table('users')
            ->where('conn_id', '=', $connId)
            ->where('username', '=', $request->getUsername())
            ->first();

        //Check if the user exists
        $user = (array)$user;
        if (count($user) < 1) {
            return response()->view('aud::password.message', [
                'title' => '',
                'message' => 'Sorry, username cannot be found.'
            ], 500);
        }

        $json = $user['config'];
        $config = json_decode($json, true);
        $email = $config['attributes']['email'];

        $tokenData = Token::tokenHandler($request, $email, $connId);

        $mail = Email::sendResetEmail($config['attributes'], $tokenData->token);

        if ($mail) {
            return response()->view('aud::password.message', [
                'title' => 'Email sent',
                'message' => 'We have emailed you your recovery link.'
            ], 200);
        } else {
            return response()->view('aud::password.message', [
                'title' => '',
                'message' => 'Sorry, something when wrong while sending the email'
            ], 500);
        }
    }

    /**
     * @param PasswordResetWithToken $request
     * @return Response
     * @throws NoAuthSourceError
     */
    public function passwordResetWithToken(PasswordResetWithToken $request)
    {
        $validate = Token::validateToken($request);

        if (!$validate) {
            return response()->view('aud::password.message', [
                'title' => '',
                'message' => 'Sorry, this request is not valid.'
            ], 500);
        }

        return response()->view('aud::password.reset', [
            'email' => $request->getEmail(),
            'token' => $request->getToken(),
        ], 200);
    }

    /**
     * @param UpdatePassword $request
     * @return RedirectResponse|Response
     * @throws GuzzleException
     * @throws NoAuthSourceError
     */
    public function updatePassword(UpdatePassword $request)
    {
        $validator = Validator::make($request->all(), [
            'token' => 'required',
            'password' => 'required|confirmed'
        ]);

        if ($validator->fails()) {

            session()->put('passwordError', [
                'message' => 'Validation failed',
                'errors' => [$validator->errors()->first()],
            ]);

            return redirect()->back(301);
        }

        $tokenData = DB::table('password_resets')
            ->where('token', $request->getToken())
            ->first();

        $user = DB::table('users')
            ->where('username', '=', $tokenData->username)
            ->where('conn_id', $this->driver->getAuthSourceId())
            ->first();

        $user = (array)$user;
        $json = $user['config'];
        $config = json_decode($json, true);
        $id = $config['attributes']['id'];

        if ($tokenData) {
            $base = env('AUD_URL');
            $api = "/api/v1/account/$id/password";
            $url = $base . $api;
            $verify = env('AUD_VERIFY_SSL', true);

            $options = [
                'verify' => $verify,
                'form_params' => [
                    'password' => $request->getPassword()
                ]
            ];

            try {
                $client = new Client(['defaults' => ['verify' => $verify]]);
                $res = $client->request('PUT', $url, $options);
                $res = $res->getBody()->getContents();

                $response = json_decode($res, true);

                if (isset($response['result']) && $response['result']) {

                    DB::table('password_resets')
                        ->where('token', $request->getToken())
                        ->update(['active' => false]);

                    return response()->view('aud::password.message', [
                        'title' => 'Success!',
                        'message' => 'Your password was successfully updated'
                    ], 200);
                }

            } catch (Exception $e) {
                \Log::error('Password Reset Failure:', [
                    'message' => $e->getMessage(),
                    'file' => $e->getFile(),
                    'line' => $e->getLine()
                ]);

                $split = explode('response:', $e->getMessage());

                if (isset($split[1])) {
                    $arr = json_decode($split[1], true);
                    session()->put('passwordError', [
                        'message' => $arr['message'],
                        'errors' => $arr['errors'],
                    ]);
                } else {
                    session()->put('passwordError', [
                        'message' => 'Invalid data supplied',
                        'errors' => [],
                    ]);
                }

                return redirect()->back(301);
            }
        }
        return response()->view('aud::password.message', [
            'title' => '',
            'message' => 'Sorry, this request is not longer valid.'
        ], 500);
    }

    /**
     * @param PasswordResetFromSP $request
     * @return JsonResponse
     * @throws GuzzleException
     * @throws NoAuthSourceError
     */
    public function passwordResetFromSP(PasswordResetFromSP $request)
    {
        try {
            $this->driver->authenticate([
                'username' => $request->getUsername(),
                'password' => $request->getOldPassword(),
            ]);
        } catch (Exception $e) {
            \Log::error('(SP) Password Reset Failure:', [
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ]);

            $error = json_decode($e->getMessage(), true);
            $eMessage = isset($error['error'][0]) ? $error['error'][0] : 'Invalid password';

            return response()->json([
                'message' => 'Invalid data supplied',
                'errors' => ['password' => $eMessage]
            ])->setStatusCode(401);
        }

        $user = DB::table('users')
            ->where('username', $request->getUsername())
            ->where('conn_id', $this->driver->getAuthSourceId())
            ->first();

        $user = (array)$user;
        $json = $user['config'];
        $config = json_decode($json, true);
        $id = $config['attributes']['id'];

        $base = env('AUD_URL');
        $api = "/api/v1/account/$id/password";
        $url = $base . $api;
        $verify = env('AUD_VERIFY_SSL', true);

        $options = [
            'verify' => $verify,
            'form_params' => [
                'password' => $request->getPassword()
            ]
        ];

        try {
            $client = new Client(['defaults' => ['verify' => $verify]]);
            $res = $client->request('PUT', $url, $options);
            $res = $res->getBody()->getContents();

            $response = json_decode($res, true);

            if (isset($response['result']) && $response['result']) {
                return response()->json([
                    'message' => 'Success, Your password was successfully updated',
                    'errors' => []
                ])->setStatusCode(200);
            }

        } catch (Exception $e) {
            \Log::error('(SP) Password Reset Failure:', [
                'message' => $e->getMessage(),
                'file' => $e->getFile(),
                'line' => $e->getLine()
            ]);

            $split = explode('response:', $e->getMessage());

            if (isset($split[1])) {
                $arr = json_decode($split[1], true);

                return response()->json([
                    'message' => $arr['message'],
                    'errors' => $arr['errors']
                ])->setStatusCode(422);
            } else {
                return response()->json([
                    'message' => 'Invalid data supplied',
                    'errors' => []
                ])->setStatusCode(422);
            }
        }

    }
}