use super::Assessment;

use crate::{constants, settings::report::SquirrelSettings, tools::squirrel::{SquirrelResult, SquirrelMetrics}};

const SHARPENING: &str = "Compare the generated widefield error map with the HAWKMAN error map. If errors are in the same place then SQUIRREL is detecting sharpening. If they are in different places or on areas of weak sharpening then SQUIRREL is detecting under counting. This could be due to fine structure that has not been resolved, poor blinking, dim emitters, high local background or non-linearity.";
const MISSING_STRUCTURE: &str = "Missing structure due to high background / bleaching or labelling. Differences are due to non-blinking issues.";

fn metrics_ok(settings: &SquirrelSettings, metrics: &SquirrelMetrics) -> (bool, f64)
{
	let score = metrics.pearsons();
	let result = score >= settings.pearsons_threshold();
	(result, score)
}

fn test(settings: &SquirrelSettings, results: &SquirrelResult) -> Result<(bool, f64), String>
{
	results.metrics().map(|m| metrics_ok(settings, &m))
}

fn generate_assessment(generated_wf_result: Option<bool>, true_wf_result: Option<bool>) -> Assessment
{
	let generated_ok = generated_wf_result.unwrap_or(true);
	if !generated_ok // failing both is the same as if generated fails and true passes
	{
		return Assessment::fail_with(constants::SQUIRREL, SHARPENING);
	}

	let true_ok = true_wf_result.unwrap_or(true);
	if !true_ok && generated_ok
	{
		return Assessment::fail_with(constants::SQUIRREL, MISSING_STRUCTURE);
	}

	Assessment::pass_with(constants::SQUIRREL, "")
}

fn get_score(generated_score: Option<f64>, true_score: Option<f64>) -> f64
{
	true_score.unwrap_or_else(|| generated_score.unwrap())
}

pub fn assess(settings: &SquirrelSettings, generated_wf: Option<&SquirrelResult>, true_wf: Option<&SquirrelResult>) -> Result<Assessment, String>
{
	if generated_wf.is_none() && true_wf.is_none()
	{
		return Err("Both generated widefield and true widefield squirrel results are missing".to_string())
	}

	let generated_wf_ok = generated_wf.map(|s| test(settings, s)).transpose().map_err(|e| format!("Error creating metrics for generated widefield: {e}"))?;
	let true_wf_ok = true_wf.map(|s| test(settings, s)).transpose().map_err(|e| format!("Error creating metrics for true widefield: {e}"))?;
	
	let generated_score = generated_wf_ok.map(|(_, score)| score);
	let true_score = true_wf_ok.map(|(_, score)| score);
	let score = get_score(generated_score, true_score);

	let generated_ok = generated_wf_ok.map(|(ok, _)| ok);
	let true_ok = true_wf_ok.map(|(ok, _)| ok);
	let mut assessment = generate_assessment(generated_ok, true_ok);
	assessment.set_score(score);
	Ok(assessment)
}

#[cfg(test)]
mod tests 
{
	use super::*;

	// a,b,c = None, True, False -> 9 inputs

	#[test] // a, a
	fn missing_data_test() 
	{
		let expected = "Both generated widefield and true widefield squirrel results are missing";
		assert_eq!(assess(&SquirrelSettings::default(), None, None).unwrap_err(), expected)
	}

	#[test] // a, b
	fn no_generated_passing_true()
	{
		let generated_wf_result = None;
		let true_wf_result = Some(true);
		assert_eq!(generate_assessment(generated_wf_result, true_wf_result).message(), "");
	}

	#[test] // a, c
	fn  no_generated_failing_true()
	{
		let generated_wf_result = None;
		let true_wf_result = Some(false);
		assert_eq!(generate_assessment(generated_wf_result, true_wf_result).message(), MISSING_STRUCTURE);
	}

	#[test] // b, a
	fn passing_generated_no_true()
	{
		let generated_wf_result = Some(true);
		let true_wf_result = None;
		assert_eq!(generate_assessment(generated_wf_result, true_wf_result).message(), "");
	}

	#[test] // b, b
	fn passing_generated_passing_true()
	{
		let generated_wf_result = Some(true);
		let true_wf_result = Some(true);
		assert_eq!(generate_assessment(generated_wf_result, true_wf_result).message(), "");
	}

	#[test] // b, c
	fn passing_generated_failing_true()
	{
		let generated_wf_result = Some(true);
		let true_wf_result = Some(false);
		assert_eq!(generate_assessment(generated_wf_result, true_wf_result).message(), MISSING_STRUCTURE);
	}

	#[test] // c, a
	fn failing_generated_no_true()
	{
		let generated_wf_result = Some(false);
		let true_wf_result = None;
		assert_eq!(generate_assessment(generated_wf_result, true_wf_result).message(), SHARPENING);
	}

	#[test] // c, b
	fn failing_generated_passing_true()
	{
		let generated_wf_result = Some(false);
		let true_wf_result = Some(true);
		assert_eq!(generate_assessment(generated_wf_result, true_wf_result).message(), SHARPENING);
	}

	#[test] // c, c
	fn failing_generated_failing_true()
	{
		let generated_wf_result = Some(false);
		let true_wf_result = Some(false);
		assert_eq!(generate_assessment(generated_wf_result, true_wf_result).message(), SHARPENING);
	}

	#[test]
	fn failing_true_wf_failing_generated()
	{
		let generated_wf_result = Some(false);
		let true_wf_result = Some(false);
		assert_eq!(generate_assessment(generated_wf_result, true_wf_result).message(), SHARPENING);
	}

	#[test]
	fn metrics_ok_test()
	{
		let settings = SquirrelSettings::default();
		let metrics = SquirrelMetrics::new(0.0, 0.99);
		assert_eq!(metrics.pearsons(), 0.99);
		let (ok, score) = metrics_ok(&settings, &metrics);
		assert_eq!(ok, true);
		assert_eq!(score, 0.99);
	}

	#[test]
	fn metrics_fail_test()
	{
		let settings = SquirrelSettings::default();
		let metrics = SquirrelMetrics::new(0.99, 0.1);
		assert_eq!(metrics.pearsons(), 0.1);
		let (ok, score) = metrics_ok(&settings, &metrics);
		assert_eq!(ok, false);
		assert_eq!(score, 0.1);
	}

	#[test]
	fn take_true_wf_score_if_exists()
	{
		assert_eq!(get_score(None, Some(1.23)), 1.23);
		assert_eq!(get_score(Some(4.56), Some(1.23)), 1.23)
	}

	#[test]
	fn fall_back_to_generated_score()
	{
		assert_eq!(get_score(Some(1.23), None), 1.23)
	}
}