package com.coreoz.wisp.schedule;

import java.time.Instant;
import java.time.LocalTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;

public class FixedHourSchedule implements Schedule {

	private final LocalTime executionTime;
	private final ZoneId zoneId;

	/**
	 * Parse time in the form of "hh:mm" or "hh:mm:ss"
	 */
	public FixedHourSchedule(String every) {
		this(LocalTime.parse(every));
	}

	/**
	 * Parse time in the form of "hh:mm" or "hh:mm:ss"
	 */
	public FixedHourSchedule(String every, ZoneId zoneId) {
		this(LocalTime.parse(every), zoneId);
	}

	public FixedHourSchedule(LocalTime every) {
		this(every, ZoneId.systemDefault());
	}

	public FixedHourSchedule(LocalTime every, ZoneId zoneId) {
		this.executionTime = every;
		this.zoneId = zoneId;
	}

	public LocalTime executionTime() {
		return executionTime;
	}

	public ZoneId zoneId() {
		return zoneId;
	}

	@Override
	public long nextExecutionInMillis(long currentTimeInMillis, int executionsCount, Long lastExecutionTimeInMillis) {
		return durationUntilNextExecutionInMillis(currentTimeInMillis, lastExecutionTimeInMillis)
				+ currentTimeInMillis;
	}

	long durationUntilNextExecutionInMillis(long currentTimeInMillis, Long lastExecutionTimeInMillis) {
		ZonedDateTime currentDateTime = Instant
				.ofEpochMilli(currentTimeInMillis)
				.atZone(zoneId);

		return currentDateTime
			.until(
				nextExecutionDateTime(
					currentDateTime,
					lastExecutionTimeInMillis != null && lastExecutionTimeInMillis == currentTimeInMillis
				),
				ChronoUnit.MILLIS
			);
	}

	private ZonedDateTime nextExecutionDateTime(ZonedDateTime currentDateTime, boolean nextExecutionShouldBeNextDay) {
		if(!nextExecutionShouldBeNextDay && currentDateTime.toLocalTime().compareTo(executionTime) <= 0) {
			return executionTime.atDate(currentDateTime.toLocalDate()).atZone(zoneId);
		}
		return executionTime.atDate(currentDateTime.toLocalDate()).plusDays(1).atZone(zoneId);
	}

	@Override
	public String toString() {
		return "at " + executionTime + " " + zoneId;
	}

}
