use crate::inspectors::mime_extension_lookup;
use crate::mime_db::*;
use crate::{extension_from_path, init_db, scan_directory, scan_from_walkdir};

use crate::parameters::{Parameters, ScanOpts};
use cfg_if::cfg_if;
use mime_guess::mime::{APPLICATION_OCTET_STREAM, APPLICATION_PDF, IMAGE_JPEG, IMAGE_PNG};
use mime_guess::Mime;
use smartstring::alias::String;
use std::collections::HashMap;
use std::path::Path;

const JPEG_BYTES: &[u8] = b"\xFF\xD8\xFF";
const PNG_BYTES: &[u8] = b"\x89\x50\x4E\x47\x0D\x0A\x1A\x0A";
const PDF_BYTES: &[u8] = b"%PDF-";
const ZIP_BYTES: &[u8] = b"PK\x03\x04";

cfg_if! {
	if #[cfg(any(all(unix, feature = "infer-backend"), all(not(unix), not(feature = "xdg-mime-backend"))))] {
		fn get_mime_db() -> InferDb {
			InferDb::init()
		}
	} else {
		fn get_mime_db() -> XdgDb {
			XdgDb::init()
		}
	}
}

fn application_zip() -> Mime {
	use std::str::FromStr;
	Mime::from_str("application/zip").unwrap()
}

#[test]
fn get_ext() {
	let mut ext_checks = HashMap::new();
	ext_checks.insert(Path::new("test.txt"), Some(String::from("txt")));
	ext_checks.insert(Path::new("test.zip"), Some(String::from("zip")));
	ext_checks.insert(Path::new("test.tar.gz"), Some(String::from("gz")));
	ext_checks.insert(Path::new("test."), Some(String::from("")));
	ext_checks.insert(Path::new("test"), None);
	ext_checks.insert(Path::new(".hidden"), None);

	for (path, ext) in ext_checks {
		assert_eq!(extension_from_path(path), ext)
	}
}

#[test]
fn detect_type() {
	let db = get_mime_db();
	assert_eq!(db.get_type(JPEG_BYTES), Some(IMAGE_JPEG));
	assert_eq!(db.get_type(PNG_BYTES), Some(IMAGE_PNG));
	assert_eq!(db.get_type(PDF_BYTES), Some(APPLICATION_PDF));
	assert_eq!(db.get_type(ZIP_BYTES), Some(application_zip()));
}

#[test]
fn recommend_ext() {
	assert!(mime_extension_lookup(IMAGE_JPEG)
		.unwrap()
		.contains(&String::from("jpg")));
	assert!(mime_extension_lookup(IMAGE_PNG).unwrap().contains(&String::from("png")));
	assert!(mime_extension_lookup(APPLICATION_PDF)
		.unwrap()
		.contains(&String::from("pdf")));
	assert!(mime_extension_lookup(application_zip())
		.unwrap()
		.contains(&String::from("zip")));
}

#[test]
fn simple_directory() {
	use std::fs::File;
	use std::io::Write;
	use tempfile::tempdir;

	let mut files = HashMap::new();
	files.insert("test.jpg", JPEG_BYTES);
	files.insert("test.jpeg", JPEG_BYTES);
	files.insert("test.png", PNG_BYTES);
	files.insert("test.pdf", PDF_BYTES);
	files.insert("test.zip", ZIP_BYTES);
	files.insert("wrong.jpg", PNG_BYTES);

	let dir = tempdir().expect("Failed to create temporary directory.");

	for (name, bytes) in &files {
		let mut file = File::create(dir.path().join(name)).expect(&*format!("Failed to create file: {}", name));

		file
			.write_all(bytes)
			.expect(&*format!("Failed to write to file: {}", name));
		drop(file);
	}

	let scan_opts = ScanOpts{
		hidden: true,
		extensionless: false
	};

	let entries = scan_directory(
		&dir.path().to_path_buf(),
		&vec!["jpg", "jpeg", "png", "pdf", "zip"],
		&scan_opts,
	)
	.expect("Directory scan failed.");

	assert_eq!(entries.len(), files.len());

	// initialise global mime DB
	init_db();

	let results = scan_from_walkdir(&entries);
	for result in results {
		let result = result.expect("Error while scanning file");

		if !result.valid {
			// this should be "wrong.jpg", which is a misnamed png file
			// 1. ensure extension is "png"
			assert_eq!(extension_from_path(&*result.file).unwrap(), String::from("jpg"));
			// 2. ensure mime type detected is IMAGE_PNG
			assert_eq!(result.mime, IMAGE_PNG);
			// 3. ensure recommended extension is in the list of known extensions for PNG files
			assert!(mime_extension_lookup(IMAGE_PNG)
				.unwrap()
				.contains(&result.recommended_extension().unwrap()));
			continue;
		}

		// check if the recommended extension for this file is in the list of known extensions for its mimetype
		assert!(mime_extension_lookup(result.mime.clone())
			.unwrap()
			.contains(&result.recommended_extension().unwrap()));

		// make sure the guessed mimetype is correct based on the extension of the scanned file
		assert_eq!(
			result.mime,
			match extension_from_path(&*result.file).as_deref() {
				Some("jpg") | Some("jpeg") => IMAGE_JPEG,
				Some("png") => IMAGE_PNG,
				Some("pdf") => APPLICATION_PDF,
				Some("zip") => application_zip(),
				Some(_) | None => APPLICATION_OCTET_STREAM, // general "fallback" type
			}
		);
	}
}

#[test]
fn argument_parsing() {
	use clap::Clap;

	// check if "jpg" is in the list of extensions to be considered when passing "-E images"
	let args: Parameters = Parameters::parse_from(vec!["fif", "-E", "images"]);
	assert!(args.extensions().contains(&"jpg"));

	// make sure "scan_hidden" is false
	assert!(!args.scan_hidden);

	// exts should be none
	assert!(args.exts.is_none());
}
