/*
 * Queue - A Queueing system that can be used to handle labs in higher education
 * Copyright (C) 2016-2021  Delft University of Technology
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */
package nl.tudelft.queue.service;

import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import nl.tudelft.labracore.api.dto.QuestionDetailsDTO;
import nl.tudelft.queue.properties.ElasticProperties;

import org.modelmapper.ModelMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.client.support.BasicAuthenticationInterceptor;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

@Service
public class QuestionService {

	private ModelMapper mapper;

	private ElasticProperties elasticProperties;

	private RestTemplate restTemplate;

	@Autowired
	public QuestionService(ElasticProperties elasticProperties) {
		this(elasticProperties, new RestTemplate());
	}

	protected QuestionService(ElasticProperties elasticProperties, RestTemplate restTemplate) {
		this.mapper = new ModelMapper();
		this.elasticProperties = elasticProperties;
		this.restTemplate = restTemplate;

		restTemplate.getInterceptors().add(new BasicAuthenticationInterceptor(elasticProperties.getUsername(),
				elasticProperties.getPassword()));
	}

	public List<QuestionDetailsDTO> searchForQuestions(String query, Long editionId, Long assignmentId,
			int amount) {
		query = query.replace("\"", "\\\"");

		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);

		HttpEntity<String> entity = new HttpEntity<>(
				"{\"query\": {\"bool\": {\"must\": [{\"exists\": {\"field\": \"answer\"}}, {\"term\": {\"edition.id\": "
						+ editionId + "}}, {\"match\": {\"question\": {\"query\": \"" + query + "\"}}}]}}}",
				headers);

		try {
			OuterHits hits = restTemplate.postForObject(
					elasticProperties.getUrl() + "/questions/_search?size=" + amount, entity,
					OuterHits.class);
			Collections.sort(hits.getHits().getHits());
			return hits.getHits().getHits().stream()
					.map(Hit::get_source)
					.map(q -> q.toDetails(mapper))
					.collect(Collectors.toList());
		} catch (Exception e) {
			e.printStackTrace();
			return Collections.emptyList();
		}
	}

	@Data
	@Builder
	@NoArgsConstructor
	@AllArgsConstructor
	private static class OuterHits {
		private Hits hits;
	}

	@Data
	@Builder
	@NoArgsConstructor
	@AllArgsConstructor
	private static class Hits {
		private List<Hit> hits;
	}

	@Data
	@Builder
	@NoArgsConstructor
	@AllArgsConstructor
	private static class Hit implements Comparable<Hit> {
		private Double _score;
		private QuestionData _source;

		@Override
		public int compareTo(Hit hit) {
			return hit._score.compareTo(this._score);
		}
	}

	@Data
	@Builder
	@NoArgsConstructor
	@AllArgsConstructor
	private static class QuestionData {
		private Long id;
		private String question;
		private String answer;
		private Integer usefulCount;
		private EditionData edition;
		private AssignmentData assignment;

		public QuestionDetailsDTO toDetails(ModelMapper mapper) {
			QuestionDetailsDTO details = mapper.map(this, QuestionDetailsDTO.class);
			return details;
		}
	}

	@Data
	@Builder
	@NoArgsConstructor
	@AllArgsConstructor
	private static class EditionData {
		private Long id;
		private String name;
		private String courseName;
	}

	@Data
	@Builder
	@NoArgsConstructor
	@AllArgsConstructor
	private static class AssignmentData {
		private Long id;
		private String name;
	}

}
