package de.ihrig.fwla.display.service.weather.impl;

import java.net.URI;
import java.util.concurrent.TimeUnit;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestClientException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.util.UriComponentsBuilder;

import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;

import de.ihrig.fwla.display.api.Coordinate;
import de.ihrig.fwla.display.service.weather.api.Weather;
import de.ihrig.fwla.display.service.weather.api.WeatherService;
import de.ihrig.fwla.display.service.weather.api.Wind;
import lombok.EqualsAndHashCode;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class WeatherServiceImpl implements WeatherService {

	static final String baseUri = "https://api.openweathermap.org/data/2.5";

	private final RestTemplate restTemplate;
	private final Cache<CacheKey, Weather> cache;

	private String apiKey;

	@Autowired
	public WeatherServiceImpl(RestTemplate restTemplate) {
		this.restTemplate = restTemplate;
		this.cache = CacheBuilder.newBuilder()
				.expireAfterWrite(10, TimeUnit.MINUTES).maximumSize(10).build();
	}

	@Value("${app.weather.apiKey}")
	public void setApiKey(String apiKey) {
		this.apiKey = apiKey;
	}

	@Override
	public Weather getWeatherAtCoordinate(Coordinate coordinate) {
		CacheKey key = new CacheKey(coordinate);
		Weather weather = cache.getIfPresent(key);
		if (weather == null) {
			weather = doGetWeatherAtCoordiante(coordinate);
			if (weather != null) {
				cache.put(key, weather);
			}
		}

		return weather;
	}

	protected Weather doGetWeatherAtCoordiante(Coordinate coordinate) {
		URI uri = UriComponentsBuilder.fromUriString(baseUri).path("/weather")
				.queryParam("lat", coordinate.getLatitude())
				.queryParam("lon", coordinate.getLongitude())
				.queryParam("units", "metric").queryParam("lang", "de")
				.queryParam("APPID", apiKey).build().toUri();

		try {
			de.ihrig.fwla.display.service.weather.impl.model.web.WeatherResponse weather = restTemplate
					.getForObject(uri,
							de.ihrig.fwla.display.service.weather.impl.model.web.WeatherResponse.class);

			return new Weather() {
				@Getter
				private final Wind wind = new Wind() {

					@Getter
					private final float speed = weather.getWind().getSpeed();
					@Getter
					private final int degrees = weather.getWind().getDeg();
				};

				@Getter
				private final Coordinate coordinate = new Coordinate() {

					@Getter
					private final double latitude = weather.getCoord().getLat();
					@Getter
					private final double longitude = weather.getCoord()
							.getLon();
				};

				@Getter
				private final float temperature = weather.getMain().getTemp();
				@Getter
				private final String iconCode = weather.getWeather().get(0)
						.getIcon();
				@Getter
				private final String description = weather.getWeather().get(0)
						.getDescription();
			};
		} catch (RestClientException e) {
			log.error("Unable to get weather.", e);
			return null;
		}
	}

	@Getter
	@EqualsAndHashCode
	static class CacheKey implements Coordinate {

		private final double latitude;
		private final double longitude;

		public CacheKey(Coordinate coordinate) {
			this.latitude = coordinate.getLatitude();
			this.longitude = coordinate.getLongitude();
		}
	}

}
