<?php

namespace Modules\Auth\Services;

use App\Models\User;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Hash;
use Modules\Auth\DTO\UserListFilter;
use Modules\Auth\Models\Role;
use Modules\Auth\Repositories\UserRepository;
use Modules\Core\Enums\RolesEnum;
use Modules\Core\Enums\StatusEnum;
use Modules\Core\Exceptions\ApiValidationException;
use Modules\Employee\Repositories\EmployeeRepository;

class UserService
{
    protected $roleGuardService;
    protected $roleAssignmentService;
    protected $employeeRepo;
    protected $userRepo;

    public function __construct(
        RoleGuardService $roleGuardService,
        RoleAssignmentService $roleAssignmentService,
        EmployeeRepository $employeeRepo,
        UserRepository $userRepo
    ) {
        $this->roleGuardService = $roleGuardService;
        $this->roleAssignmentService = $roleAssignmentService;
        $this->employeeRepo = $employeeRepo;
        $this->userRepo = $userRepo;
    }

    public function list(UserListFilter $filters)
    {
        $user = authUser();

        $includeSuperAdmin = $user->hasRole(RolesEnum::SUPER_ADMIN->value);

        $columns = [
            'id',
            'userId',
            'name',
            'phone',
            'email',
            'image',
            'status'
        ];

        return $this->userRepo->getUsers($filters, $includeSuperAdmin, $columns);
    }

    public function store(array $data): User
    {
        return DB::transaction(function () use ($data) {
            $roleForId = RolesEnum::ADMIN->value;
            $roles = $data['roles'];
            $employeeId = $data['employee_id'] ?? null;

            unset($data['roles'], $data['employee_id']);

            $user = $this->userRepo->createUser($data);

            if ($employeeId) {
                $roleForId = RolesEnum::EMPLOYEES->value;
                $employee = $this->employeeRepo->find($employeeId);

                if ($employee->hasUserAccount()) {
                    throw ApiValidationException::message(
                        'Employee already linked with another user.'
                    );
                }

                $this->employeeRepo->attachUserToEmployee($employee, $user->id);
            }

            $this->roleGuardService->validate($user, $roles);
            $this->roleAssignmentService->sync($user, $roles);
            $this->employeeRepo->updateUserId($user, $roleForId);

            return $user->fresh([
                'roles:id,name,title',
                'employee:id,user_id,emp_number,name'
            ]);
        });
    }



    public function find(int $id): User
    {
        return $this->userRepo->find($id);
    }

    public function update(User $user, array $data): User
    {
        return DB::transaction(function () use ($user, $data) {
            $employeeId = $data['employee_id'] ?? null;

            unset($data['employee_id']);

            $this->userRepo->updateUser($user, $data);

            if (!$employeeId) {
                return $user->fresh([
                    'roles:id,name,title',
                    'employee:id,user_id,emp_number,name'
                ]);
            }

            $user->loadMissing('employee');
            $employee = $this->employeeRepo->find($employeeId);

            if ($user->isEmployeeUser() && $user->employee->id !== $employeeId) {
                ApiValidationException::message(
                    'User is already linked to an employee.'
                );
            }

            if ($employee->hasUserAccount() && $employee->user_id !== $user->id) {
                ApiValidationException::message(
                    'Selected employee is already linked to another user.'
                );
            }

            if (!$employee->user_id) {
                $employee->update([
                    'user_id' => $user->id,
                    'phone' => $data['phone'] ?? $employee->phone,
                ]);

                $this->roleAssignmentService->assign(
                    $user,
                    RolesEnum::EMPLOYEES->value
                );
            }

            return $user->fresh([
                'roles:id,name,title',
                'employee:id,user_id,emp_number,name'
            ]);
        });
    }

    public function updateRoles(User $user, array $roles)
    {
        $user->load('employee:id,user_id,email,phone');
        $this->roleGuardService->validate($user, $roles);
        $this->roleAssignmentService->sync($user, $roles);

        return $user->fresh([
            'roles:id,name,title',
            'employee:id,user_id,emp_number,name'
        ]);
    }

    public function toggleStatus(User $user)
    {
        $newStatus = $user->status === StatusEnum::ACTIVE
            ? StatusEnum::INACTIVE
            : StatusEnum::ACTIVE;

        return $this->userRepo->updateStatus($user, $newStatus);
    }

    public function passwordReset(User $user): void
    {
        $defaultPassword = config('auth.default_password');

        $user->update([
            'password' => Hash::make($defaultPassword),
            'password_changed_at' => now(),
        ]);
    }
}
