<?php

namespace App\Http\Controllers\api;

use Exception;
use App\Http\Requests\GroupRequest;
use App\Http\Controllers\Controller;
use App\Models\Application\Group;
use Illuminate\Support\Facades\DB;

class GroupController extends Controller
{
    protected $MESSAGES = [
        'INDEX' => 'WARNING.GROUP_INDEX',
        'STORE' => 'WARNING.GROUP_STORE',
        'UPDATE' => 'WARNING.GROUP_UPDATE',
        'DESTROY' => 'WARNING.GROUP_DESTROY',
        'DUPLICATED' => 'WARNING.GROUP_DUPLICATED',
        'STATUS' => 'WARNING.GROUP_STATUS',
        'VISIBLE' => 'WARNING.GROUP_VISIBLE',
        'LIST' => 'WARNING.GROUP_LIST',
        'SHOW' => 'WARNING.GROUP_SHOW',
        'GROUPPROMOTER' => 'WARNING.GROUP_PROMOTER_SHOW',
    ];

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

    public function index(GroupRequest $request)
    {
        try {
            $isSystemAdministrator = $this->hasSystemAdministrator($request->fk_office);
            $isAdministrator = $this->hasAppOwner($request->fk_office) || $this->hasSiteAdministrator($request->fk_office) || $this->hasSystemAdministrator($request->fk_office);

            $groups = Group::with(['zone', 'promoter' => function ($query) {
                $query->select(
                    'id',
                    'fk_position',
                    DB::raw("CONCAT(firstname, ' ', lastname, ' ', secondlastname) as fullname"),
                );
            }, 'user_creator', 'user_updater'])->where([
                ['fk_site', $request->fk_site],
                ['fk_office', $request->fk_office],
                ['is_active', !$this->hasSpecial($request->fk_office)]
            ])->when(!$isSystemAdministrator, function ($query) {
                $query->where('visible', true);
            })->when(!$isAdministrator, function ($query) {
                $query->whereIn('id', $this->getUserGroups());
            })->orderBy('id')->get();

            if (!$isSystemAdministrator) {
                $this->makeFieldHidden($groups, ['zone', 'promoter', 'visible', 'user_updater', 'user_creator']);
            }

            return $this->responseJson(null, $groups, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES['INDEX'], null, 501);
        }
    }

    public function store(GroupRequest $request)
    {
        try {
            $isDuplicated = Group::where([
                ['fk_site', $request->fk_site],
                ['fk_office', $request->fk_office],
                ['fk_zone', $request->fk_zone],
                ['groupname', mb_strtoupper(trim($request->groupname), $this->UTF_ENCODING)],
                ['is_active', true]
            ])->exists();

            if ($isDuplicated) {
                return $this->responseJson($this->MESSAGES['DUPLICATED'], null, 501);
            }

            Group::create([
                'fk_site' => $request->fk_site,
                'fk_office' => $request->fk_office,
                'fk_zone' => $request->fk_zone,
                'groupname' => mb_strtoupper(trim($request->groupname), $this->UTF_ENCODING),
                'fk_promoter' => $request->fk_promoter,
                'fk_usercreator' => $this->user()->id
            ]);

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

    public function update(GroupRequest $request)
    {
        try {
            $route = $request->route()->getName();
            $group = Group::findOrFail($request->id);

            $actions = [
                'group.update' => fn() => $this->updateGroup($request, $group),
                'group.changeStatus' => fn() => $this->updateBooleanStatus($request, $group),
                'group.changeVisibility' => fn() => $this->updateBooleanStatus($request, $group, 'visible'),
            ];

            if (array_key_exists($route, $actions)) {
                $actions[$route]();
            }

            return $this->responseJson(null, true, 201);
        } catch (Exception $ex) {
            $responses = [
                'group.update' => $this->MESSAGES['UPDATE'],
                'group.changeStatus' => $this->MESSAGES['STATUS'],
                'group.changeVisibility' => $this->MESSAGES['VISIBLE']
            ];

            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($responses[$route], null, 501);
        }
    }

    private function updateGroup(GroupRequest $request, Group $group)
    {
        $isDuplicated = Group::where([
            ['fk_site', $request->fk_site],
            ['fk_office', $request->fk_office],
            ['fk_zone', $request->fk_zone],
            ['groupname', mb_strtoupper(trim($request->groupname), $this->UTF_ENCODING)],
            ['is_active', true]
        ])->where('id', '!=', $group->id)->exists();

        if ($isDuplicated) {
            throw new Exception($this->MEESAGES['DUPLICATED']);
        }

        $group->update([
            'fk_zone' => $request->fk_zone,
            'groupname' => mb_strtoupper(trim($request->groupname), $this->UTF_ENCODING),
            'fk_promoter' => $request->fk_promoter,
            'fk_userupdater' => $this->user()->id
        ]);
    }

    public function destroy(GroupRequest $request)
    {
        try {
            $group = Group::findOrFail($request->id);

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

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

    public function list(GroupRequest $request)
    {
        try {
            $isSystemAdministrator = $this->hasSystemAdministrator($request->fk_office);

            $groups = Group::select('id', 'groupname', 'fk_zone')->where([
                ['fk_site', $request->fk_site],
                ['fk_office', $request->fk_office],
                ['available', true],
                ['is_active', true]
            ])->when(isset($request->fk_zones), function ($query) use ($request) {
                $query->whereIn('fk_zone', $request->fk_zones);
            })->when(isset($request->fk_executives), function ($query) use ($request) {
                $query->whereHas('loans', function ($loans) use ($request) {
                    $loans->whereIn('fk_executive', $request->fk_executives)->where([
                        ['is_active', true]
                    ]);
                });
            })->when(!$isSystemAdministrator, fn($query) => $query->where('visible', true))
                ->when(isset($request->fk_zones), fn($zone) => $zone->whereIn('fk_zone', $request->fk_zones))
                ->orderBy('groupname')->get();

            return $this->responseJson(null, $groups, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES['DESTROY'], null, 501);
        }
    }

    public function show(GroupRequest $request)
    {
        try {
            return $this->responseJson(null, true, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES['DESTROY'], null, 501);
        }
    }

    public function getPromoterGroup(GroupRequest $request)
    {
        try {
            $group = Group::findOrFail($request->id);

            $group->load(['promoter' => fn($query) => $query->select('id', DB::raw("CONCAT(firstname, ' ', lastname, ' ', secondlastname) as fullname"))]);
            $pomoter = $group->promoter;

            return $this->responseJson(null, $pomoter, 201);
        } catch (Exception $ex) {
            $this->generateLog($this->CHANNEL, $ex);
            return $this->responseJson($this->MESSAGES['GROUPPROMOTER'], null, 501);
        }
    }
}
