<?php

namespace App\Services\Api\Scim;

use App\User;
use App\Models\Role;
use App\Models\ApiCredential;
use App\Repositories\Scim\UserRepository;

class ScimUserService
{
    protected $userRepository;

    public function __construct(UserRepository $userRepository)
    {
        $this->userRepository = $userRepository;
    }

    public function create(array $scimData)
    {
        try {
            return $this->processUserData($scimData, true);
        } catch (\Exception $e) {
            \Log::error('Error while creating SCIM user: ' . $e->getMessage());
            return $this->errorResponse(500, 'An error occurred while processing the request.');
        }
    }

    public function update(array $scimData)
    {
        try {
            return $this->processUserData($scimData, false);
        } catch (\Exception $e) {
            \Log::error('Error while updating SCIM user: ' . $e->getMessage());
            return $this->errorResponse(500, 'An error occurred while processing the request.');
        }
    }

    private function processUserData(array $scimData, $isNewUser)
    {
        if (!isset($scimData['userName']) || ($isNewUser && !isset($scimData['user_role']))) {
            return $this->errorResponse(400, 'userName and Role are required fields.');
        }

        $user = User::where('email', $scimData['userName'])->first();

        if (!$user) {
            if ($isNewUser) {
                $user = $this->createUser($scimData);
            } else {
                return $this->errorResponse(404, 'User not found.');
            }
        } else {
            $this->updateUser($user, $scimData);
            $user = $user->fresh();
        }

        $this->updateUserRoles($user, $scimData);

        return [
            'id' => $user->id,
            'userName' => $scimData['userName'],
            'meta' => [
                'location' => route('api.scim.user.detail', ['id' => $user->id])
            ],
            'active' => true,
            'schemas' => ['urn:ietf:params:scim:schemas:core:2.0:User']
        ];
    }

    protected function updateUserRoles(User $user, array $scimData)
    {
        if ($scimData['active'] === '1' && isset($scimData['user_role'])) {
            $userRole = Role::where('name', $scimData['user_role'])->first();

            if ($userRole) {
                $data['user_type_id'] = $userRole->id;
                $this->syncRoles($user, $userRole->name);
            }
        } else {
            $defaultRole = Role::find(ApiCredential::whereSlug('okta_scim')->value('role_id'))
            ?? Role::where('name', 'Employee')->first();

            $data['user_type_id'] = $defaultRole->id;
            $this->syncRoles($user, $defaultRole->name);
        }

        $user->update($data);
    }

    protected function syncRoles(User $user, string $roleName)
    {
        if ($user->userType) {
            $user->removeRole($user->userType->name);
        }

        $user->assignRole($roleName);
    }

    protected function errorResponse($status, $detail)
    {
        return [
            'schemas' => ['urn:ietf:params:scim:api:messages:2.0:Error'],
            'status' => $status,
            'detail' => $detail
        ];
    }

    private function createUser($scimData)
    {
        $userData = $this->generateUserData($scimData);

        return User::create($userData);
    }

    private function updateUser(User $user, array $scimData)
    {
        $userData = $this->generateUserData($scimData);

        return $user->update($userData);
    }

    private function generateUserData($scimData)
    {
        $scimData['active'] = $scimData['active'] ? '1' : '0';

        if ($scimData['active'] === '1') {
            $headerMap = config('scim');

            $scimData['address'] = $scimData['addresses'][0] ?? [];

            $userData = [];

            $fieldMappings = [
                'coreDataMapping' => [],
                'nameDataMapping' => ['name'],
                'addressDataMapping' => ['address'],
                'enterpriseDataMapping' => ['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User'],
                'managerDataMapping' => ['urn:ietf:params:scim:schemas:extension:enterprise:2.0:User', 'manager'],
            ];
        
            foreach ($fieldMappings as $fieldGroup => $fieldPrefixes) {
                foreach ($headerMap[$fieldGroup] as $dbField => $fileHeader) {
                    $fieldValue = $scimData;
                    foreach ($fieldPrefixes as $prefix) {
                        $fieldValue = $fieldValue[$prefix] ?? [];
                    }
                    $fieldValue = $fieldValue[$fileHeader] ?? '';
        
                    $userData[$dbField] = "";
        
                    if ($fieldValue !== "") {
                        $result = $this->getRelationalValues($dbField, $fieldValue);
                        if ($result) {
                            $userData[$dbField] = $result;
                        }
                    }
                }
            }

            $userData['okta_synced'] = 1;
            $userData['status'] = 1;
            $userData['terminated_date'] = null;
        } else {
            $userData['okta_synced'] = 0;
            $userData['status'] = 0;
            $userData['terminated_date'] = date('Y-m-d');
        }

        return $userData;
    }

    private function getRelationalValues($dbField, $fieldValue)
    {
        $value = $fieldValue;

        switch ($dbField) {
            case 'position_id':
                $value = $this->userRepository->getPositionId($fieldValue);
                break;

            case 'department_id':
                $value = $this->userRepository->getDepartmentId($fieldValue);
                break;

            case 'employee_type_id':
                $value = $this->userRepository->getEmployeeTypeId($fieldValue);
                break;

            case 'hire_date':
                $value = parse_date_okta_hire_date($fieldValue);
                break;

            case 'manager_id':
                $value = $this->userRepository->getManagerId($fieldValue);
                break;

            case 'division_id':
                $value = $this->userRepository->getDivisionId($fieldValue);
                break;

            case 'state_id':
                $value = $this->userRepository->getStateId($fieldValue);
                break;

            case 'country_id':
                $value = $this->userRepository->getCountryId($fieldValue);
                break;
        }

        return $value ?: null;
    }
}

