package org.strangeforest.tcb.stats.web;

import java.sql.*;
import java.time.*;
import java.util.*;

import org.springframework.beans.factory.annotation.*;
import org.springframework.jdbc.core.namedparam.*;
import org.springframework.jdbc.support.*;
import org.springframework.stereotype.*;

import static java.util.stream.Collectors.*;
import static org.strangeforest.tcb.stats.service.ParamsUtil.*;

@Repository @VisitorSupport
public class VisitorRepository {

	@Autowired private NamedParameterJdbcTemplate jdbcTemplate;

	private static final String FIND = "SELECT visitor_id, country_id, country, visits, last_visit FROM visitor WHERE ip_address = :ipAddress AND active";
	private static final String FIND_ALL = "SELECT visitor_id, country_id, country, ip_address, visits, last_visit FROM visitor WHERE active";
	private static final String CREATE = "INSERT INTO visitor (ip_address, country_id, country, visits, last_visit) VALUES (:ipAddress, :countryId, :country, :visits, :lastVisit)";
	private static final String SAVE = "UPDATE visitor SET visits = :visits, last_visit = :lastVisit WHERE visitor_id = :visitorId";
	private static final String EXPIRE = "UPDATE visitor SET active = FALSE WHERE visitor_id = :visitorId";

	public Optional<Visitor> find(String ipAddress) {
		return jdbcTemplate.query(FIND, params("ipAddress", ipAddress), rs ->
			rs.next() ? Optional.of(mapVisitor(ipAddress, rs)) : Optional.<Visitor>empty()
		);
	}

	public List<Visitor> findAll() {
		return jdbcTemplate.query(FIND_ALL, (rs, rowNum) -> mapVisitor(rs.getString("ip_address"), rs));
	}

	private static Visitor mapVisitor(String ipAddress, ResultSet rs) throws SQLException {
		return new Visitor(
			rs.getLong("visitor_id"),
			ipAddress,
			rs.getString("country_id"),
			rs.getString("country"),
			rs.getInt("visits"),
			rs.getTimestamp("last_visit").toInstant()
		);
	}

	public Visitor create(String ipAddress, String countryId, String country) {
		int visits = 1;
		Instant lastVisit = Instant.now();
		KeyHolder keyHolder = new GeneratedKeyHolder();
		jdbcTemplate.update(CREATE,
			params("ipAddress", ipAddress)
				.addValue("countryId", countryId)
				.addValue("country", country)
				.addValue("visits", visits)
				.addValue("lastVisit", Timestamp.from(lastVisit)),
		keyHolder, new String[] {"visitor_id"});
		return new Visitor(keyHolder.getKey().longValue(), ipAddress, countryId, country, visits, lastVisit);
	}

	public void save(Visitor visitor) {
		jdbcTemplate.update(SAVE,
			params("visitorId", visitor.getId())
				.addValue("visits", visitor.getVisits())
				.addValue("lastVisit", Timestamp.from(visitor.getLastVisit()))
		);
	}

	public void saveAll(Collection<Visitor> visitors) {
		jdbcTemplate.batchUpdate(SAVE, visitors.stream().map(visitor ->
			params("visitorId", visitor.getId())
				.addValue("visits", visitor.getVisits())
				.addValue("lastVisit", Timestamp.from(visitor.getLastVisit()))
		).collect(toList()).toArray(new SqlParameterSource[visitors.size()]));
	}

	public void expire(Visitor visitor) {
		jdbcTemplate.update(EXPIRE, params("visitorId", visitor.getId()));
	}
}

@@ -282,24 +282,58 @@
 reotype.*;%0A%0A
+import static java.lang.String.*;%0A
 import stati
@@ -1125,113 +1125,12 @@
 isit
- WHERE visitor_id = :visitorId%22;%0A%09private static final String EXPIRE = %22UPDATE visitor SET active = FALSE
+%251$s
  WHE
@@ -2400,105 +2400,134 @@
 %7B%0A%09%09
-jdbcTemplate.update(SAVE,%0A%09%09%09params(%22visitorId%22, visitor.getId())%0A%09%09%09%09.addValue(%22v
+save(visitor, false);%0A%09%7D%0A%0A%09public void expire(Visitor visitor) %7B%0A%09%09save(visitor, true);%0A%09%7D%0A%0A%09private void save(V
 isit
-s%22,
+or
  visitor
 .get
@@ -2526,91 +2526,124 @@
 itor
-.getVisits())%0A%09%09%09%09.addValue(%22lastVisit%22, Timestamp.from(visitor.getLastVisit()))%0A%09%09
+, boolean expire) %7B%0A%09%09jdbcTemplate.update(format(SAVE, expire ? %22, active = FALSE%22 : %22%22), saveVisitorParams(visitor)
 );%0A%09
@@ -2725,22 +2725,41 @@
 hUpdate(
-SAVE, 
+%0A%09%09%09format(SAVE, %22%22),%0A%09%09%09
 visitors
@@ -2776,173 +2776,44 @@
 map(
-v
+V
 isitor
- -%3E%0A%09%09%09params(%22visitorId%22, visitor.getId())%0A%09%09%09%09.addValue(%22visits%22, visitor.getVisits())%0A%09%09%09%09.addValue(%22lastVisit%22, Timestamp.from(visitor.getLastVisit()))%0A%09%09
+Repository::saveVisitorParams
 ).co
@@ -2876,16 +2876,19 @@
 size()%5D)
+%0A%09%09
 );%0A%09%7D%0A%0A%09
@@ -2892,108 +2892,232 @@
 %0A%0A%09p
-ublic void expire(Visitor visitor) %7B%0A%09%09jdbcTemplate.update(EXPIRE, params(%22visitorId%22, visitor.getId
+rivate static MapSqlParameterSource saveVisitorParams(Visitor visitor) %7B%0A%09%09return params(%22visitorId%22, visitor.getId())%0A%09%09%09.addValue(%22visits%22, visitor.getVisits())%0A%09%09%09.addValue(%22lastVisit%22, Timestamp.from(visitor.getLastVisit
 ()))

Save Visitor on expiry