<?php

namespace App\Modules\Company\Users;

use App\Models\BatchInvitationLink;
use App\Models\BatchInvitationLinkRole;
use App\Models\Company;
use App\Models\CompanyExperience;
// use App\Models\Role;
// use Illuminate\Support\Collection;
use App\Traits\CreateUserActionTraits;
use App\Traits\SearchTraits;
use App\Traits\SortingTraits;
use App\Traits\ValidatorTraits;
use App\Traits\UserTraits;
use Illuminate\Http\Resources\Json\JsonResource;
use Illuminate\Support\Str;
use Carbon\Carbon;

class BatchInvitationLinkActions
{
    use SortingTraits, SearchTraits, UserTraits, ValidatorTraits, createUserActionTraits;
    private function isExpired($entry = NULL) {
        if (isset($entry)) {
            if (!$entry->expired) {
                if (
                    strtotime($entry->expiry_date) > strtotime('now') &&
                    $entry->total_registrants < $entry->max_registrants
                ) {
                    return false;
                }
                $entry->expired = true;
                $entry->save();
            }
        }
        return true;
    }
    /**
     * Display a listing of the resource.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function getInviteLinks($request)
    {
        $sortField = $this->sortField($request, 'id'); // name = default column to search
        $sortOrder = $this->sortOrder($request);
        $nameSearch = $this->searchField($request->name);
        $roleSearch = $request->roles;
        $links = BatchInvitationLink::select(
            'batch_invitation_links.id',
            'batch_invitation_links.company_id',
            'batch_invitation_links.name',
            'batch_invitation_links.expiry_date',
            'batch_invitation_links.expired',
            'batch_invitation_links.locked',
            'batch_invitation_links.rate',
            'batch_invitation_links.token',
            'batch_invitation_links.total_registrants',
            'batch_invitation_links.max_registrants'
        )
            ->join('batch_invitation_link_roles', 'batch_invitation_link_roles.batch_invitation_link_id', '=', 'batch_invitation_links.id')
            ->with('roles')->whereHas('roles', function ($query) use ($roleSearch) {
                $query->when(!empty($roleSearch), function ($query) use ($roleSearch) {
                    return $query
                        ->join('roles', 'roles.id', '=', 'batch_invitation_link_roles.role_id')
                        ->whereIn('roles.id', $roleSearch);
                });
            })
            ->where('company_id', $this->getCurrentUser()->company_id)
            ->when(!empty($nameSearch), function ($query) use ($nameSearch) {
                return $query->where(function ($query) use ($nameSearch) {
                    $query->where('batch_invitation_links.name', 'LIKE', '%'. $nameSearch . '%');
                });
            })
            ->orderBy($sortField, $sortOrder)
            ->groupBy('batch_invitation_link_roles.batch_invitation_link_id')
            ->paginate(20);
        // return response()->json($links)->setStatusCode(201);
        foreach ($links as &$item) {
            $this->isExpired($item);
        }
        return JsonResource::collection($links)->response()->setStatusCode(201);
    }

    /**
     * Verify the id and token of the invite link.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function verifyLink($request)
    {
        $validate = $this->validateRequest($request, [
            'id' => 'required|integer',
            'token' => 'required|max:20'
        ]);
        if ($validate) {
            return $validate;
        }
        $entry = BatchInvitationLink::where([
            'id' => $request->id,
            'token' => $request->token
        ])->first();
        $response = [];
        if (isset($entry)) {
            if ($this->isExpired($entry)) {
                $response['error'] = 'Expired invite link.';
            } else if ($entry->locked) {
                $response['error'] = 'Invite link is currently locked.';
            } else {
                $response['success'] = 'Valid invite link. Congratulations.';
                $response['companyExperiences'] = CompanyExperience::where('company_id', $entry->company_id)->get();
                $response['isWorker'] = false;
                $role_entries = BatchInvitationLinkRole::where([
                    'batch_invitation_link_id' => $entry->id
                ])->get();
                foreach ($role_entries as $linkrole) {
                    if ($linkrole->role_id === 5) {
                        $response['isWorker'] = true;
                        break;
                    }
                }
            }
        } else {
            $response['error'] = 'Invalid invite link.';
        }
        return response()->json($response)->setStatusCode(201);
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function createLink($request)
    {
        $validate = $this->validateRequest($request, [
            'expiry_date' => 'required',
            'hourly_rate' => 'required|numeric|min:1|max:10000',
            'max_registrants' => 'required|integer',
            'name' => 'required|max:100',
        ]);
        if ($validate) {
            return $validate;
        }
        $name = trim($request->name);
        $existing = BatchInvitationLink::where([
            'company_id' => $this->getCurrentUser()->company_id,
            'name' => $name
        ])->first();
        if (!$existing) {
            $entry = BatchInvitationLink::create([
                'company_id' => $this->getCurrentUser()->company_id,
                'expiry_date' => $request->expiry_date,
                'max_registrants' => $request->max_registrants,
                'name' => $name,
                'rate' => $request->hourly_rate,
                'token' => Str::random(20)
            ]);
            $roles = $request->roles ?: [];
            if (count($roles) === 0) {
                return response()->json([
                    'error' => 'A minimum of one role is required',
                    'field_name' => 'roles'
                ])->setStatusCode(201);
            }
            foreach ($roles as $role) {
                BatchInvitationLinkRole::create([
                    'batch_invitation_link_id' => $entry->id,
                    'role_id' => $role
                ]);
            }
            // $this->createUserAction('Created invite link "' . $name . '" [ID:' . $entry->id . ']');
            $this->createUserAction('Created invite link "' . $name . '"');
            return response()->json([
                'name' => $name,
                'search' => '?id=' . $entry->id . '&token=' . $entry->token,
                'success' => true
            ])->setStatusCode(201);
        }
        return response()->json([
            'error' => 'Name is already taken!',
            'field_name' => 'name',
            'name' => $name
        ])->setStatusCode(201);
    }

    /**
     * Edit a resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function editLink($request)
    {
        $validate = $this->validateRequest($request, [
            'expiry_date' => 'required',
            'hourly_rate' => 'required|numeric|min:1|max:10000',
            'id' => 'required|integer',
            'max_registrants' => 'required|integer',
            'name' => 'required|max:100',
        ]);
        if ($validate) {
            return $validate;
        }
        $name = trim($request->name);
        $existing = BatchInvitationLink::where([
            'company_id' => $this->getCurrentUser()->company_id,
            'name' => $name
        ])->with('roles')->first();
        if ($existing && $existing->id !== $request->id) {
            return response()->json([
                'error' => 'The name "' . $name . '" is already taken!',
                'field_name' => 'name',
                'value' => $name
            ])->setStatusCode(201);
        }
        $roles = $request->roles ?: [];
        if (count($roles) === 0) {
            return response()->json([
                'error' => 'A minimum of one role is required',
                'field_name' => 'roles'
            ])->setStatusCode(201);
        }
        $entry = $existing ?: BatchInvitationLink::where([
            'id' => $request->id
        ])->with('roles')->first();
        $this->isExpired($entry);
        $original_name = $entry->name;
        $entry->name = $name;
        if ($request->max_registrants < $entry->total_registrants) {
            return response()->json([
                'error' => 'The maximum number of registrants should be more than the current total.',
                'field_name' => 'max_registrants',
                'value' => $request->max_registrants
            ])->setStatusCode(201);
        }
        if (!$request->hourly_rate) {
            return response()->json([
                'error' => 'Value should be nonzero.',
                'field_name' => 'hourly_rate',
                'value' => $request->hourly_rate
            ])->setStatusCode(201);
        }
        if ($entry->expired) {
            if (
                (strtotime($request->expiry_date) > strtotime('now')) &&
                ($request->max_registrants > $entry->total_registrants)
            ) {
                $entry->expired = false;
                $entry->locked = false;
            }
        } else {
            if (strtotime($request->expiry_date) <= strtotime('now')) {
                return response()->json([
                    'error' => 'Date has already passed.',
                    'field_name' => 'expiry_date',
                    'value' => $request->expiry_date
                ])->setStatusCode(201);
            }
            if ($request->max_registrants === $entry->total_registrants) {
                $entry->expired = true;
            }
        }
        $entry->expiry_date = $request->expiry_date;
        $entry->max_registrants = $request->max_registrants;
        $entry->rate = $request->hourly_rate;
        $entry->save();
        $role_entries = BatchInvitationLinkRole::where([
            'batch_invitation_link_id' => $entry->id
        ])->get();
        foreach ($role_entries as $rentry) {
            $i = array_search($rentry->role_id, $roles);
            if ($i === false) {
                $rentry->delete();
            } else {
                unset($roles[$i]);
            }
        }
        $roles = array_values($roles);
        foreach ($roles as $role) {
            BatchInvitationLinkRole::create([
                'batch_invitation_link_id' => $entry->id,
                'role_id' => $role
            ]);
        }
        if ($name === $original_name) {
            $this->createUserAction('Edited invite link "' . $name . '"');
        } else {
            $this->createUserAction('Edited invite link "' . $name . '" (previously named as "' . $original_name . '")');
        }
        $entry->refresh();
        return response()->json([
            'entry' => $entry,
            'success' => true
        ])->setStatusCode(201);
    }
    
    /**
     * Register a new worker.
     *
     * @param  \App\Modules\Company\Users\AddUser  $addUser
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function registerUser($addUser, $request)
    {
        // return response()->json([
        //     'error' => $request->getRealMethod()
        // ])->setStatusCode(201);
        $entry = BatchInvitationLink::where([
            'id' => $request->id,
            'token' => $request->token
        ])->first();
        $response = [];
        if (!isset($entry)) {
            $response['error'] = 'Invalid invite link.';
        } else if ($this->isExpired($entry)) {
            $response['error'] = 'Expired invite link.';
        } else if ($entry->locked) {
            $response['error'] = 'Invite link is currently locked.';
        }
        if (count($response) > 0) {
            $response['redirect'] = true;
            return response()->json($response)->setStatusCode(201);
        }
        $role_entries = BatchInvitationLinkRole::where([
            'batch_invitation_link_id' => $entry->id
        ])->get();
        $roles = [];
        $isWorker = $request->isWorker;
        foreach ($role_entries as $linkrole) {
            if (!$isWorker && $linkrole->role_id === 5) {
                return response()->json([
                    'error' => 'Please enter the missing required details.',
                    'isWorker' => true,
                    'redirect' => false
                ])->setStatusCode(201);
            }
            $roles[] = $linkrole->role_id;
        }
        if ($isWorker && !in_array(5, $roles)) {
            $isWorker = false;
        }
        // Increment count early on, we'll just rollback when there's an error (via onError)
        $entry->total_registrants += 1;
        if ($entry->total_registrants === $entry->max_registrants) {
            $entry->expired = true;
        }
        $entry->save();
        // https://stackoverflow.com/questions/28109179/getting-current-date-time-and-day-in-laravel
        // https://stackoverflow.com/questions/7067536/how-to-call-a-closure-that-is-a-class-variable
        // if ($isWorker && !$request->hasFile('resume')) {
        //     return response()->json([
        //         'error' => 'Please upload a resume.',
        //         'isWorker' => true,
        //         'redirect' => false
        //     ])->setStatusCode(201);
        // }
        /*
            For some reason, a closure (lambda/anonymous function) can't be merged with a request instance
            if the request contains a binary file (in this case, a resume). It works fine otherwise, despite
            the existing is_scalar check inside InputBag->set

            .\server\vendor\laravel\framework\src\Illuminate\Http\Request.php
            .\server\vendor\symfony\http-foundation\Request.php
            .\server\vendor\symfony\http-foundation\InputBag.php
        */
        $request->merge([
            "company_id" => $entry->company_id,
            "company_name" => Company::find($entry->company_id)->name,
            "date_hired" => Carbon::now()->toDateTimeString(),
            "hourly_rate" => $entry->rate,
            "isInviteLink" => true,
            "roles" => $roles
        ]);
        // $test_response = response()->json($response)->setStatusCode(201);
        // $content = $test_response->getData(true);
        // $content['jaycliff'] = 'gwapo';
        // $test_response->setData($content);
        // return $test_response;
        $response = $addUser->add($request, function () use ($entry) {
            $entry->refresh();
            $entry->total_registrants -= 1;
            if ($entry->expired) {
                $entry->expired = false;
            }
            $entry->save();
        });
        $content = $response->getData(true);
        if (isset($content['errors'])) {
            $content['error'] = implode(', ', $content['errors']);
            unset($content['errors']);
        }
        $content['isWorker'] = $isWorker;
        $response->setData($content);
        return $response;
    }

    /**
     * Toggle the lock status of the specified resource.
     *
     * @param  $id
     * @return \Illuminate\Http\Response
     */
    public function toggleLink($id)
    {
        $entry = BatchInvitationLink::where([
            'id' => $id,
            'company_id' => $this->getCurrentUser()->company_id
        ])->first();
        if ($entry) {
            $entry->locked = !$entry->locked;
            $entry->save();
            $this->createUserAction(($entry->locked ? 'Locked' : 'Unlocked') . ' invite link "' . $entry->name . '" [ID:' . $id . ']');
            return response()->json([
                'id' => $id,
                'name' => $entry->name,
                'success' => $entry->name . ' has been ' . ($entry->locked ? 'locked' : 'unlocked') . '.'
            ])->setStatusCode(201);
        }
        return response()->json([
            'error' => 'Invalid link id [' . $id . '].'
        ])->setStatusCode(201);
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  $id
     * @return \Illuminate\Http\Response
     */
    public function destroyLink($id)
    {
        $entry = BatchInvitationLink::where([
            'id' => $id,
            'company_id' => $this->getCurrentUser()->company_id
        ])->first();
        if ($entry) {
            $entry->delete();        
            $this->createUserAction('Deleted invite link "' . $entry->name . '" [ID:' . $id . ']');
            return response()->json([
                'id' => $id,
                'name' => $entry->name,
                'success' => $entry->name . ' has been deleted.'
            ])->setStatusCode(201);
        }
        return response()->json([
            'error' => 'Invalid link id [' . $id . '].'
        ])->setStatusCode(201);
    }
}