import { ErrorType, IpcClient as ManagerIpc } from "@autoplex-api/manager";
import { MovieTicket } from "@autoplex/database";
import { IpcResponseError } from "@autoplex/ipc";
import { handleRequest, RouteRegisterFactory, MiddlewareMethod, Status, respond } from "@autoplex/webserver";
import Application from "../../../Application";
import MovieSearch from "../../MovieSearch";
import { auth } from "../middleware/auth";
import { RequestTmdbMovieRequest } from "../requests";
import { convertTicketsToMovies } from "../../../utils";

/**
 * Register API routes
 */
export default function register(factory: RouteRegisterFactory<MiddlewareMethod<void>, Application>, app: Application) {

	factory.prefix("/api", [auth], (factory, app) => {

		/**
		 * Service proxies
		 */
		factory.proxy("/tvdb/artwork", "https://artworks.thetvdb.com/");
		factory.proxy("/tmdb/image", "https://image.tmdb.org/t/p/");

		/**
		 * Search for a movie
		 */
		factory.get("/movie/search", async (request, reply) => {
			let query = <string>(<any>request.query)["query"];
			let year = parseInt((<any>request.query)["year"]) || undefined;
			let results = await app.service<MovieSearch>("Movie Search").search(query, year);
			respond(reply, Status.Ok, results);
		});

		/**
		 * Lookup a movie's details
		 */
		factory.get("/movie/details/:id", async (request, reply) => {
			let id = parseInt((<any>request.params)["id"]);
			if (id.toString() !== (<any>request.params)["id"].trim()) {
				return respond(reply, Status.BadRequest);
			}
			let results = await app.service<MovieSearch>("Movie Search").details(id);
			respond(reply, Status.Ok, results);
		});

		// Movie Request ---------------------------------------------------------------------------

		factory.prefix("/movie/request", [], (factory, app) => {
			/**
			 * Fetch a user's active movie tickets
			 */
			factory.get("/active", async (request, reply) => {
				let user = request.middlewareParams.auth.user;
				let tickets = await user.activeMovieTickets();
				let movies = await convertTicketsToMovies(tickets);
				respond(reply, Status.Ok, movies);
				return reply.send({ status: "Success", data: movies });
			});

			/**
			 * Fetch a movie request
			 */
			factory.get("/:id", async (request, reply) => {
				let id = parseInt((<any>request.params)["id"]);
				let ticket = await MovieTicket.findOne(id);
				if (ticket === undefined) {
					return respond(reply, Status.NotFound);
				}
				let movie = (await convertTicketsToMovies([ticket]))[0];
				respond(reply, Status.Ok, movie);
			});

			/**
			 * Cancel an active request
			 */
			factory.delete("/:id", async (request, reply) => {
				let user = request.middlewareParams.auth.user;
				let id = parseInt((<any>request.params)["id"]);
				let ticket = await MovieTicket.findOne(id, { where: { user: user.id } });
				if (ticket === undefined) {
					return respond(reply, Status.NotFound);
				}
				ticket.isCanceled = true;
				await ticket.save();
				respond(reply, Status.Ok);
			});

			/**
			 * Request a movie to download
			 */
			factory.post("/create/:tmdb_id", handleRequest([RequestTmdbMovieRequest], async (request, reply) => {
				let ticketId: any;
				try {
					let userId = request.middlewareParams.auth.user.id;
					let tmdbId = (<any>request.params)["tmdb_id"].parseInt();
					ticketId = app.service<ManagerIpc>("Manager").createMovieRequest(userId, tmdbId);
				} catch(e) {
					if (e instanceof IpcResponseError) {
						switch(e.message) {
							case ErrorType.MovieNotFound:
								respond(reply, Status.NotFound);
								return;
							case ErrorType.MovieTicketConflict:
								respond(reply, Status.Conflict);
								return;
						}
					}
					respond(reply, Status.InternalServerError);
					return;
				}
				respond(reply, Status.Ok, { ticketId });
			}));
		});
	});
}
