package eu.dnetlib.statsapi.repositories;

import com.fasterxml.jackson.databind.ObjectMapper;

import eu.dnetlib.statsapi.domain.Community;
import eu.dnetlib.statsapi.domain.Result;
import eu.dnetlib.statsapi.domain.StatsByAccessMode;

import org.apache.commons.dbutils.DbUtils;
import org.apache.log4j.Logger;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Repository;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Set;

import javax.sql.DataSource;

@Repository
public class CommunityRepository {

    private DataSource statsDB;

    private RedisTemplate<String, String> redisTemplate;

    private HashOperations<String, String, String> jedis;

    private final Logger log = Logger.getLogger(this.getClass());

    public CommunityRepository(DataSource statsDB, RedisTemplate<String, String> redisTemplate) {
        this.statsDB = statsDB;
        this.redisTemplate = redisTemplate;
        this.jedis = this.redisTemplate.opsForHash();
    }

    public Result refreshCommunities() {
        RedisTemplate<String, String> jd = redisTemplate;
        Set<String> keys = jd.keys("community:*");
        for (String key : keys) {
            jd.delete(key);
            getCommunity(key.substring(key.lastIndexOf(":") + 1));
        }
        return new Result("OK", "200", null);
    }

    public Result getCommunity(String community_id) {
        Connection connection = null;
        PreparedStatement st = null;
        ResultSet rs = null;

        String redisKey = "community:" + community_id;
        String redisResponse = jedis.get(redisKey, "result");
        try {
            if (redisResponse != null) {
                return new Result("OK", "200", new ObjectMapper().readValue(redisResponse, Community.class));
            } else {
                connection = statsDB.getConnection();
                st = connection.prepareStatement("SELECT r.type, COUNT(DISTINCT r.id) AS total, sum(CASE WHEN r.bestlicence='Open Access' THEN 1 else 0 END) AS open_access, sum(CASE WHEN r.bestlicence='Embargo' THEN 1 else 0 END) AS embargo, sum(CASE WHEN r.bestlicence='Restricted' THEN 1 else 0 END) AS restricted, sum(CASE WHEN r.bestlicence='Closed Access' THEN 1 else 0 END) AS closed_access FROM result_concepts rc, result r WHERE rc.id=r.id AND (rc.concept=? OR rc.concept LIKE concat(? , '::%')) GROUP BY r.type;");
                st.setString(1, community_id);
                st.setString(2, community_id);

                StatsByAccessMode publications = new StatsByAccessMode();
                StatsByAccessMode software = new StatsByAccessMode();
                StatsByAccessMode datasets = new StatsByAccessMode();
                StatsByAccessMode others = new StatsByAccessMode();

                rs = st.executeQuery();
                while (rs.next()) {
                    switch (rs.getString(1)) {
                        case "publication":
                            publications = new StatsByAccessMode(rs.getInt(2), rs.getInt(3), rs.getInt(4), rs.getInt(5), rs.getInt(6));
                            break;
                        case "software":
                            software = new StatsByAccessMode(rs.getInt(2), rs.getInt(3), rs.getInt(4), rs.getInt(5), rs.getInt(6));
                            break;
                        case "dataset":
                            datasets = new StatsByAccessMode(rs.getInt(2), rs.getInt(3), rs.getInt(4), rs.getInt(5), rs.getInt(6));
                            break;
                        case "other":
                            others = new StatsByAccessMode(rs.getInt(2), rs.getInt(3), rs.getInt(4), rs.getInt(5), rs.getInt(6));
                            break;
                    }
                }
                rs.close();
                st.close();

                st = connection.prepareStatement("SELECT r.type, COUNT(DISTINCT pr.id) FROM result r, result_concepts rc, project_results pr WHERE r.id=rc.id AND r.id=pr.result AND (rc.concept=? OR rc.concept LIKE concat(?, '::%')) GROUP BY r.type;");
                st.setString(1, community_id);
                st.setString(2, community_id);

                int projects = 0;
                rs = st.executeQuery();
                while (rs.next()) {
                    projects += rs.getInt(2);
                    switch (rs.getString(1)) {
                        case "publication":
                            publications.setProjects(rs.getInt(2));
                            break;
                        case "software":
                            software.setProjects(rs.getInt(2));
                            break;
                        case "dataset":
                            datasets.setProjects(rs.getInt(2));
                            break;
                        case "other":
                            others.setProjects(rs.getInt(2));
                            break;
                    }
                }
                rs.close();
                st.close();

                st = connection.prepareStatement("SELECT COUNT(DISTINCT c.name) FROM result_concepts rc, concept c, category cat WHERE c.id=rc.concept AND cat.id=c.category AND cat.id=concat(?, '::virtual');");
                st.setString(1, community_id);

                int virtual_organizations = 0;
                rs = st.executeQuery();
                while (rs.next()) {
                    virtual_organizations = rs.getInt(1);
                }

                Community community = new Community(publications, datasets, software, others, projects, virtual_organizations);
                jedis.put(redisKey, "persistent", "false");
                jedis.put(redisKey, "result", new ObjectMapper().writeValueAsString(community));

                return new Result("OK", "200", community);
            }
        } catch (Exception e) {
            log.error(e);
        } finally {
            DbUtils.closeQuietly(rs);
            DbUtils.closeQuietly(st);
            DbUtils.closeQuietly(connection);
        }
        return new Result("Not Found", "404", null);
    }
}
