<?php

class PancekKppbcUserController extends BaseController
{
    public function listVerifiaktorByKppbcId($kppbcid)
    {
        $this->checkOneOfScopes(['admin.view', 'pancek.admin', 'pancek.monitoring', 'pancek.kppbc.admin', 'pancek.kppbc.assignor']);
        $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
        if($this->hasScopes(['pancek.monitoring']) && !$this->hasScopes(['pancek.admin']) && !$this->hasScopes(['admin.view'])) {
            if($this->shared->user->id_instansi != $idInstansiBeaCukai) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Hanya bisa diakses oleh Observer Pancek Bea Cukai");
            }
        }
        if($this->hasScopes(['pancek.kppbc.assignor']) && !$this->hasScopes(['pancek.admin']) && !$this->hasScopes(['admin.view'])) {
            if($this->shared->user->id_instansi != $idInstansiBeaCukai) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Hanya bisa diakses oleh Assignor Pancek Bea Cukai");
            }
        }

        $params = $this->request->get();

        $params['um_id'] = array_key_exists('um_id', $params) ? $params['um_id'] : null;
        $params['keyword'] = array_key_exists('keyword', $params) ? $params['keyword'] : '';
        $params['limit'] = $this->getDefaultLimit($params);
        $params['offset'] = array_key_exists('offset', $params) ? $params['offset'] : 0;
        $params['order_by'] = array_key_exists('order_by', $params) ? strtolower($params['order_by']) : 'id';
        $params['order_direction'] = array_key_exists('order_direction', $params) ? strtolower($params['order_direction']) : 'asc';

        $allowed_order = [
            'id',
            'nama_user',
            'created_at',
            'updated_at'
        ];
        $allowed_order_direction = ['asc', 'desc'];
        $bind_order = [
            'id' => 'PancekKppbcUser.id',
            'nama_user' => 'UmUser.nama',
            'created_at' => 'PancekKppbcUser.created_at',
            'updated_at' => 'PancekKppbcUser.updated_at'
        ];

        $validatedParams = $this->validate(
            $params,
            [
                'um_id' => ['validators' => ['digit']],
                'keyword' => ['validators' => []],
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'order_by' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order
                ]]],
                'order_direction' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order_direction
                ]]]
            ]
        );

        $conditions = [];
        $conditionsCount = [];
        $bind = [];

        $conditions[] = 'PancekKppbcUser.kppbc_id = :kppbc_id:';
        $conditionsCount[] = 'pku.kppbc_id  = :kppbc_id';
        $bind['kppbc_id'] = $kppbcid;

        $conditions[] = 'PancekKppbcUser.user_type = :user_type:';
        $conditionsCount[] = 'pku.user_type  = :user_type';
        $bind['user_type'] = PancekKppbcUser::USER_TYPE_VERIFIATOR;

        $conditions[] = 'PancekKppbcUser.deleted_at IS NULL';
        $conditionsCount[] = 'pku.deleted_at IS NULL';

        $conditions[] = '(UmUser.nama ILIKE :keyword: OR UmUser.um_user_name ILIKE :keyword:)';
        $conditionsCount[] = '(uu.nama ILIKE :keyword OR uu.um_user_name ILIKE :keyword)';
        $bind['keyword'] = '%' . $validatedParams['keyword'] . '%';

        if($validatedParams['um_id'] != null) {
            $conditions[] = 'PancekKppbcUser.um_id = :um_id:';
            $conditionsCount[] = 'um_id = :um_id';
            $bind['um_id'] = $validatedParams['um_id'];
        }

        $where = implode(' AND ', $conditions);
        $whereCount = 'WHERE ' . implode(' AND ', $conditionsCount);

        $result = PancekKppbcUser::query()
            ->columns("
                PancekKppbcUser.id,
                PancekKppbcUser.kppbc_id,
                PancekKppbcUser.um_id,
                PancekKppbcUser.created_at,
                PancekKppbcUser.updated_at,
                PancekKppbcUser.deleted_at,
                UmUser.um_user_name,
                UmUser.nama AS nama_user
            ")
            ->leftJoin('UmUser', 'PancekKppbcUser.um_id = UmUser.um_id')
            ->where($where)
            ->bind($bind)
            ->orderBy($bind_order[$params['order_by']] . " " . $params['order_direction'])
            ->limit($params['limit'], $params['offset'])
            ->execute()
            ->toArray();

        $total = $this->rawQuery("
            SELECT count(*) as total FROM jaga.pancek_kppbc_user pku
            LEFT JOIN jaga.um_user uu ON pku.um_id = uu.um_id 
            $whereCount
        ",
            $bind
        )[0]['total'];

        return new PaginatedResponse($result, $total, $params['limit'], $params['offset']);
    }

    public function listAssignorByKppbcId($kppbcid)
    {
        $this->checkOneOfScopes(['admin.view', 'pancek.admin', 'pancek.monitoring', 'pancek.kppbc.admin']);
        if($this->hasScopes(['pancek.monitoring']) && !$this->hasScopes(['pancek.admin']) && !$this->hasScopes(['admin.view'])) {
            $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
            if($this->shared->user->id_instansi != $idInstansiBeaCukai) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Hanya bisa diakses oleh Observer Pancek Bea Cukai");
            }
        }

        $params = $this->request->get();

        $params['um_id'] = array_key_exists('um_id', $params) ? $params['um_id'] : null;
        $params['keyword'] = array_key_exists('keyword', $params) ? $params['keyword'] : '';
        $params['limit'] = $this->getDefaultLimit($params);
        $params['offset'] = array_key_exists('offset', $params) ? $params['offset'] : 0;
        $params['order_by'] = array_key_exists('order_by', $params) ? strtolower($params['order_by']) : 'id';
        $params['order_direction'] = array_key_exists('order_direction', $params) ? strtolower($params['order_direction']) : 'asc';

        $allowed_order = [
            'id',
            'nama_user',
            'created_at',
            'updated_at'
        ];
        $allowed_order_direction = ['asc', 'desc'];
        $bind_order = [
            'id' => 'PancekKppbcUser.id',
            'nama_user' => 'UmUser.nama',
            'created_at' => 'PancekKppbcUser.created_at',
            'updated_at' => 'PancekKppbcUser.updated_at'
        ];

        $validatedParams = $this->validate(
            $params,
            [
                'um_id' => ['validators' => ['digit']],
                'keyword' => ['validators' => []],
                'limit' => ['validators' => ['digit']],
                'offset' => ['validators' => ['digit']],
                'order_by' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order
                ]]],
                'order_direction' => ['validators' =>  [[
                    'name' => 'inclusion_in',
                    'params' => $allowed_order_direction
                ]]]
            ]
        );

        $conditions = [];
        $conditionsCount = [];
        $bind = [];

        $conditions[] = 'PancekKppbcUser.kppbc_id = :kppbc_id:';
        $conditionsCount[] = 'pku.kppbc_id  = :kppbc_id';
        $bind['kppbc_id'] = $kppbcid;

        $conditions[] = 'PancekKppbcUser.user_type = :user_type:';
        $conditionsCount[] = 'pku.user_type  = :user_type';
        $bind['user_type'] = PancekKppbcUser::USER_TYPE_ASSIGNOR;

        $conditions[] = 'PancekKppbcUser.deleted_at IS NULL';
        $conditionsCount[] = 'pku.deleted_at IS NULL';

        $conditions[] = '(UmUser.nama ILIKE :keyword: OR UmUser.um_user_name ILIKE :keyword:)';
        $conditionsCount[] = '(uu.nama ILIKE :keyword OR uu.um_user_name ILIKE :keyword)';
        $bind['keyword'] = '%' . $validatedParams['keyword'] . '%';

        if($validatedParams['um_id'] != null) {
            $conditions[] = 'PancekKppbcUser.um_id = :um_id:';
            $conditionsCount[] = 'um_id = :um_id';
            $bind['um_id'] = $validatedParams['um_id'];
        }

        $where = implode(' AND ', $conditions);
        $whereCount = 'WHERE ' . implode(' AND ', $conditionsCount);

        $result = PancekKppbcUser::query()
            ->columns("
                PancekKppbcUser.id,
                PancekKppbcUser.kppbc_id,
                PancekKppbcUser.um_id,
                PancekKppbcUser.created_at,
                PancekKppbcUser.updated_at,
                PancekKppbcUser.deleted_at,
                UmUser.um_user_name,
                UmUser.nama AS nama_user
            ")
            ->leftJoin('UmUser', 'PancekKppbcUser.um_id = UmUser.um_id')
            ->where($where)
            ->bind($bind)
            ->orderBy($bind_order[$params['order_by']] . " " . $params['order_direction'])
            ->limit($params['limit'], $params['offset'])
            ->execute()
            ->toArray();

        $total = $this->rawQuery("
            SELECT count(*) as total FROM jaga.pancek_kppbc_user pku
            LEFT JOIN jaga.um_user uu ON pku.um_id = uu.um_id 
            $whereCount
        ",
            $bind
        )[0]['total'];

        return new PaginatedResponse($result, $total, $params['limit'], $params['offset']);
    }

    public function detail($id) {
        $this->checkOneOfScopes(['admin.view', 'pancek.admin', 'pancek.monitoring', 'pancek.kppbc.admin']);
        if($this->hasScopes(['pancek.monitoring']) && !$this->hasScopes(['pancek.admin']) && !$this->hasScopes(['admin.view'])) {
            $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
            if($this->shared->user->id_instansi != $idInstansiBeaCukai) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Hanya bisa diakses oleh Observer Pancek Bea Cukai");
            }
        }

        $data = PancekKppbcUser::findFirst($id);
        if(!$data) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Data KPPBC User tidak ditemukan");
        }

        $dataUmUser = $data->umUser->baseToArray();
        $dataKppbc = $data->kppbc->toArray();

        $data = $data->toArray();
        $data['um_user'] = $dataUmUser;
        $data['kppbc'] = $dataKppbc;

        return new SimpleResponse($data);
    }

    public function assignVerifiaktorUsers($kppbcid) {
        $this->checkOneOfScopes(['admin.view', 'pancek.admin', 'pancek.monitoring', 'pancek.kppbc.admin']);
        if($this->hasScopes(['pancek.monitoring']) && !$this->hasScopes(['pancek.admin']) && !$this->hasScopes(['admin.view'])) {
            $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
            if($this->shared->user->id_instansi != $idInstansiBeaCukai) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Hanya bisa diakses oleh Observer Pancek Bea Cukai");
            }
        }

        $payload = $this->request->getJsonRawBody(true);
        $payload = $this->validate(
            $payload,
            [
                'um_ids' => ['validators' => ['required']]
            ]
        );

        try {
            $arrUmIds = json_decode($payload['um_ids']);

            $this->db->begin();

            $countAdded = 0;
            $failedToAddUmIds = [];
            if($arrUmIds > 0) {
                $instansiBeaCukai = Instansi::findFirstByNama(Instansi::DITJEN_BEA_CUKAI);
                if(!$instansiBeaCukai) {
                    throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Instansi Ditjen Bea Cukai Tidak Ditemukan");
                }
                $idInstansiBeaCukai = $instansiBeaCukai->id;

                $users = [];
                if(!empty($arrUmIds)) {
                    $users = $this->getUsers($arrUmIds);
                }

                $dataExisting = PancekKppbcUser::find([
                    'conditions' => 'kppbc_id = :kppbc_id: AND user_type = :user_type:',
                    'bind' => [
                        'kppbc_id' => $kppbcid,
                        'user_type' => PancekKppbcUser::USER_TYPE_VERIFIATOR,
                    ]
                ]);

                $keyValDataExisting = ArrayHelper::asKeyValue($dataExisting->toArray(), 'um_id');

                foreach ($users as $user) {
                    $umId = $user['um_id'];

                    if(strpos($user['role_names'], Roles::ROLE_PANCEK_VERIFIKATOR_KPPBC) === false) {
                        $failedToAddUmIds[] = [
                            'um_id' => $umId,
                            'um_user_name' => $user['um_user_name'],
                            'nama' => $user['nama'],
                            'message' => 'User tidak memiliki role: "' . Roles::ROLE_PANCEK_VERIFIKATOR_KPPBC . '"'
                        ];
                    }
                    else if($user['id_instansi'] != $idInstansiBeaCukai) {
                        $failedToAddUmIds[] = [
                            'um_id' => $umId,
                            'um_user_name' => $user['um_user_name'],
                            'nama' => $user['nama'],
                            'message' => 'Instansi User bukan: "' . Instansi::DITJEN_BEA_CUKAI . '"'
                        ];
                    }
                    else {
                        if (array_key_exists($umId, $keyValDataExisting)) {
                            if ($keyValDataExisting[$umId]['deleted_at'] != null) {
                                foreach ($dataExisting as $itemExisting) {
                                    if ($itemExisting->um_id == $umId) {
                                        $itemExisting->deleted_at = null;
                                        $itemExisting->user_type = PancekKppbcUser::USER_TYPE_VERIFIATOR;
                                        $itemExisting->save();
                                        $countAdded++;
                                    }
                                }
                            }
                        } else {
                            $newItem = new PancekKppbcUser();
                            $newItem->kppbc_id = $kppbcid;
                            $newItem->um_id = $umId;
                            $newItem->user_type = PancekKppbcUser::USER_TYPE_VERIFIATOR;
                            $newItem->save();
                            $countAdded++;
                        }
                    }
                }
            }

            $deleteUmIds = [];
            $now = DateHelper::getNowTimezoned();
            foreach ($dataExisting as $item) {
                if($item->deleted_at == null) {
                    if(!in_array($item->um_id, $arrUmIds)) {
                        $deleteUmIds[] = $item->um_id;
                        $item->deleted_at = $now;
                        $item->save();
                    }
                }
            }
            $countDeleted = count($deleteUmIds);

            $this->db->commit();

            return new SimpleResponse([
                'added_users_count' => $countAdded,
                'deleted_users_count' => $countDeleted,
                'failed_um_ids' => $failedToAddUmIds
            ]);

        } catch (\Exception $e) {
            $this->db->rollback();
            throw $e;
        }
    }

    public function assignAssignorUsers($kppbcid) {
        $this->checkOneOfScopes(['admin.view', 'pancek.admin', 'pancek.monitoring', 'pancek.kppbc.admin']);
        if($this->hasScopes(['pancek.monitoring']) && !$this->hasScopes(['pancek.admin']) && !$this->hasScopes(['admin.view'])) {
            $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
            if($this->shared->user->id_instansi != $idInstansiBeaCukai) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Hanya bisa diakses oleh Observer Pancek Bea Cukai");
            }
        }

        $payload = $this->request->getJsonRawBody(true);
        $payload = $this->validate(
            $payload,
            [
                'um_ids' => ['validators' => ['required']]
            ]
        );

        try {
            $arrUmIds = json_decode($payload['um_ids']);

            $this->db->begin();

            $countAdded = 0;
            $failedToAddUmIds = [];
            if($arrUmIds > 0) {
                $instansiBeaCukai = Instansi::findFirstByNama(Instansi::DITJEN_BEA_CUKAI);
                if(!$instansiBeaCukai) {
                    throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Instansi Ditjen Bea Cukai Tidak Ditemukan");
                }
                $idInstansiBeaCukai = $instansiBeaCukai->id;

                $users = [];
                if(!empty($arrUmIds)) {
                    $users = $this->getUsers($arrUmIds);
                }

                $dataExisting = PancekKppbcUser::find([
                    'conditions' => 'kppbc_id = :kppbc_id: AND user_type = :user_type:',
                    'bind' => [
                        'kppbc_id' => $kppbcid,
                        'user_type' => PancekKppbcUser::USER_TYPE_ASSIGNOR,
                    ]
                ]);

                $keyValDataExisting = ArrayHelper::asKeyValue($dataExisting->toArray(), 'um_id');

                $newAssignorIds = [];
                foreach ($users as $user) {
                    $umId = $user['um_id'];

                    if(strpos($user['role_names'], Roles::ROLE_PANCEK_ASSIGNOR_KPPBC) === false) {
                        $failedToAddUmIds[] = [
                            'um_id' => $umId,
                            'um_user_name' => $user['um_user_name'],
                            'nama' => $user['nama'],
                            'message' => 'User tidak memiliki role: "' . Roles::ROLE_PANCEK_ASSIGNOR_KPPBC . '"'
                        ];
                    }
                    else if($user['id_instansi'] != $idInstansiBeaCukai) {
                        $failedToAddUmIds[] = [
                            'um_id' => $umId,
                            'um_user_name' => $user['um_user_name'],
                            'nama' => $user['nama'],
                            'message' => 'Instansi User bukan: "' . Instansi::DITJEN_BEA_CUKAI . '"'
                        ];
                    }
                    else {
                        if (array_key_exists($umId, $keyValDataExisting)) {
                            if ($keyValDataExisting[$umId]['deleted_at'] != null) {
                                foreach ($dataExisting as $itemExisting) {
                                    if ($itemExisting->um_id == $umId) {
                                        $itemExisting->deleted_at = null;
                                        $itemExisting->user_type = PancekKppbcUser::USER_TYPE_ASSIGNOR;
                                        $itemExisting->save();
                                        $countAdded++;
                                    }
                                }
                            }
                        } else {
                            $newItem = new PancekKppbcUser();
                            $newItem->kppbc_id = $kppbcid;
                            $newItem->um_id = $umId;
                            $newItem->user_type = PancekKppbcUser::USER_TYPE_ASSIGNOR;
                            $newItem->save();
                            $countAdded++;
                            $newAssignorIds[] = $umId;
                        }
                    }
                }
            }

            $deleteUmIds = [];
            $now = DateHelper::getNowTimezoned();
            foreach ($dataExisting as $item) {
                if($item->deleted_at == null) {
                    if(!in_array($item->um_id, $arrUmIds)) {
                        $deleteUmIds[] = $item->um_id;
                        $item->deleted_at = $now;
                        $item->save();
                    }
                }
            }
            $countDeleted = count($deleteUmIds);

            $pancekService = new PancekService($this);
            foreach ($newAssignorIds as $newAssignorId) {
                $pancekService->handleAssignorNotification($newAssignorId);
            }

            $this->db->commit();

            return new SimpleResponse([
                'added_users_count' => $countAdded,
                'deleted_users_count' => $countDeleted,
                'failed_um_ids' => $failedToAddUmIds
            ]);

        } catch (\Exception $e) {
            $this->db->rollback();
            throw $e;
        }
    }
    
    private function getUsers($arrUmIds)
    {
        $q = "
            SELECT 
            	uu.um_id, 
            	uu.um_user_name, 
            	uu.nama, 
            	uu.id_instansi,
            	STRING_AGG(r.name, ',') AS role_names
            FROM um_user uu 
            LEFT JOIN users_roles ur ON uu.um_id = ur.user_id
            LEFT JOIN roles r ON r.id = ur.role_id
            WHERE uu.um_id IN (". implode(',', $arrUmIds) .")
            GROUP BY 
            	uu.um_id, 
            	uu.um_user_name, 
            	uu.nama, 
            	uu.id_instansi;
	    ";

        $users = $this->rawQuery($q);

        return $users;
    }
    
    public function delete($id) {
        $this->checkOneOfScopes(['admin.view', 'pancek.admin', 'pancek.monitoring', 'pancek.kppbc.admin']);
        if($this->hasScopes(['pancek.monitoring']) && !$this->hasScopes(['pancek.admin']) && !$this->hasScopes(['admin.view'])) {
            $idInstansiBeaCukai = Instansi::getIdInstansiDitjenBeaCukai();
            if($this->shared->user->id_instansi != $idInstansiBeaCukai) {
                throw new CustomErrorException(BaseResponse::INVALID_AUTHENTICATION_AUTHORIZATION, "Hanya bisa diakses oleh Observer Pancek Bea Cukai");
            }
        }

        $kppbcUser = PancekKppbcUser::findFirst($id);
        if(!$kppbcUser) {
            throw new CustomErrorException(BaseResponse::DATA_NOT_FOUND, "Data KPPBC User tidak ditemukan");
        }

        $now = DateHelper::getNowTimezoned();
        $kppbcUser->deleted_at = $now;
        $kppbcUser->save();
        return new SimpleResponse($id);
    }
}
