import axios from "axios";
import { Downloader } from "./downloader";
import { Links } from "./links";
import { random_int } from "./tools";

let links = new Links;

function sleep(ms: number) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

export class UrlPool {

	id: string;
	storage: UrlPoolStorage;

	urls: string[] = [];
	used: boolean[] = [];

	valid_to: Date;

	generating = false;
	downloaders: Downloader[] = [];

	total_size: number = 0;
	is_direct = false;
	ready = false;

	constructor(storage: UrlPoolStorage, id: string) {
		this.valid_to = new Date();
		this.valid_to.setDate(this.valid_to.getDate() + 1);
		this.id = id;
		this.storage = storage;
	}

	async init() {

		let page = await axios.get("https://uloz.to/file/"+this.id, {validateStatus: null});
		if(page.status == 404) return false;
		// TODO: Add quick download support
		// let quick_dl_url = links.regex_parse(new RegExp('href="(/quickDownload/[^"]*)"'), page.data, 1);
		this.is_direct = 'js-free-download-button-direct' == links.regex_parse(new RegExp('data-href="/download-dialog/free/[^"]+" +class=".+(js-free-download-button-direct).+"'), page.data, 1);

		await this.generate();
		let url = await this.get();
		if(!url) throw "No available URL in pool!";
		let r = await axios.get(url[1], {
			responseType: 'arraybuffer',
			headers: {
				Range: `bytes=0-0`
			}
		});
		this.total_size = parseInt(r.headers["content-range"].split("/")[1]);
		this.return(url[0]);

		this.ready = true;
		return true;

	}

	get_raw(): [number, string] | undefined {
		for(let i=0; i<this.urls.length; i++) {
			if(this.used[i]) continue;
			this.used[i] = true;
			return [i, this.urls[i]];
		}
	}

	async get(): Promise<[number, string] | undefined> {
		let url = this.get_raw();
		if(url) return url;
		return new Promise((complete) => {
			let check = setInterval(() => {
				let url = this.get_raw();
				if(url) {
					clearInterval(check);
					complete(url);
				}
			}, 100);
		});
	}

	return(i: number, sucess: boolean = true) {
		setTimeout(() => this.used[i] = false, sucess ? random_int(1000, 5000) : random_int(5000, 60000));
	}

	add(url: string) {
		this.urls.push(url);
		this.used.push(false);
	}

	available(count?: number) {
		let available: number[] = [];
		for(let i=0; i<this.used.length; i++) {
			if(!this.used[i]) {
				available.push(i);
			}
		}
		return count ? available.slice(0, count) : available;
	}

	async start_generation() {
		this.generating = true;
		while(this.urls.length < 15 && this.generating) {
			console.log(this.id, "| new link:", (await this.generate()) ? true : false);
			await sleep(2000);
		}
		this.generating = false;
	}

	async generate(): Promise<string | undefined> {
		try {
			let link = await (this.is_direct ? links.direct_link(this.id) : links.captcha_link(this.id));
			if(link) this.add(link);
			return link;
		} catch(e) {
			console.log(e);
			this.generating = false;
			return undefined;
		}
	}

	get_downloader(from: number) {
		let d = new Downloader(this, from);
		this.downloaders.push(d);
		return d;
	}

	destroy() {
		this.downloaders.forEach(d => d.destroy());
		delete this.storage.pools[this.id];
	}

}

export class UrlPoolStorage {

	validity_interval: NodeJS.Timer;

	pools: {[U: string]: UrlPool} = {};

	constructor() {
		this.validity_interval = setInterval(this.validity_check, 1000*60*60);
	}

	validity_check() {
		for(let i in this.pools) {
			if(this.pools[i].valid_to.getTime() < new Date().getTime()) {
				this.pools[i].destroy();
			}
		}
	}

	async get(id: string): Promise<UrlPool | undefined> {
		return this.pools[id] ? this.pools[id] : await this.new(id);
	}

	async new(id: string): Promise<UrlPool | undefined> {
		let pool = new UrlPool(this, id);
		let check = await pool.init();
		if(!check) return undefined;
		pool.start_generation();
		this.pools[id] = pool;
		return pool;
	}

}