<?php

namespace App\Http\Controllers\api;

use Exception;
use App\Http\Requests\RoleRequest;
use App\Models\Administration\Role;
use App\Models\Administration\Site;
use App\Http\Controllers\Controller;
use App\Models\Administration\Office;
use Illuminate\Database\Eloquent\ModelNotFoundException;

class RoleController extends Controller
{
    protected $MESSAGES = [
        'ROLE_INDEX' => 'WARNING.ROLE_INDEX',
        'ROLE_DUPLICATED' => 'WARNING.ROLE_DUPLICATED',
        'ROLE_STORE' => 'WARNING.ROLE_STORE',
        'ROLE_UPDATE' => 'WARNING.ROLE_UPDATE',
        'ROLE_NOT_FOUND' => 'ROLE.NOT_FOUND',
        'ROLE_DESTROY' => 'WARNING.ROLE_DESTROY',
        'ROLE_NOT_EDITABLE' => 'WARNING.ROLE_NOT_EDITABLE',
        'ROLE_SPECIAL' => 'WARNING.ROLE_SPECIAL',
        'ROLE_EDITABLE' => 'WARNING.ROLE_EDITABLE',
        'ROLE_AVAILABLE' => 'WARNING.ROLE_AVAILABLE',
        'ROLE_VISIBLE' => 'WARNING.ROLE_VISIBLE',
        'OFFICE_ROLES' => 'WARNING.OFFICE_ROLES',
    ];

    public function __construct()
    {
        $this->CHANNEL = 'role_errors';
    }

    public function index(RoleRequest $request)
    {
        try {
            $isSystemAdministrator = $this->hasSystemAdministrator();

            $role_query = Role::with(['user_creator', 'user_updater'])->whereHas('site', function ($site_filter) use ($request) {
                $site_filter->where('id', $request->fk_site);
            })->where([
                ['fk_site', $request->fk_site],
                ['is_active', !$this->hasSpecial($request->fk_office)]
            ])->when($isSystemAdministrator, function ($query) {
                $query->orWHere([
                    ['fk_site', null]
                ]);
            })->withCount('users');

            $roles = $role_query->orderBy('id', 'ASC')->get();

            if (!$isSystemAdministrator) {
                $this->makeFieldHidden($roles, ['visible', 'editable', 'special', 'user_creator', 'user_updater']);
            }

            return $this->responseJson(null, $roles, 200);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return response()->json($this->MESSAGES['ROLE_INDEX'], 501);
        }
    }

    public function store(RoleRequest $request)
    {
        try {
            if (Role::where([
                ['fk_site', $request->fk_site],
                ['rolename', trim(mb_strtoupper($request->rolename, $this->UTF_ENCODING))],
                ['is_active', true]
            ])->exists()) {
                return response()->json($this->MESSAGES['ROLE_DUPLICATED'], 501);
            }

            Role::create([
                'fk_site' => $request->fk_site,
                'rolename' => trim(mb_strtoupper($request->rolename, $this->UTF_ENCODING)),
                'fk_usercreator' => $this->user()->id
            ]);

            return $this->responseJson(null, true, 200);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            $this->responseJson($this->MESSAGES['ROLE_STORE'], null, 501);
        }
    }

    public function update(RoleRequest $request)
    {
        try {
            $role = Role::findOrFail($request->id);

            if (Role::where('id', '<>', $role->id)->where(function ($query) use ($request) {
                $query->where([
                    ['rolename', trim(mb_strtoupper($request->rolename, $this->UTF_ENCODING))],
                    ['fk_site', $request->fk_site],
                    ['is_active', true]
                ]);
            })->exists()) {
                $this->responseJson($this->MESSAGES['ROLE_DUPLICATED'], null, 501);
            }

            if (!$role->editable) {
                $this->responseJson($this->MESSAGES['ROLE_NOT_EDITABLE'], null, 501);
            }

            $role->update([
                'rolename' => trim(mb_strtoupper($request->rolename, $this->UTF_ENCODING)),
                'fk_userupdater' => $this->user()->id
            ]);

            return $this->responseJson(null, true, 200);
        } catch (ModelNotFoundException $mex) {
            $this->generateLog($this->CHANNEL, $mex);
            $this->responseJson($this->MESSAGES['NOT_FOUND'], null, 501);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            $this->responseJson($this->MESSAGES['ROLE_UPDATE'], null, 501);
        }
    }

    public function destroy(RoleRequest $request)
    {
        try {
            $role = Role::findOrFail($request->id);

            $role->update([
                'is_active' => $this->hasSpecial($request->fk_office),
                'fk_userupdater' => $this->user()->id
            ]);

            return response()->json(true, 200);
        } catch (ModelNotFoundException $mex) {
            $this->generateLog($this->CHANNEL, $mex);
            $this->responseJson($this->MESSAGES['NOT_FOUND'], null, 501);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            $this->responseJson($this->MESSAGES['ROLE_DESTROY'], null, 501);
        }
    }

    public function changeSpeciality(RoleRequest $request)
    {
        try {
            $role = Role::findOrFail($request->id);

            if (!$role->editable) {
                $this->responseJson($this->MESSAGES['ROLE_NOT_EDITABLE'], null, 501);
            }

            $role->update([
                'special' => $request->special,
                'fk_userupdater' => $this->user()->id
            ]);

            return response()->json(true, 200);
        } catch (ModelNotFoundException $mex) {
            $this->generateLog($this->CHANNEL, $mex);
            $this->responseJson($this->MESSAGES['NOT_FOUND'], null, 501);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            $this->responseJson($this->MESSAGES['ROLE_SPECIAL'], null, 501);
        }
    }

    public function chageEditability(RoleRequest $request)
    {
        try {
            $role = Role::findOrFail($request->id);

            $role->editable = $request->editable;
            $role->fk_userupdater = $this->user()->id;
            $role->update();

            return response()->json(true, 200);
        } catch (ModelNotFoundException $mex) {
            $this->generateLog($this->CHANNEL, $mex);
            $this->responseJson($this->MESSAGES['NOT_FOUND'], null, 501);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            $this->responseJson($this->MESSAGES['ROLE_EDITABLE'], null, 501);
        }
    }

    public function changeStatus(RoleRequest $request)
    {
        try {
            $role = Role::findOrFail($request->id);

            $role->update([
                'available' => $request->available,
                'fk_userupdater' => $this->user()->id
            ]);

            return response()->json(true, 200);
        } catch (ModelNotFoundException $mex) {
            $this->generateLog($this->CHANNEL, $mex);
            $this->responseJson($this->MESSAGES['NOT_FOUND'], null, 501);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            $this->responseJson($this->MESSAGES['ROLE_AVAILABLE'], null, 501);
        }
    }

    public function changeVisibility(RoleRequest $request)
    {
        try {
            $role = Role::findOrFail($request->id);

            $role->update([
                'visible' => $request->visible,
                'fk_userupdater' => $this->user()->id
            ]);

            return response()->json(true, 200);
        } catch (ModelNotFoundException $mex) {
            $this->generateLog($this->CHANNEL, $mex);
            $this->responseJson($this->MESSAGES['NOT_FOUND'], null, 501);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            $this->responseJson($this->MESSAGES['ROLE_VISIBLE'], null, 501);
        }
    }

    public function getOfficeRoles(RoleRequest $request)
    {
        try {
            $office = Office::findOrFail($request->fk_office);
            $site = Site::findOrFail($office->fk_site)->load(['roles' => function ($query) use ($request) {
                $query->select('id', 'rolename', 'fk_site')->where([
                    ['visible', !$this->hasSpecial($request->fk_office)],
                    ['available', true]
                ]);
            }]);

            return response()->json($site->roles, 200);
        } catch (ModelNotFoundException $model) {
            $this->generateLog($this->CHANNEL, $model);
            $this->responseJson($this->MESSAGES['NOT_FOUND'], null, 501);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            $this->responseJson($this->MESSAGES['OFFICE_ROLES'], null, 501);
        }
    }
}
