package com.dne.ems.repository.impl;

import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicLong;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import org.springframework.context.annotation.Primary;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;
import org.springframework.util.StringUtils;

import com.dne.ems.model.UserAccount;
import com.dne.ems.model.enums.Role;
import com.dne.ems.repository.UserAccountRepository;
import com.dne.ems.service.JsonStorageService;
import com.fasterxml.jackson.core.type.TypeReference;

import jakarta.annotation.PostConstruct;

@Repository
@Primary
public class JsonUserAccountRepositoryImpl implements UserAccountRepository {

    private static final String FILE_NAME = "users.json";
    private final JsonStorageService jsonStorageService;
    private final List<UserAccount> userCache = new ArrayList<>();
    private final AtomicLong idCounter = new AtomicLong(0);

    public JsonUserAccountRepositoryImpl(JsonStorageService jsonStorageService) {
        this.jsonStorageService = jsonStorageService;
    }

    @PostConstruct
    public void init() {
        List<UserAccount> users = jsonStorageService.readData(FILE_NAME, new TypeReference<>() {});
        synchronized (userCache) {
            userCache.addAll(users);
            long maxId = users.stream()
                              .mapToLong(UserAccount::getId)
                              .max()
                              .orElse(0L);
            idCounter.set(maxId);
        }
    }

    @Override
    public Optional<UserAccount> findByEmail(String email) {
        synchronized (userCache) {
            return userCache.stream()
                    .filter(user -> user.getEmail().equalsIgnoreCase(email))
                    .findFirst();
        }
    }

    @Override
    public Optional<UserAccount> findByPhone(String phone) {
        synchronized (userCache) {
            return userCache.stream()
                    .filter(user -> user.getPhone() != null && user.getPhone().equals(phone))
                    .findFirst();
        }
    }

    @Override
    public Optional<UserAccount> findById(Long id) {
        synchronized (userCache) {
            return userCache.stream()
                    .filter(user -> user.getId().equals(id))
                    .findFirst();
        }
    }

    @Override
    public List<UserAccount> findByRole(Role role) {
        synchronized (userCache) {
            return userCache.stream()
                    .filter(user -> user.getRole() == role)
                    .collect(Collectors.toList());
        }
    }

    @Override
    public List<UserAccount> findGridWorkersByCoordinates(Integer gridX, Integer gridY) {
        synchronized (userCache) {
            return userCache.stream()
                    .filter(user -> user.getRole() == Role.GRID_WORKER &&
                            Objects.equals(user.getGridX(), gridX) &&
                            Objects.equals(user.getGridY(), gridY))
                    .collect(Collectors.toList());
        }
    }

    @Override
    public Optional<UserAccount> findByGridXAndGridY(Integer gridX, Integer gridY) {
        synchronized (userCache) {
            return userCache.stream()
                    .filter(user -> Objects.equals(user.getGridX(), gridX) &&
                            Objects.equals(user.getGridY(), gridY))
                    .findFirst();
        }
    }

    @Override
    public List<UserAccount> findAll() {
        synchronized (userCache) {
            return new ArrayList<>(userCache);
        }
    }

    @Override
    public UserAccount save(UserAccount userAccount) {
        synchronized (userCache) {
            if (userAccount.getId() == null) {
                userAccount.setId(idCounter.incrementAndGet());
                userCache.add(userAccount);
            } else {
                Optional<UserAccount> existing = findById(userAccount.getId());
                if (existing.isPresent()) {
                    userCache.remove(existing.get());
                }
                userCache.add(userAccount);
            }
            jsonStorageService.writeData(FILE_NAME, userCache);
            return userAccount;
        }
    }

    @Override
    public List<UserAccount> saveAll(Iterable<UserAccount> entities) {
        synchronized (userCache) {
            List<UserAccount> savedUsers = StreamSupport.stream(entities.spliterator(), false)
                .map(this::save)
                .collect(Collectors.toList());
            // The save method already writes to the file, but a final single write is more efficient.
            // However, for simplicity and consistency with individual 'save', we leave it as is.
            return savedUsers;
        }
    }

    @Override
    public long countByRole(Role role) {
        synchronized (userCache) {
            return userCache.stream().filter(user -> user.getRole() == role).count();
        }
    }

    @Override
    public long count() {
        synchronized (userCache) {
            return userCache.size();
        }
    }

    @Override
    public void deleteById(Long id) {
        synchronized (userCache) {
            userCache.removeIf(user -> user.getId().equals(id));
            jsonStorageService.writeData(FILE_NAME, userCache);
        }
    }

    @Override
    public Page<UserAccount> findUsers(Role role, String name, Pageable pageable) {
        synchronized (userCache) {
            List<UserAccount> filteredUsers = userCache.stream()
                .filter(user -> role == null || user.getRole() == role)
                .filter(user -> !StringUtils.hasText(name) || user.getName().toLowerCase().contains(name.toLowerCase()))
                .collect(Collectors.toList());
            
            int start = (int) pageable.getOffset();
            int end = Math.min((start + pageable.getPageSize()), filteredUsers.size());
            
            List<UserAccount> pageContent = (start > filteredUsers.size()) ? List.of() : filteredUsers.subList(start, end);

            return new PageImpl<>(pageContent, pageable, filteredUsers.size());
        }
    }
} 