/**
 * 
 */
package com.spring.webflux.demo.controller;

import javax.validation.Valid;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import com.spring.webflux.demo.exception.TweetNotFoundException;
import com.spring.webflux.demo.model.Tweet;
import com.spring.webflux.demo.payload.ErrorResponse;
import com.spring.webflux.demo.repository.TweetRepository;

import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

/**
 * @author vrajesh
 *
 */
@RestController
public class TweetController {

	@Autowired
	private TweetRepository tweetRepository;

	/**
	 * Method to return all the tweets
	 * 
	 * @return
	 */
	@GetMapping("/myTweets")
	public Flux<Tweet> getAllMyTweets() {
		return tweetRepository.findAll();
	}

	/**
	 * Method to create a new Tweet
	 * 
	 * @param myTweet
	 * @return
	 */
	@PostMapping("/createTweet")
	public Mono<Tweet> createTweet(@Valid @RequestBody Tweet myTweet) {
		return tweetRepository.save(myTweet);
	}

	/**
	 * Method to retrieve tweet by tweetId
	 * 
	 * @param tweetId
	 * @return
	 */
	@GetMapping("/myTweets/{tweetId}")
	public Mono<ResponseEntity<Tweet>> getTweetById(@PathVariable(value = "tweetId") String tweetId) {
		return tweetRepository.findById(tweetId).map(savedTweet -> ResponseEntity.ok(savedTweet))
				.defaultIfEmpty(ResponseEntity.notFound().build());
	}

	/**
	 * Method to update Tweet by tweetId
	 * 
	 * @param tweetId
	 * @param tweet
	 * @return
	 */
	@PutMapping("/myTweets/{tweetId}")
	public Mono<ResponseEntity<Tweet>> updateTweet(@PathVariable(value = "tweetId") String tweetId,
			@Valid @RequestBody Tweet tweet) {
		return tweetRepository.findById(tweetId).flatMap(existingTweet -> {
			existingTweet.setText(tweet.getText());
			return tweetRepository.save(existingTweet);
		}).map(updateTweet -> new ResponseEntity<>(updateTweet, HttpStatus.OK))
				.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
	}

	/**
	 * Method to delete Tweet by tweetId
	 * 
	 * @param tweetId
	 * @return
	 */
	@DeleteMapping("/myTweets/{tweetId}")
	public Mono<ResponseEntity<Void>> deleteTweet(@PathVariable(value = "tweetId") String tweetId) {

		return tweetRepository.findById(tweetId)
				.flatMap(existingTweet -> tweetRepository.delete(existingTweet)
						.then(Mono.just(new ResponseEntity<Void>(HttpStatus.OK))))
				.defaultIfEmpty(new ResponseEntity<>(HttpStatus.NOT_FOUND));
	}

	/**
	 * Tweets are Sent to the client as Server Sent Events
	 * 
	 * @return
	 */
	@GetMapping(value = "/stream/tweets", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
	public Flux<Tweet> streamAllTweets() {
		return tweetRepository.findAll();
	}

	/**
	 * Exception Handling Examples (These can be put into a @ControllerAdvice to
	 * handle exceptions globally)
	 * 
	 * @param ex
	 * @return
	 */

	@SuppressWarnings("rawtypes")
	@ExceptionHandler(DuplicateKeyException.class)
	public ResponseEntity handleDuplicateKeyException(DuplicateKeyException ex) {
		return ResponseEntity.status(HttpStatus.CONFLICT)
				.body(new ErrorResponse("A Tweet with the same text already exists"));
	}

	/**
	 * Method to handle tweet not found exception
	 * 
	 * @param ex
	 * @return
	 */
	@SuppressWarnings("rawtypes")
	@ExceptionHandler(TweetNotFoundException.class)
	public ResponseEntity handleTweetNotFoundException(TweetNotFoundException ex) {
		return ResponseEntity.notFound().build();
	}
}