package com.example.demo.entrysData.service;

import java.util.List;
import java.util.Optional;
import java.util.stream.StreamSupport;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import com.example.demo.core.configuration.Constants;
import com.example.demo.core.error.NotFoundException;
import com.example.demo.core.security.UserPrincipal;
import com.example.demo.entrysData.model.EntrysDataEntity;
import com.example.demo.entrysData.model.EntrysDataGrouped;
import com.example.demo.entrysData.model.EntrysDataGroupedDepartment;
import com.example.demo.entrysData.model.EntrysDataRole;
import com.example.demo.entrysData.repository.EntrysDataRepository;

@Service
public class EntrysDataService implements UserDetailsService{
    private final EntrysDataRepository repository;
    private final PasswordEncoder passwordEncoder;

    public EntrysDataService(EntrysDataRepository repository, PasswordEncoder passwordEncoder) {
        this.repository = repository;
        this.passwordEncoder = passwordEncoder;
    }

    private void checklogin(Long id, String login) {
        final Optional<EntrysDataEntity> existsUser = repository.findByloginIgnoreCase(login);
        if (existsUser.isPresent() && !existsUser.get().getId().equals(id)) {
            throw new IllegalArgumentException(
                    String.format("User with login %s is already exists", login));
        }
    }

    @Transactional(readOnly = true)
    public List<EntrysDataEntity> getAll(Long departmentId) {
        if (departmentId <= 0L) {
            return StreamSupport.stream(repository.findAll().spliterator(), false).toList();
        } else {
            return repository.findByDepartmentId(departmentId);
        }
    }

    @Transactional(readOnly = true)
    public Page<EntrysDataEntity> getAll(Long departmentId, int page, int size) {
        final Pageable pageRequest = PageRequest.of(page, size);
        if (departmentId <= 0L) {
            return repository.findAll(pageRequest);
        } else {
            return repository.findByDepartmentId(departmentId, pageRequest);
        }
    }

    @Transactional(readOnly = true)
    public EntrysDataEntity get(Long id) {
        return repository.findById(id)
                .orElseThrow(() -> new NotFoundException(EntrysDataEntity.class, id));
    }

    @Transactional
    public EntrysDataEntity create(EntrysDataEntity entity) {
        if (entity == null) {
            throw new IllegalArgumentException("Entity is null");
        }
        checklogin(null, entity.getLogin());
        final String password = Optional.ofNullable(entity.getPassword()).orElse("");
        entity.setPassword(
                passwordEncoder.encode(
                        StringUtils.hasText(password.strip()) ? password : Constants.DEFAULT_PASSWORD));
        entity.setRole(Optional.ofNullable(entity.getRole()).orElse(EntrysDataRole.USER));
        repository.save(entity);
        return repository.save(entity);
    }

    @Transactional
    public EntrysDataEntity update(Long id, EntrysDataEntity entity) {
        final EntrysDataEntity existsEntity = get(id);
        checklogin(id, entity.getLogin());
        existsEntity.setLogin(entity.getLogin());
        repository.save(existsEntity);
        return repository.save(existsEntity);
    }

    @Transactional
    public EntrysDataEntity delete(Long id) {
        final EntrysDataEntity existsEntity = get(id);
        repository.delete(existsEntity);
        return existsEntity;
    }

    @Transactional
    public List<EntrysDataGrouped> getCount() {
        List<EntrysDataGrouped> list = repository.getCountEntrysInDepartment();
        return list;
    }

    @Transactional
    public Page<EntrysDataGroupedDepartment> getAllWithDepartment(int page, int size) {
        final Pageable pageRequest = PageRequest.of(page, size);
        return repository.findAllWithDepartment(pageRequest);
    }

    @Transactional(readOnly = true)
    public EntrysDataEntity getByLogin(String login) {
        return repository.findByloginIgnoreCase(login)
                .orElseThrow(() -> new IllegalArgumentException("Invalid login"));
    }

    @Override
    @Transactional(readOnly = true)
    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
        final EntrysDataEntity existsUser = getByLogin(username);
        return new UserPrincipal(existsUser);
    }
}
