package com.dne.ems.repository.impl;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
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.stereotype.Repository;

import com.dne.ems.dto.GridCoverageDTO;
import com.dne.ems.model.Grid;
import com.dne.ems.repository.GridRepository;
import com.dne.ems.service.JsonStorageService;
import com.fasterxml.jackson.core.type.TypeReference;

import jakarta.annotation.PostConstruct;

@Repository
@Primary // 优先使用此实现，覆盖JPA
public class JsonGridRepositoryImpl implements GridRepository {

    private static final String FILE_NAME = "grids.json";
    private final JsonStorageService jsonStorageService;
    private final List<Grid> gridCache = new ArrayList<>();
    private final AtomicLong idCounter = new AtomicLong(0);

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

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

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

    @Override
    public Grid save(Grid grid) {
        synchronized (gridCache) {
            if (grid.getId() == null) {
                grid.setId(idCounter.incrementAndGet());
                gridCache.add(grid);
            } else {
                Optional<Grid> existingGrid = gridCache.stream()
                        .filter(g -> g.getId().equals(grid.getId()))
                        .findFirst();
                if (existingGrid.isPresent()) {
                    gridCache.remove(existingGrid.get());
                    gridCache.add(grid);
                } else {
                    gridCache.add(grid);
                }
            }
            jsonStorageService.writeData(FILE_NAME, gridCache);
            return grid;
        }
    }

    @Override
    public List<Grid> saveAll(Iterable<Grid> entities) {
        List<Grid> savedEntities;
        synchronized (gridCache) {
            savedEntities = StreamSupport.stream(entities.spliterator(), false)
                .map(this::save) // Re-use the save logic for each entity
                .collect(Collectors.toList());
        }
        return savedEntities;
    }

    @Override
    public Optional<Grid> findById(Long id) {
        synchronized (gridCache) {
            return gridCache.stream().filter(g -> g.getId().equals(id)).findFirst();
        }
    }

    @Override
    public Optional<Grid> findByGridXAndGridY(Integer gridX, Integer gridY) {
        synchronized (gridCache) {
            return gridCache.stream()
                    .filter(g -> g.getGridX().equals(gridX) && g.getGridY().equals(gridY))
                    .findFirst();
        }
    }

    @Override
    public List<Grid> findByIsObstacle(boolean isObstacle) {
        synchronized (gridCache) {
            return gridCache.stream()
                    .filter(g -> g.getIsObstacle() != null && g.getIsObstacle() == isObstacle)
                    .collect(Collectors.toList());
        }
    }

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

    @Override
    public List<GridCoverageDTO> getGridCoverageByCity() {
        synchronized (gridCache) {
            return gridCache.stream()
                    .filter(grid -> grid.getCityName() != null && !grid.getCityName().isEmpty())
                    .collect(Collectors.groupingBy(Grid::getCityName, Collectors.counting()))
                    .entrySet().stream()
                    .map(entry -> new GridCoverageDTO(entry.getKey(), entry.getValue()))
                    .sorted(Comparator.comparing(GridCoverageDTO::coveredGrids).reversed())
                    .collect(Collectors.toList());
        }
    }
} 