import { IpcClient as TorrentSearchIpc, ITorrentLink } from "@autoplex-api/torrent-search";
import { ISerializedTorrent } from "@autoplex-api/torrent";
import { MovieTicket, MovieTorrent } from "@autoplex/database";
import { InternalService, Microservice } from "@autoplex/microservice";
import PostProcessor from "./PostProcessor";
import TorrentManager from "./TorrentManager";

export default class Supervisor extends InternalService
{
	/**
	 * The movie search service instance
	 */
	protected torrentSearch!: TorrentSearchIpc;

	/**
	 * The torrent client IPC service instance
	 */
	protected torrentManager!: TorrentManager;

	/**
	 * The post-processor service instance
	 */
	protected postProcessor!: PostProcessor;

	/**
	 * The service name
	 */
	public readonly NAME = "Supervisor";

	/**
	 * Link to other internal services
	 */
	public link(app: Microservice) {
		this.torrentSearch  = app.service<TorrentSearchIpc>("Torrent Search");
		this.torrentManager = app.service<TorrentManager>("Torrent Manager");
		this.postProcessor  = app.service<PostProcessor>("Post Processor");
	}

	/**
	 * All services are booted and ready
	 */
	public start() {
		this.searchMovies();
	}

	// Request Flow --------------------------------------------------------------------------------

	/**
	 * 1. A movie ticket has been added/torrent has gone stale, so
	 * dispatch a search task for a torrent
	 */
	public async onMovieTicketNeedsTorrent(ticket: MovieTicket) {
		let link = await this.torrentSearch.searchMovie(ticket.title, ticket.imdbId, ticket.year);
		if (link === null) {
			this.log("Could not find a torrent for:", ticket.title);
			return;
		}
		this.onMovieTorrentFound(ticket, link);
	}

	/**
	 * 2. A movie torrent has been found, so add it to the client
	 */
	public onMovieTorrentFound(ticket: MovieTicket, link: ITorrentLink) {
		this.log("A torrent was found for movie:", ticket.title);
		this.torrentManager.enqueueMovie(ticket, link);
	}

	/**
	 * 3. A torrent has finished downloading, so send it for post-processing
	 */
	public onMovieTorrentFinished(torrent: MovieTorrent, details: ISerializedTorrent) {
		this.log("A torrent has finished for movie:", torrent.movieTicket.title);
		this.postProcessor.enqueueMovie(torrent, details);
	}

	/**
	 * 4. A ticket's torrent has finished post-processing, so fulfill the ticket
	 */
	public onMovieTorrentProcessed(ticket: MovieTicket) {
		this.log("A movie torrent has been successfully processed:", ticket.title);
		ticket.fulfill();
	}

	// Tasks ---------------------------------------------------------------------------------------

	/**
	 * Search available movies in the database
	 */
	public async searchMovies() {
		let tickets = await MovieTicket.find({
			where: {isFulfilled: false, isCanceled: false},
			relations: [ "torrents" ]
		});
		for (let ticket of tickets) {
			// Skip already-resolved non-stale torrents
			if (ticket.torrents.length > 0 && !ticket.isStale) {
				this.log("Skipping already satisfied ticket")
				continue;
			}
			this.onMovieTicketNeedsTorrent(ticket);
		}
	}
}
