<?php

namespace App\Http\Controllers\Admin;

use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Carbon;
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
use App\Models\User;
use App\Models\Group;
use App\Models\Organisation;
use App\Mail\UserInvitation;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\Mail;
use App\Http\Controllers\Controller;
use App\Mail\PasswordReset as MailPasswordReset;
use App\Models\PasswordReset;
use App\Notifications\SlackNotification;
use Illuminate\Support\Facades\Notification;

class WhmcsController extends Controller
{
    use AuthorizesRequests;

    /**
     * Create a new controller instance.
     *
     * @return void
     */
    public function __construct()
    {
        $this->middleware('whmcs');
    }

    public function email_change(Request $_request) {
        $validator = Validator::make($_request->input(), [
            'old-email' => 'required|email:rfc,dns',
            'new-email' => 'required|email:rfc,dns',
        ]);

        $old_email            = $_request->input('old-email');
        $new_email            = $_request->input('new-email');
        $notification_message = "Failed to alter user email. OLD EMAIL: $old_email, NEW EMAIL: $new_email, REASON: FALL THROUGH";
        $response_code        = 500;

        if(!$validator->fails()) {
            // Find the account - If we cannot then return a failure to WHMCS and throw up a notification.
            if($user = User::where('email', '=', $old_email)->first()) {
                if($user->canLogin()) {
                    // Setup new email
                    $user->email = $new_email;

                    if($user->save()) {
                        $response_code = 200;
                        $notification_message = "Altered user email. OLD EMAIL: $old_email, NEW EMAIL: $new_email";
                    }
                    else {
                        $notification_message = "Failed to alter user email. OLD EMAIL: $old_email, NEW EMAIL: $new_email, REASON: FAILED TO SAVE.";
                    }
                }
                $notification_message = "Failed to alter user email. OLD EMAIL: $old_email, NEW EMAIL: $new_email, REASON: USER ACCOUNT TERMINATED OR SUSPENDED.";
            }
            else {
                $notification_message = "Failed to alter user email. OLD EMAIL: $old_email, NEW EMAIL: $new_email, REASON: USER WITH THAT OLD EMAIL ASSOCIATED WITH THEIR MERCURY ACCOUNT DOES NOT EXIST";
            }
        }
        else {
            $notification_message = "Failed to alter user email. OLD EMAIL: $old_email, NEW EMAIL: $new_email, REASON: INPUT INVALID";
        }

        Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification($notification_message));
        return response($response_code === 200 ? 'SUCCESS' : 'FAILURE', $response_code);
    }

    public function password_reset(Request $_request) {
        $validator = Validator::make($_request->input(), [
            'email' => 'required|email:rfc,dns',
        ]);

        $user_email                  = $_request->input('email');
        $failed_notification_message = "Failed to send email for user password reset. SUBMITTED EMAIL: $user_email, REASON: UNKNOWN";

        if(!$validator->fails()) {
            // Find the account. If we cannot, 
            if($user = User::where('email', '=', $user_email)->first()) {
                if($user->canLogin()) {
                    // Generate a random token.
                    $email_token = Str::random(32);

                    // Keep generating tokens until we find one that isn't taken.
                    while(PasswordReset::where('token', '=', $email_token)->count()) {
                        $email_token = Str::random(32);
                    }

                    PasswordReset::create([
                        'email' => $user_email,
                        'token' => $email_token,
                        'expires_at' => Carbon::now()->addHours(48),
                        'user_id' => $user->id,
                        'used' => 0
                    ]);

                    // Send our invitation.
                    Mail::to($user_email)->send(new MailPasswordReset($email_token));

                    Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification("Password reset email sent. EMAIL: $user_email"));
                    return response('SUCCESS', 200);
                }
                $failed_notification_message = "Failed to send email for user password reset. SUBMITTED EMAIL: $user_email, REASON: USER ACCOUNT TERMINATED OR SUSPENDED.";
            }
            else {
                $failed_notification_message = "Failed to send email for user password reset. SUBMITTED EMAIL: $user_email, REASON: COULD NOT FIND ACCOUNT WITH THAT EMAIL. IF THE EMAIL HAS CHANGED, USE THE OLD EMAIL FIELD TO TRIGGER CORRECTION THEN ATTEMPT PASSWORD RESET AGAIN.";
            }
        }
        else {
            $failed_notification_message = "Failed to send email for user password reset. SUBMITTED EMAIL: $user_email, REASON: INPUT INVALID";
        }

        Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification($failed_notification_message));
        return response('FAILURE', 500);
    }

    public function cancel(Request $_request) {
        $validator = Validator::make($_request->input(), [
            'email' => 'required|email:rfc,dns',
        ]);

        $user_email                  = $_request->input('email');
        $failed_notification_message = "Failed to terminate service. EMAIL: $user_email, REASON: UNKNOWN";

        if(!$validator->fails()) {
            // If this account already exists
            if($user = User::where('email', '=', $user_email)->first()) {
                try {
                    Organisation::where('id', '=', $user->organisation_id)->delete();

                    Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification("Deleted organisation due to product termination. EMAIL: $user_email"));
                    return response('SUCCESS', 200);
                }
                catch(\Exception $ex) {
                    $failed_notification_message = "Failed to terminate service. EMAIL: $user_email, REASON: TECHNICAL ERROR";
                }
            }
            else {
                $failed_notification_message = "Failed to terminate service. EMAIL: $user_email, REASON: ACCOUNT CANNOT BE FOUND";
            }
        }

        Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification($failed_notification_message));
        return response('FAILURE', 500);
    }

    public function suspend(Request $_request) {
        $validator = Validator::make($_request->input(), [
            'email' => 'required|email:rfc,dns',
        ]);

        $user_email = $_request->input('email');
        $failed_notification_message = "Failed to suspend accounts for organisation. EMAIL: $user_email, REASON: UNKNOWN";

        if(!$validator->fails()) {
            // If this account already exists
            if($user = User::where('email', '=', $_request->input('email'))->first()) {
                try {
                    // Suspend access for an organisation.
                    Organisation::find($user->organisation_id)->suspend();

                    // Send notification.
                    Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification("Successfully suspended accounts for organisation. EMAIL: $user_email"));
                    return response('SUCCESS', 200);
                }
                catch(\Exception $ex) {
                    $failed_notification_message = "Failed to suspend accounts for organisation. EMAIL: $user_email, REASON: TECHNICAL ERROR";
                }
            }
            $failed_notification_message = "Failed to suspend accounts for organisation. EMAIL: $user_email, REASON: COULD NOT FIND ACCOUNT";
        }

        Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification($failed_notification_message));
        return response('FAILURE', 500);
    }

    public function unsuspend(Request $_request) {
        $validator = Validator::make($_request->input(), [
            'email' => 'required|email:rfc,dns',
        ]);

        $user_email = $_request->input('email');
        $failed_notification_message = "Failed to unsuspend accounts for organisation. EMAIL: $user_email, REASON: UNKNOWN";

        if(!$validator->fails()) {
            // If this account already exists
            if($user = User::where('email', '=', $_request->input('email'))->first()) {
                try {
                    // Grant access to an organisation once again.
                    Organisation::find($user->organisation_id)->unsuspend();
                    
                    // Send notification.
                    Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification("Successfully unsuspended accounts for organisation. EMAIL: $user_email"));
                    return response('SUCCESS', 200);
                }
                catch(\Exception $ex) {
                    $failed_notification_message = "Failed to unsuspend accounts for organisation. EMAIL: $user_email, REASON: TECHNICAL ERROR";
                }
            }
            $failed_notification_message = "Failed to unsuspend accounts for organisation. EMAIL: $user_email, REASON: COULD NOT FIND ACCOUNT";
        }

        Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification($failed_notification_message));
        return response('FAILURE', 500);
    }

    public function purchase(Request $_request) {
        Log::debug("Purchase hit");
        $validator = Validator::make($_request->input(), [
            'organisation' => 'required|string',
            'email' => 'required|email:rfc,dns',
            'bandwidth-cap' => 'required|integer|min:0'
        ]);

        $organisation_name    = $_request->input('organisation');
        $user_email           = $_request->input('email');
        $bandwidth_cap        = $_request->input('bandwidth-cap', 0);

        try {
            if(!$validator->fails()) {
                
                // If this account already exists - This shouldn't happen, but I shouldn't doubt that it can.
                if($user = User::where('email', '=', $user_email)->first()) {
                    
                    // Update the user organisation.
                    Organisation::onlyTrashed()->where('id', '=', $user->organisation_id)->restore();
                    
                    // Unsuspend organisation - It may be suspended.
                    Organisation::find($user->organisation_id)->unsuspend();

                    // Generate a random token.
                    $email_token = Str::random(32);

                    // Keep generating tokens until we find one that isn't taken.
                    while(User::where('email_token', '=', $email_token)->count()) {
                        $email_token = Str::random(32);
                    }

                    $user->email_token = $email_token;
                    $user->password = '';
                    $user->save();

                    Mail::to($user_email)->send(new UserInvitation($email_token));
                }
                else { // More realistic scenario - User account does not exist.
                    // Generate a random token.
                    $email_token = Str::random(32);

                    Log::debug("Testicus");

                    // Keep generating tokens until we find one that isn't taken.
                    while(User::where('email_token', '=', $email_token)->count()) {
                        $email_token = Str::random(32);
                    }

                    $organisation = Organisation::create([
                        'name' => $organisation_name
                    ]);


                    User::create([
                        'name' => "$organisation_name NOC",
                        'email' => $user_email,
                        'organisation_id' => $organisation->id,
                        'group_id' => Group::where('name', '=', 'Default')->get()->first()->id,
                        'email_token' => $email_token
                    ]);

                    Mail::to($user_email)->send(new UserInvitation($email_token));
                }

                Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification("Successfully provisioned new account. NAME: $organisation_name, EMAIL: $user_email, BANDWIDTH CAP: $bandwidth_cap - VIA WHMCS, NO SPECIAL CONDITIONS APPLIED."));
                return response('SUCCESS', 200);
            }
        }
        catch(\Exception $ex) {
            Log::debug(json_encode($ex));
        }

        Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification("Failed to provision new account. NAME: $organisation_name, EMAIL: $user_email, BANDWIDTH CAP: $bandwidth_cap"));
        return response('FAILURE', 500);
    }

    public function change(Request $_request) {
        $validator = Validator::make($_request->input(), [
            'email' => 'required|email:rfc,dns',
            'bandwidth-cap' => 'required|integer|min:0'
        ]);

        $user_email                  = $_request->input('email');
        $bandwidth_cap               = $_request->input('bandwidth-cap', 0);
        $failed_notification_message = "Failed to change existing service. EMAIL: $user_email, BANDWIDTH CAP: $bandwidth_cap, REASON: UNKNOWN";

        try {
            if(!$validator->fails()) {
                
                // If this account already exists - This shouldn't happen, but I shouldn't doubt that it can.
                if($user = User::where('email', '=', $user_email)->first()) {
                    
                    Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification("Successfully changed existing service. EMAIL: $user_email, BANDWIDTH CAP: $bandwidth_cap"));
                    return response('SUCCESS', 200);
                }

                $failed_notification_message = "Failed to change existing service. EMAIL: $user_email, BANDWIDTH CAP: $bandwidth_cap, REASON: USER DOES NOT EXIST";
            }
            else {
                $failed_notification_message = "Failed to change existing service. EMAIL: $user_email, BANDWIDTH CAP: $bandwidth_cap, REASON: INPUT INVALID";
            }
        }
        catch(\Exception $ex) {}

        Notification::route('slack', env('SLACK_HOOK'))->notify(new SlackNotification($failed_notification_message));
        return response('FAILURE', 500);
    }
}
