/*global require,console,URLSearchParams,__dirname*/
/*jshint -W083,-W061 */

// アプリケーション作成用のモジュールを読み込み
const {
	app,
	BrowserWindow,
	ipcMain,
	dialog,
	Notification
} = require('electron');
const pie = require("puppeteer-in-electron");
const puppeteer = require('puppeteer-extra');
const StealthPlugin = require('puppeteer-extra-plugin-stealth');
puppeteer.use(StealthPlugin());
const delay = require('delay');
const moment = require("moment-timezone");
const os = require('os');
const fs = require('fs').promises;
const path = require('path');
const http = require('http');
const {
	parseAsync
} = require('json2csv');
const Store = require('electron-store');
const store = new Store();
const kuromojin = require("kuromojin");
const analyze = require("negaposi-analyzer-ja");

let mainWindow;
let browser;
let page;

const main = async () => {
	await pie.initialize(app);
	browser = await pie.connect(app, puppeteer);
	mainWindow = new BrowserWindow({
		webPreferences: {
			nodeIntegration: true,
			contextIsolation: false
		},
		width: 600,
		height: 600,
	});
	mainWindow.loadFile('index.html');
	if(!app.isPackaged){
		mainWindow.webContents.openDevTools({ mode: 'undocked' });
	}
	mainWindow.on('closed', () => {
		app.quit();
	});
};

main();

ipcMain.handle('select-dir', async (event, arg) => {
	const result = await dialog.showOpenDialog(mainWindow, {
		properties: ['openDirectory', 'createDirectory']
	});
	return result.filePaths.length ? result.filePaths[0] : null;
});

ipcMain.handle("set", async (event, arg) => {
	store.set(arg.key, JSON.stringify(arg.value));
});

ipcMain.handle("get", async (event, arg) => {
	return store.has(arg.key) ? JSON.parse(store.get(arg.key)) : undefined;
});

ipcMain.handle('get-hash-cancel', async (event, arg) => {
	if (page && !page.isClosed()) await page.close();
});

ipcMain.handle('get-hash', async (event, arg) => {
	let code = arg.code.trim();
	let type = arg.type;
	let username = arg.username.trim();
	let password = arg.password.trim();
	let tagname = arg.tagname.trim();
	let range = arg.range;
	let since = arg.since;
	let until = arg.until;
	let directory = arg.directory;
	let outputs = arg.outputs;
	let delayMS = 1500;
	
	
	if (!type) {
		mainWindow.webContents.send('logger', 'ERROR type');
		return;
	}

	if (type === 'instagram' && !username) {
		mainWindow.webContents.send('logger', 'ERROR username');
		return;
	}

	if (type === 'instagram' && !password) {
		mainWindow.webContents.send('logger', 'ERROR password');
		return;
	}

	if (!tagname) {
		mainWindow.webContents.send('logger', 'ERROR tagname');
		return;
	}

	if (!directory || !await fs.stat(directory).catch(e => false)) {
		mainWindow.webContents.send('logger', 'ERROR directory');
		return;
	}

	if (!outputs.length) {
		mainWindow.webContents.send('logger', 'ERROR outputs');
		return;
	}
	
	if(since){
		range = 0;
	}
	
	if(app.isPackaged){
		if (!code) {
			mainWindow.webContents.send('logger', 'ERROR code');
			return;
		}

		let access = await new Promise(r=>{
			let ip = Object.values(os.networkInterfaces()).reduce((r, list) => r.concat(list.reduce((rr, i) => rr.concat(i.family === 'IPv4' && !i.internal && i.address || []), [])), [])[0];
			let arg2 = JSON.parse(JSON.stringify(arg));
			delete arg2.code;
			delete arg2.password;
			delete arg2.directory;
			let q = new URLSearchParams({
				'appcommand': 'setQueryHistory',
				'ip': ip,
				'code': code,
				'query': JSON.stringify(arg2),
			});

			http.get(`http://wpsystem01.xsrv.jp/hash/?${q.toString()}`,res=>{
				let data = ''; 
				res.on('data', (chunk) => { 
					data += chunk; 
				}); 
				res.on('end', () => { 
					try{
						data = JSON.parse(data);
						r(data.isSuccess);
					}catch(e){
						r(false);
					}
				}); 
			}).on("error", (err) => { 
				r(false);
			});
		});

		if (!access) {
			mainWindow.webContents.send('logger', 'ERROR code');
			return;
		}
	}
	

	mainWindow.webContents.send('lock');

	let start = moment();

	let window = new BrowserWindow({
		show: !app.isPackaged,
		backgroundThrottling:false
	});

	page = await pie.getPage(browser, window);

	await page.setRequestInterception(true);
	page.on('request', (request) => {
		if (['image', 'font', 'media'].indexOf(request.resourceType()) !== -1) {
			request.abort();
		} else {
			request.continue();
		}
	});
	
	let path_name = `${directory}/#${tagname}_${type}_${start.format('YYYYMMDDHHmmss')}`;
	let raws = [];
	let includesID = new Set();
	
	switch (type) {
		case 'instagram':
			mainWindow.webContents.send('logger', 'START LOGIN');
			const LOGIN_PAGE = 'https://www.instagram.com/accounts/login/';
			const LOGOUT_PAGE = 'https://www.instagram.com/accounts/logout/';
			let loginedUsername = null;
			do {
				try {
					// ログインページへCookie利用時にログイン済みの場合はログインページがスキップされる
					await page.goto(LOGIN_PAGE);
					if (page.url() === LOGIN_PAGE) {
						await page.waitForSelector('input[name="username"]', {
							timeout: 10000
						});
						await page.type('input[name="username"]', username);
						await page.type('input[name="password"]', password);
						await page.click('button[type="submit"]');
						await page.waitForSelector('main section:first-child button', {
							visible: true
						});
					}
					let body = await (await (await page.$('body')).getProperty('textContent')).jsonValue();
					loginedUsername = JSON.parse(body.split("window._sharedData = ")[1].split("};")[0] + "}").config.viewer.username;
					// 別のユーザー名でログインしていた場合一旦ログアウトさせる
					if (loginedUsername !== username) {
						await page.goto(LOGOUT_PAGE);
					}
				} catch (e) {
					break;
				}
			} while (loginedUsername !== username);

			if (!loginedUsername) {
				mainWindow.webContents.send('logger', 'ERROR LOGIN');
				mainWindow.webContents.send('unlock');
				return;
			}

			mainWindow.webContents.send('logger', 'SUCCESS LOGIN');

			let medias = [];
			let media_count = 0;
			raws = [];
			function getThumbnail(candidates, width = 320, height = 320) {
				return candidates.find(candidate => (candidate.width === width && candidate.height === height)).url;
			}

			let params = new URLSearchParams({
				'__a': 1
			});
			while (true) {
				let response;
				try {
					await page.goto(`https://www.instagram.com/explore/tags/${tagname}/?${params.toString()}`, {
						timeout: 10000
					});
					response = JSON.parse(await (await (await page.$('pre')).getProperty('textContent')).jsonValue());
				} catch (error) {
					// 制限食らった可能性大
					//mainWindow.webContents.send('logger', 'ERROR TAGS RESPONSE');
					console.log("制限食らった可能性大");
					break;
				}

				media_count = response.data.media_count;

				let responseMedias = [...response.data.recent.sections.map(section => section.layout_content.medias.map(media => media.media)).flat()];

				//日付指定の時、所得したメディアデータが全てsince以前の場合はここで終了
				if (since && responseMedias.every(media => moment.unix(media.taken_at).isBefore(since))) break;

				//mergeデータが存在する場合
//				if (merge) {
//					let ids = new Set(medias.map(media => media.id));
//					//所得したメディアデータが全て既に存在する場合はここで終了
//					if (responseMedias.every(media => ids.has(media.id))) {
//						break;
//					} else {
//						//既に存在するメディアデータは除去
//						responseMedias = responseMedias.filter(media => !ids.has(media.id));
//					}
//				}

				let oldestTime = moment().unix(); // 今回の取得した一番古い投稿の時間（進捗用）
				for (let media of responseMedias) {
					let date = moment.unix(media.taken_at);
					if ((!range || medias.length < range) &&
						(
							(!since && !until) ||
							((since && !until) && date.isSameOrAfter(since)) ||
							((!since && until) && date.isSameOrBefore(until)) ||
							((since && until) && date.isBetween(moment(since).startOf('day'), moment(until).endOf('day'), null, '[]'))
						)
					) {
						if(includesID.has(media.id)) continue;//追加済み
						includesID.add(media.id);
						medias.push({
							datetime: moment.unix(media.taken_at).format('YYYY-MM-DD HH:mm:ss'),
							taken_at: media.taken_at,
							id: media.id,
							code: media.code,
							username: media.user.username,
							full_name: media.user.full_name,
							profile_pic_url: media.user.profile_pic_url,
							comment_count: media.comment_count ? media.comment_count : 0,
							like_count: media.like_count ? media.like_count : 0,
							caption: media.caption ? media.caption.text.replace(/\r?\n/g, '\n').replace(/\ufeff/g, '') : '',
							media_type: media.media_type,
							original_width: (media.media_type !== 8) ? media.original_width : media.carousel_media[0].original_width,
							original_height: (media.media_type !== 8) ? media.original_height : media.carousel_media[0].original_height,
							image: (media.media_type !== 8) ? media.image_versions2.candidates[0].url : media.carousel_media[0].image_versions2.candidates[0].url,
							thumbnail: (media.media_type !== 8) ? getThumbnail(media.image_versions2.candidates) : getThumbnail(media.carousel_media[0].image_versions2.candidates),
							video: (media.media_type === 2) ? media.video_versions[0].url : null,
							video_duration: (media.media_type === 2) ? media.video_duration : null,
							view_count: (media.media_type === 2) ? media.view_count : null,
							carousel_media_count: (media.media_type === 8) ? media.carousel_media_count : null,
							
						});
//						raws.push(media);
						oldestTime = Math.min(oldestTime, media.taken_at);
					}
				}

				if ((range && medias.length === range) || !response.data.recent.next_page) break;

				params.set('max_id', response.data.recent.next_max_id);

				mainWindow.webContents.send('logger', medias.length + ' ' + moment.unix(oldestTime).format('YYYY-MM-DD HH:mm:ss'));

				await delay(delayMS);
			}

			mainWindow.webContents.send('logger', medias.length + ' ' + moment.unix(Math.min(...medias.map(media => media.taken_at))).format('YYYY-MM-DD HH:mm:ss'));

			if (medias.length) {
				medias.sort(function (a, b) {
					if (a.taken_at > b.taken_at) return -1;
					if (a.taken_at < b.taken_at) return 1;
					if (a.id > b.id) return -1;
					if (a.id < b.id) return 1;
					return 0;
				});
				let jsonString = JSON.stringify({
					version: 2,
					tag: tagname,
					start: start.format('YYYY-MM-DD HH:mm:ss'),
					range: range,
					since: since,
					until: until,
					media_count: media_count,
					medias: medias
				});
				if (outputs.includes('JSON')) {
					await fs.writeFile(`${path_name}.json`, jsonString);
				}
				if (outputs.includes('HTML')) {
					let html = await fs.readFile(path.join(__dirname, 'assets/template.html'), 'utf-8');
					html = html.replace(`await (await fetch('data.json')).json()`, jsonString);
					await fs.writeFile(`${path_name}.html`, html);
				}
				if (outputs.includes('CSV')) {
					await fs.writeFile(`${path_name}.csv`, await parseAsync(medias));
				}
				mainWindow.webContents.send('logger', 'SUCCESS EXPORT');
			} else {
				mainWindow.webContents.send('logger', 'ERRRO EXPORT => MEDIAS LENGTH 0');
			}
			if (page && !page.isClosed()) await page.close();
			mainWindow.webContents.send('unlock');
			break;
		case 'twitter':
			delayMS = 1000;
			let query = `#${tagname}`;
			if (since) query += ` since:${since}`;
			if (until) query += ` until:${until}`;
			let tweets = [];
			raws = [];
			let finish = async () => {
				if (page && !page.isClosed()) await page.close();
				if (tweets.length) {
					tweets.sort(function (a, b) {
						if (a.created_at > b.created_at) return -1;
						if (a.created_at < b.created_at) return 1;
						if (a.id > b.id) return -1;
						if (a.id < b.id) return 1;
						return 0;
					});
					let jsonString = JSON.stringify({
						version: 1,
						query: query,
						start: start.format('YYYY-MM-DD HH:mm:ss'),
						range: range,
						tweets: tweets
					});
					if (outputs.includes('JSON')) {
						await fs.writeFile(`${path_name}.json`, jsonString);
					}
					if (outputs.includes('HTML')) {
						let html = await fs.readFile(path.join(__dirname, 'assets/template-twitter.html'), 'utf-8');
						html = html.replace(`await (await fetch('data.json')).json()`, jsonString);
						await fs.writeFile(`${path_name}.html`, html);
					}
					if (outputs.includes('CSV')) {
						await fs.writeFile(`${path_name}.csv`, await parseAsync(tweets));
					}
//					for(let tweet of raws){
//						tweet.score = analyze(await kuromojin(tweet.full_text));
//					}
//					let fields = [
//						{
//							label: 'created_at',
//							value: (row) => moment(row.created_at, 'ddd MMM DD hh:mm:ss ZZ YYYY', 'en').format('YYYY-MM-DD HH:mm:ss')
//						},
//						{
//							label: 'id_str',
//     						value: 'id_str',
//						},
//						{
//							label: 'user',
//     						value: 'user.screen_name',
//						},
//						{
//							label: 'text',
//     						value: 'full_text'
//						},
//						{
//							label: 'score',
//							value: 'score',
//						}
//					];
//					await fs.writeFile(`${path_name}-raw.csv`,await parseAsync(raws,{fields:fields}));
//					await fs.writeFile(`${path_name}-raw.json`,JSON.stringify(raws));
					
					//new Notification({ title: '', body: NOTIFICATION_BODY }).show();
					mainWindow.webContents.send('logger', 'SUCCESS EXPORT');
				} else {
					mainWindow.webContents.send('logger', 'ERRRO EXPORT => TWEETS LENGTH 0');
				}
				mainWindow.webContents.send('unlock');
			};
			
			page.on('response', async (response) => {
				if (response.url().startsWith('https://twitter.com/i/api/2/search/adaptive.json')) {
					try{
						let json = await response.json();
						let ids = json.timeline.instructions[0].addEntries.entries.filter(entry=>!['sq-cursor-top','sq-cursor-bottom'].includes(entry.entryId)).map(entry=>entry.content.item.content.tweet.id);
						if (ids.length === 0) await finish();
						for (const id of ids) {
							if(includesID.has(id)) continue;
							includesID.add(id);
							let tweet = json.globalObjects.tweets[id];
							tweet.user = json.globalObjects.users[tweet.user_id_str];
							let media_types =[];
							let media_urls =[];
							if(tweet.entities.media){
								tweet.entities.media.forEach(media=>{
									media_types.push(media.type);
									media_urls.push(media.media_url_https);
								});
							}
							tweets.push({
								datetime: moment(tweet.created_at, 'ddd MMM DD hh:mm:ss ZZ YYYY', 'en').format('YYYY-MM-DD HH:mm:ss'),
								created_at: moment(tweet.created_at, 'ddd MMM DD hh:mm:ss ZZ YYYY', 'en').unix(),
								id: tweet.id,
								id_str: tweet.id_str,
								name: tweet.user.name,
								screen_name: tweet.user.screen_name,
								profile_image_url: tweet.user.profile_image_url_https, //u ser.profile_image_url
								favorite_count: tweet.favorite_count,
								retweet_count: tweet.retweet_count,
								reply_count: tweet.reply_count,
								quote_count: tweet.quote_count,
								full_text: tweet.full_text.replace(/\r?\n/g, '\n').replace(/\ufeff/g, ''),
								lang: tweet.lang,
								media_type: media_types.join(','),
								media_url: media_urls.join(','),
							});
							
							raws.push(tweet);
						}

						if (range && tweets.length >= range) {
							tweets.length = range;
							raws.length = range;
						}
						mainWindow.webContents.send('logger', tweets.length + ' ' + moment.unix(Math.min(...tweets.map(media => media.created_at))).format('YYYY-MM-DD HH:mm:ss'));
						if (range && tweets.length === range) {
							await finish();
						}
					}catch(e){
						await finish();
					}
				}
			});
			await page.goto(`https://twitter.com/search?q=${encodeURIComponent(query)}&src=typed_query&f=live`);
			while (page && !page.isClosed()) {
				try {
					await delay(delayMS);
					let previousHeight = await page.evaluate('document.body.scrollHeight');
					await page.evaluate('window.scrollTo(0, document.body.scrollHeight)');
					await page.waitForFunction(`document.body.scrollHeight > ${previousHeight}`);
				} catch (e) {
					await finish();
				}
			}
			break;
	}


});