package com.atlassian.bitbucket.mesh.repository;

import com.atlassian.bitbucket.mesh.AbstractDao;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.support.GeneratedKeyHolder;
import org.springframework.jdbc.support.KeyHolder;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Nonnull;
import java.sql.PreparedStatement;
import java.sql.Statement;
import java.time.Instant;
import java.util.List;

@Repository("workTreeStateDao")
public class JdbcWorkTreeStateDao extends AbstractDao implements WorkTreeStateDao {

    private final int batchSize = 200;
    private final JdbcTemplate jdbcTemplate;

    public JdbcWorkTreeStateDao(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    @Transactional
    public long create(@Nonnull Instant expiry, boolean hasAlternates, @Nonnull String repositoryId) {
        String sql = "INSERT INTO work_tree(expiry, has_alternates, repository_id) VALUES(?, ?, ?)";

        KeyHolder holder = new GeneratedKeyHolder();
        jdbcTemplate.update(con -> {
            PreparedStatement ps = con.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
            ps.setLong(1, expiry.toEpochMilli());
            ps.setBoolean(2, hasAlternates);
            ps.setString(3, repositoryId);
            return ps;
        }, holder);
        Number id = holder.getKey();
        if (id != null) {
            return id.longValue();
        }
        throw new DataIntegrityViolationException("Database did not return a valid ID for work tree creation");
    }

    @Override
    @Transactional
    public int deleteById(long id) {
        String sql = "DELETE FROM work_tree WHERE id = ?";

        return jdbcTemplate.update(sql, id);
    }

    @Override
    @Transactional
    public void deleteByIds(@Nonnull List<Long> ids) {
        String sql = "DELETE FROM work_tree WHERE id = ?";

        jdbcTemplate.batchUpdate(sql, ids, batchSize, (ps, argument) -> ps.setLong(1, argument));
    }

    @Override
    public WorkTreeState findById(long id) {
        String sql = "SELECT repository_id, has_alternates, expiry FROM work_tree WHERE id = ?";

        return jdbcTemplate.query(sql, rs -> {
            if (rs.next()) {
                return new WorkTreeState.Builder()
                        .id(id)
                        .expiry(Instant.ofEpochMilli(rs.getLong(3)))
                        .hasAlternates(rs.getBoolean(2))
                        .repositoryId(rs.getString(1))
                        .build();
            }
            return null;
        }, id);
    }

    @Override
    public List<WorkTreeState> findByRepository(@Nonnull String repositoryId) {
        String sql = "SELECT id, repository_id, has_alternates, expiry  FROM work_tree WHERE repository_id = ?";
        return jdbcTemplate.query(sql, (rs, rowNum) -> new WorkTreeState.Builder()
                .id(rs.getLong(1))
                .expiry(Instant.ofEpochMilli(rs.getLong(4)))
                .hasAlternates(rs.getBoolean(3))
                .repositoryId(rs.getString(2))
                .build(), repositoryId);
    }

    @Override
    @Nonnull
    public List<Long> getAllIds() {
        String sql = "SELECT id FROM work_tree ORDER BY expiry DESC";
        return jdbcTemplate.query(sql,
                (rs, rowNum) -> rs.getLong(1));
    }
}
