package com.restfully.shop.services;

import com.restfully.shop.domain.Link;
import com.restfully.shop.domain.Order;
import com.restfully.shop.domain.Orders;
import org.jboss.resteasy.annotations.providers.jaxb.Formatted;

import javax.ws.rs.Consumes;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.HEAD;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Context;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.UriBuilder;
import javax.ws.rs.core.UriInfo;
import java.net.URI;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;

@Path("/orders")
public class OrderResource
{
   private Map<Integer, Order> orderDB = new Hashtable<Integer, Order>();
   private AtomicInteger idCounter = new AtomicInteger();

   @POST
   @Consumes("application/xml")
   public Response createOrder(Order order, @Context UriInfo uriInfo)
   {
      order.setId(idCounter.incrementAndGet());
      orderDB.put(order.getId(), order);
      System.out.println("Created order " + order.getId());
      UriBuilder builder = uriInfo.getAbsolutePathBuilder();
      builder.path(Integer.toString(order.getId()));
      return Response.created(builder.build()).build();

   }

   @GET
   @Path("{id}")
   @Produces("application/xml")
   public Response getOrder(@PathParam("id") int id, @Context UriInfo uriInfo)
   {
      Order order = orderDB.get(id);
      if (order == null)
      {
         throw new WebApplicationException(Response.Status.NOT_FOUND);
      }
      Response.ResponseBuilder builder = Response.ok(order);
      if (!order.isCancelled()) addCancelHeader(uriInfo, builder);
      return builder.build();
   }

   protected void addCancelHeader(UriInfo uriInfo, Response.ResponseBuilder builder)
   {
      UriBuilder absolute = uriInfo.getAbsolutePathBuilder();
      String cancelUrl = absolute.clone().path("cancel").build().toString();
      builder.header("Link", new Link("cancel", cancelUrl, null));
   }

   @POST
   @Path("{id}/cancel")
   public void cancelOrder(@PathParam("id") int id)
   {
      Order order = orderDB.get(id);
      if (order == null)
      {
         throw new WebApplicationException(Response.Status.NOT_FOUND);
      }
      order.setCancelled(true);
   }


   @HEAD
   @Path("{id}")
   @Produces("application/xml")
   public Response getOrderHeaders(@PathParam("id") int id, @Context UriInfo uriInfo)
   {
      Order order = orderDB.get(id);
      if (order == null)
      {
         throw new WebApplicationException(Response.Status.NOT_FOUND);
      }
      Response.ResponseBuilder builder = Response.ok();
      builder.type("application/xml");
      if (!order.isCancelled()) addCancelHeader(uriInfo, builder);
      return builder.build();
   }

   @GET
   @Produces("application/xml")
   @Formatted
   public Response getOrders(@QueryParam("start") int start,
                             @QueryParam("size") @DefaultValue("2") int size,
                             @Context UriInfo uriInfo)
   {
      UriBuilder builder = uriInfo.getAbsolutePathBuilder();
      builder.queryParam("start", "{start}");
      builder.queryParam("size", "{size}");

      ArrayList<Order> list = new ArrayList<Order>();
      ArrayList<Link> links = new ArrayList<Link>();
      synchronized (orderDB)
      {
         int i = 0;
         for (Order order : orderDB.values())
         {
            if (i >= start && i < start + size) list.add(order);
            i++;
         }
         // next link
         if (start + size < orderDB.size())
         {
            int next = start + size;
            URI nextUri = builder.clone().build(next, size);
            Link nextLink = new Link("next", nextUri.toString(), "application/xml");
            links.add(nextLink);
         }
         // previous link
         if (start > 0)
         {
            int previous = start - size;
            if (previous < 0) previous = 0;
            URI previousUri = builder.clone().build(previous, size);
            Link previousLink = new Link("previous", previousUri.toString(), "application/xml");
            links.add(previousLink);
         }
      }
      Orders orders = new Orders();
      orders.setOrders(list);
      orders.setLinks(links);
      Response.ResponseBuilder responseBuilder = Response.ok(orders);
      addPurgeLinkHeader(uriInfo, responseBuilder);
      return responseBuilder.build();
   }

   protected void addPurgeLinkHeader(UriInfo uriInfo, Response.ResponseBuilder builder)
   {
      UriBuilder absolute = uriInfo.getAbsolutePathBuilder();
      String purgeUrl = absolute.clone().path("purge").build().toString();
      builder.header("Link", new Link("purge", purgeUrl, null));
   }

   @POST
   @Path("purge")
   public void purgeOrders()
   {
      synchronized (orderDB)
      {
         List<Order> orders = new ArrayList<Order>();
         orders.addAll(orderDB.values());
         for (Order order : orders)
         {
            if (order.isCancelled())
            {
               orderDB.remove(order.getId());
            }
         }
      }
   }

   @HEAD
   @Produces("application/xml")
   public Response getOrdersHeaders(@QueryParam("start") int start,
                                    @QueryParam("size") @DefaultValue("2") int size,
                                    @Context UriInfo uriInfo)
   {
      Response.ResponseBuilder builder = Response.ok();
      builder.type("application/xml");
      addPurgeLinkHeader(uriInfo, builder);
      return builder.build();
   }

}