use std::{convert::TryInto, fmt::Display, ops::Deref};

use serde::{Deserialize, Serialize};

use crate::common::data::{HttpMockRequest, Mismatch, RequestRequirements, Tokenizer};

use crate::server::matchers::comparators::{
    AnyValueComparator, BytesExactMatchComparator, BytesIncludesComparator, BytesPrefixComparator,
    BytesSuffixComparator, FunctionMatchesRequestComparator, HostEqualsComparator,
    HttpMockBytesPatternComparator, JSONContainsMatchComparator, JSONExactMatchComparator,
    StringContainsComparator, StringEqualsComparator, StringPatternMatchComparator,
    StringPrefixMatchComparator, StringRegexMatchComparator, StringSuffixMatchComparator,
    U16ExactMatchComparator,
};

use crate::server::matchers::generic::{
    FunctionValueMatcher, KeyValueOperator, MatchingStrategy, MultiValueCountMatcher,
    MultiValueMatcher, SingleValueMatcher,
};

pub mod comparators;
mod comparison;
pub mod generic;
pub mod readers;

pub fn all() -> Vec<Box<dyn Matcher + Sync + Send>> {
    vec![
        //*************************************************************************************
        // Scheme matchers
        //*************************************************************************************
        Box::new(SingleValueMatcher {
            entity_name: "scheme",
            matcher_method: "scheme",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(StringEqualsComparator::new(false, false)),
            expectation: readers::expectations::scheme_equal_to,
            request_value: readers::request_value::scheme,
            diff_with: None,
            weight: 3,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "scheme",
            matcher_method: "scheme_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(StringEqualsComparator::new(false, true)),
            expectation: readers::expectations::scheme_not_equal_to,
            request_value: readers::request_value::scheme,
            diff_with: None,
            weight: 3,
        }),
        //*************************************************************************************
        // Method matchers
        //*************************************************************************************
        Box::new(SingleValueMatcher {
            entity_name: "method",
            matcher_method: "method",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(StringEqualsComparator::new(false, false)),
            expectation: readers::expectations::method_equal_to,
            request_value: readers::request_value::method,
            diff_with: None,
            weight: 3,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "method",
            matcher_method: "method_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(StringEqualsComparator::new(false, true)),
            expectation: readers::expectations::method_not_equal_to,
            request_value: readers::request_value::method,
            diff_with: None,
            weight: 3,
        }),
        //*************************************************************************************
        // Host matchers
        //*************************************************************************************
        Box::new(SingleValueMatcher {
            entity_name: "host",
            matcher_method: "host",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(HostEqualsComparator::new(false)),
            expectation: readers::expectations::host_equal_to,
            request_value: readers::request_value::host,
            diff_with: None,
            weight: 3,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "host",
            matcher_method: "host_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(HostEqualsComparator::new(true)),
            expectation: readers::expectations::host_not_equal_to,
            request_value: readers::request_value::host,
            diff_with: None,
            weight: 3,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "host",
            matcher_method: "host_includes",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(StringContainsComparator::new(false, false)),
            expectation: readers::expectations::host_includes,
            request_value: readers::request_value::host,
            diff_with: None,
            weight: 3,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "host",
            matcher_method: "host_excludes",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(StringContainsComparator::new(false, true)),
            expectation: readers::expectations::host_excludes,
            request_value: readers::request_value::host,
            diff_with: None,
            weight: 3,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "host",
            matcher_method: "host_prefix",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(StringPrefixMatchComparator::new(false, false)),
            expectation: readers::expectations::host_prefix,
            request_value: readers::request_value::host,
            diff_with: None,
            weight: 3,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "host",
            matcher_method: "host_prefix_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(StringPrefixMatchComparator::new(false, true)),
            expectation: readers::expectations::host_prefix_not,
            request_value: readers::request_value::host,
            diff_with: None,
            weight: 3,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "host",
            matcher_method: "host_suffix",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(StringSuffixMatchComparator::new(false, false)),
            expectation: readers::expectations::host_has_suffix,
            request_value: readers::request_value::host,
            diff_with: None,
            weight: 3,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "host",
            matcher_method: "host_suffix_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(StringSuffixMatchComparator::new(false, true)),
            expectation: readers::expectations::host_has_no_suffix,
            request_value: readers::request_value::host,
            diff_with: None,
            weight: 3,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "host",
            matcher_method: "host_matches",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            expectation: readers::expectations::host_matches_regex,
            request_value: readers::request_value::host,
            diff_with: None,
            weight: 3,
        }),
        //*************************************************************************************
        // Port matchers
        //*************************************************************************************
        Box::new(SingleValueMatcher {
            entity_name: "port",
            matcher_method: "port",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(U16ExactMatchComparator::new(false)),
            expectation: readers::expectations::port_equal_to,
            request_value: readers::request_value::port,
            diff_with: None,
            weight: 2,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "port",
            matcher_method: "port_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(U16ExactMatchComparator::new(true)),
            expectation: readers::expectations::port_not_equal_to,
            request_value: readers::request_value::port,
            diff_with: None,
            weight: 2,
        }),
        //*************************************************************************************
        // Path matchers
        //*************************************************************************************
        Box::new(SingleValueMatcher {
            entity_name: "path",
            matcher_method: "path",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(StringEqualsComparator::new(true, false)),
            expectation: readers::expectations::path_equal_to,
            request_value: readers::request_value::path,
            diff_with: None,
            weight: 10,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "path",
            matcher_method: "path_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(StringEqualsComparator::new(true, true)),
            expectation: readers::expectations::path_not_equal_to,
            request_value: readers::request_value::path,
            diff_with: None,
            weight: 10,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "path",
            matcher_method: "path_includes",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(StringContainsComparator::new(true, false)),
            expectation: readers::expectations::path_includes,
            request_value: readers::request_value::path,
            diff_with: None,
            weight: 10,
        }),
        // path excludes
        Box::new(SingleValueMatcher {
            entity_name: "path",
            matcher_method: "path_excludes",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(StringContainsComparator::new(true, true)),
            expectation: readers::expectations::path_excludes,
            request_value: readers::request_value::path,
            diff_with: None,
            weight: 10,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "path",
            matcher_method: "path_prefix",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(StringPrefixMatchComparator::new(true, false)),
            expectation: readers::expectations::path_prefix,
            request_value: readers::request_value::path,
            diff_with: None,
            weight: 10,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "path",
            matcher_method: "path_prefix_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(StringPrefixMatchComparator::new(true, true)),
            expectation: readers::expectations::path_prefix_not,
            request_value: readers::request_value::path,
            diff_with: None,
            weight: 10,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "path",
            matcher_method: "path_suffix",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(StringSuffixMatchComparator::new(true, false)),
            expectation: readers::expectations::path_suffix,
            request_value: readers::request_value::path,
            diff_with: None,
            weight: 10,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "path",
            matcher_method: "path_suffix_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(StringSuffixMatchComparator::new(true, true)),
            expectation: readers::expectations::path_suffix_not,
            request_value: readers::request_value::path,
            diff_with: None,
            weight: 10,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "path",
            matcher_method: "path_matches",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(StringRegexMatchComparator::new()),
            expectation: readers::expectations::path_matches,
            request_value: readers::request_value::path,
            diff_with: None,
            weight: 10,
        }),
        //*************************************************************************************
        // Query param matchers
        //*************************************************************************************
        Box::new(MultiValueMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param",
            matching_strategy: MatchingStrategy::Presence,
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::query_param,
            request_value: readers::request_value::query_params,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringEqualsComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param_not",
            matching_strategy: MatchingStrategy::Absence,
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::query_param_not,
            request_value: readers::request_value::query_params,
            key_required: true,
            // Key is not negated, since we expect a query parameter to be present with the expected key.
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringEqualsComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param_exists",
            matching_strategy: MatchingStrategy::Presence,
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::query_param_exists,
            request_value: readers::request_value::query_params,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(AnyValueComparator::new()),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param_missing",
            matching_strategy: MatchingStrategy::Absence,
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::query_param_missing,
            request_value: readers::request_value::query_params,
            key_required: false,
            key_comparator: Box::new(StringEqualsComparator::new(true, true)),
            value_comparator: Box::new(AnyValueComparator::new()),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param_includes",
            matching_strategy: MatchingStrategy::Presence,
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::query_param_includes,
            request_value: readers::request_value::query_params,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringContainsComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param_excludes",
            matching_strategy: MatchingStrategy::Absence,
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::query_param_excludes,
            request_value: readers::request_value::query_params,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringContainsComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param_prefix",
            matching_strategy: MatchingStrategy::Presence,
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::query_param_prefix,
            request_value: readers::request_value::query_params,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringPrefixMatchComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param_prefix_not",
            matching_strategy: MatchingStrategy::Absence,
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::query_param_prefix_not,
            request_value: readers::request_value::query_params,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringPrefixMatchComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param_suffix",
            matching_strategy: MatchingStrategy::Presence,
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::query_param_suffix,
            request_value: readers::request_value::query_params,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringSuffixMatchComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param_suffix_not",
            matching_strategy: MatchingStrategy::Absence,
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::query_param_suffix_not,
            request_value: readers::request_value::query_params,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringSuffixMatchComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param_matches",
            matching_strategy: MatchingStrategy::Presence,
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::query_param_matches,
            request_value: readers::request_value::query_params,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            value_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            weight: 1,
        }),
        Box::new(MultiValueCountMatcher {
            entity_name: "query parameter",
            matcher_method: "query_param_count",
            expectation: readers::expectations::query_param_count,
            request_value: readers::request_value::query_params,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            value_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            weight: 1,
        }),
        //************************************************************************************
        // Header matchers
        //************************************************************************************
        Box::new(MultiValueMatcher {
            entity_name: "header",
            matcher_method: "header",
            matching_strategy: MatchingStrategy::Presence,
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::header,
            request_value: readers::request_value::headers,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(StringEqualsComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "header",
            matcher_method: "header_not",
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::header_not,
            request_value: readers::request_value::headers,
            matching_strategy: MatchingStrategy::Absence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(StringEqualsComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "header",
            matcher_method: "header_exists",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::header_exists,
            request_value: readers::request_value::headers,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(AnyValueComparator::new()),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "header",
            matcher_method: "header_missing",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::header_missing,
            request_value: readers::request_value::headers,
            matching_strategy: MatchingStrategy::Absence,
            key_required: false,
            key_comparator: Box::new(StringEqualsComparator::new(false, true)),
            value_comparator: Box::new(AnyValueComparator::new()),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "header",
            matcher_method: "header_includes",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::header_includes,
            request_value: readers::request_value::headers,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(StringContainsComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "header",
            matcher_method: "header_excludes",
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::header_excludes,
            request_value: readers::request_value::headers,
            matching_strategy: MatchingStrategy::Absence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(StringContainsComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "header",
            matcher_method: "header_prefix",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::header_prefix,
            request_value: readers::request_value::headers,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(StringPrefixMatchComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "header",
            matcher_method: "header_prefix_not",
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::header_prefix_not,
            request_value: readers::request_value::headers,
            matching_strategy: MatchingStrategy::Absence,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(StringPrefixMatchComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "header",
            matcher_method: "header_suffix",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::header_suffix,
            request_value: readers::request_value::headers,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(StringSuffixMatchComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "header",
            matcher_method: "header_suffix_not",
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::header_suffix_not,
            request_value: readers::request_value::headers,
            matching_strategy: MatchingStrategy::Absence,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(StringSuffixMatchComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "header",
            matcher_method: "header_matches",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::header_matches,
            request_value: readers::request_value::headers,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringPatternMatchComparator::new(false, false)),
            value_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            weight: 1,
        }),
        Box::new(MultiValueCountMatcher {
            entity_name: "header",
            matcher_method: "header_count",
            expectation: readers::expectations::header_count,
            request_value: readers::request_value::headers,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringPatternMatchComparator::new(false, false)),
            value_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            weight: 1,
        }),
        // ***********************************************************************************
        // Cookie matchers
        // ***********************************************************************************
        #[cfg(feature = "cookies")]
        Box::new(MultiValueMatcher {
            entity_name: "cookie",
            matcher_method: "cookie",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::cookie,
            request_value: readers::request_value::cookies,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringEqualsComparator::new(true, false)),
            weight: 1,
        }),
        #[cfg(feature = "cookies")]
        Box::new(MultiValueMatcher {
            entity_name: "cookie",
            matcher_method: "cookie_not",
            matching_strategy: MatchingStrategy::Absence,
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::cookie_not,
            request_value: readers::request_value::cookies,
            key_required: true,
            // Key is not negated, since we expect a query parameter to be present with the expected key.
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringEqualsComparator::new(true, true)),
            weight: 1,
        }),
        #[cfg(feature = "cookies")]
        Box::new(MultiValueMatcher {
            entity_name: "cookie",
            matcher_method: "cookie_exists",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::cookie_exists,
            request_value: readers::request_value::cookies,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(AnyValueComparator::new()),
            weight: 1,
        }),
        #[cfg(feature = "cookies")]
        Box::new(MultiValueMatcher {
            entity_name: "cookie",
            matcher_method: "cookie_missing",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::cookie_missing,
            request_value: readers::request_value::cookies,
            matching_strategy: MatchingStrategy::Absence,
            key_required: false,
            key_comparator: Box::new(StringEqualsComparator::new(true, true)),
            value_comparator: Box::new(AnyValueComparator::new()),
            weight: 1,
        }),
        #[cfg(feature = "cookies")]
        Box::new(MultiValueMatcher {
            entity_name: "cookie",
            matcher_method: "cookie_includes",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::cookie_includes,
            request_value: readers::request_value::cookies,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringContainsComparator::new(true, false)),
            weight: 1,
        }),
        #[cfg(feature = "cookies")]
        Box::new(MultiValueMatcher {
            entity_name: "cookie",
            matcher_method: "cookie_excludes",
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::cookie_excludes,
            request_value: readers::request_value::cookies,
            matching_strategy: MatchingStrategy::Absence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringContainsComparator::new(true, true)),
            weight: 1,
        }),
        #[cfg(feature = "cookies")]
        Box::new(MultiValueMatcher {
            entity_name: "cookie",
            matcher_method: "cookie_prefix",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::cookie_prefix,
            request_value: readers::request_value::cookies,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringPrefixMatchComparator::new(true, false)),
            weight: 1,
        }),
        #[cfg(feature = "cookies")]
        Box::new(MultiValueMatcher {
            entity_name: "cookie",
            matcher_method: "cookie_prefix_not",
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::cookie_prefix_not,
            request_value: readers::request_value::cookies,
            matching_strategy: MatchingStrategy::Absence,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(StringPrefixMatchComparator::new(true, true)),
            weight: 1,
        }),
        #[cfg(feature = "cookies")]
        Box::new(MultiValueMatcher {
            entity_name: "cookie",
            matcher_method: "cookie_suffix",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::cookie_suffix,
            request_value: readers::request_value::cookies,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringSuffixMatchComparator::new(true, false)),
            weight: 1,
        }),
        #[cfg(feature = "cookies")]
        Box::new(MultiValueMatcher {
            entity_name: "cookie",
            matcher_method: "cookie_suffix_not",
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::cookie_suffix_not,
            request_value: readers::request_value::cookies,
            matching_strategy: MatchingStrategy::Absence,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(StringSuffixMatchComparator::new(true, true)),
            weight: 1,
        }),
        #[cfg(feature = "cookies")]
        Box::new(MultiValueMatcher {
            entity_name: "cookie",
            matcher_method: "cookie_matches",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::cookie_matches,
            request_value: readers::request_value::cookies,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            value_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            weight: 1,
        }),
        #[cfg(feature = "cookies")]
        Box::new(MultiValueCountMatcher {
            entity_name: "cookie",
            matcher_method: "cookie_count",
            expectation: readers::expectations::cookie_count,
            request_value: readers::request_value::cookies,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            value_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            weight: 1,
        }),
        // ************************************************************************************
        // Body matchers
        // ************************************************************************************
        Box::new(SingleValueMatcher {
            entity_name: "body",
            matcher_method: "body",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(BytesExactMatchComparator::new(false)),
            expectation: readers::expectations::body,
            request_value: readers::request_value::body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "body",
            matcher_method: "body_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(BytesExactMatchComparator::new(true)),
            expectation: readers::expectations::body_not,
            request_value: readers::request_value::body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "body",
            matcher_method: "body_includes",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(BytesIncludesComparator::new(false)),
            expectation: readers::expectations::body_includes,
            request_value: readers::request_value::body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "body",
            matcher_method: "body_excludes",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(BytesIncludesComparator::new(true)),
            expectation: readers::expectations::body_excludes,
            request_value: readers::request_value::body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "body",
            matcher_method: "body_prefix",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(BytesPrefixComparator::new(false)),
            expectation: readers::expectations::body_prefix,
            request_value: readers::request_value::body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "body",
            matcher_method: "body_prefix_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(BytesPrefixComparator::new(true)),
            expectation: readers::expectations::body_prefix_not,
            request_value: readers::request_value::body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "body",
            matcher_method: "body_suffix",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(BytesSuffixComparator::new(false)),
            expectation: readers::expectations::body_suffix,
            request_value: readers::request_value::body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "body",
            matcher_method: "body_suffix_not",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(BytesSuffixComparator::new(true)),
            expectation: readers::expectations::body_suffix_not,
            request_value: readers::request_value::body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "body",
            matcher_method: "body_matches",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(HttpMockBytesPatternComparator::new()),
            expectation: readers::expectations::body_matches,
            request_value: readers::request_value::body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        //************************************************************************************
        // JSON body matchers
        //************************************************************************************
        Box::new(SingleValueMatcher {
            entity_name: "body",
            matcher_method: "json_body",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(JSONExactMatchComparator::new()),
            expectation: readers::expectations::json_body,
            request_value: readers::request_value::json_body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "JSON body",
            matcher_method: "json_body_includes",
            matching_strategy: MatchingStrategy::Presence,
            comparator: Box::new(JSONContainsMatchComparator::new(false)),
            expectation: readers::expectations::json_body_includes,
            request_value: readers::request_value::json_body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        Box::new(SingleValueMatcher {
            entity_name: "JSON body",
            matcher_method: "json_body_excludes",
            matching_strategy: MatchingStrategy::Absence,
            comparator: Box::new(JSONContainsMatchComparator::new(true)),
            expectation: readers::expectations::json_body_excludes,
            request_value: readers::request_value::json_body,
            diff_with: Some(Tokenizer::Line),
            weight: 1,
        }),
        Box::new(FunctionValueMatcher {
            entity_name: "custom matcher function",
            matcher_function: "is_true",
            comparator: Box::new(FunctionMatchesRequestComparator::new(false)),
            expectation: readers::expectations::is_true,
            request_value: readers::request_value::full_request,
            weight: 1,
        }),
        Box::new(FunctionValueMatcher {
            entity_name: "custom matcher function",
            matcher_function: "is_false",
            comparator: Box::new(FunctionMatchesRequestComparator::new(true)),
            expectation: readers::expectations::is_false,
            request_value: readers::request_value::full_request,
            weight: 1,
        }),
        //*************************************************************************************
        // x-www-form-urlencoded body
        //*************************************************************************************
        Box::new(MultiValueMatcher {
            entity_name: "form-urlencoded body",
            matcher_method: "form_urlencoded_tuple",
            expectation: readers::expectations::form_urlencoded_tuple,
            request_value: readers::request_value::form_urlencoded_body,
            operator: KeyValueOperator::AND,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringEqualsComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "form-urlencoded body",
            matcher_method: "form_urlencoded_tuple_not",
            matching_strategy: MatchingStrategy::Absence,
            operator: KeyValueOperator::IMPLICATION,
            expectation: readers::expectations::form_urlencoded_tuple_not,
            request_value: readers::request_value::form_urlencoded_body,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringEqualsComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "form-urlencoded body",
            matcher_method: "form_urlencoded_tuple_exists",
            expectation: readers::expectations::form_urlencoded_key_exists,
            request_value: readers::request_value::form_urlencoded_body,
            operator: KeyValueOperator::AND,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(AnyValueComparator::new()),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "form-urlencoded body",
            matcher_method: "form_urlencoded_tuple_missing",
            expectation: readers::expectations::form_urlencoded_key_missing,
            request_value: readers::request_value::form_urlencoded_body,
            operator: KeyValueOperator::AND,
            matching_strategy: MatchingStrategy::Absence,
            key_required: false,
            key_comparator: Box::new(StringEqualsComparator::new(true, true)),
            value_comparator: Box::new(AnyValueComparator::new()),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "form-urlencoded body",
            matcher_method: "form_urlencoded_tuple_includes",
            expectation: readers::expectations::form_urlencoded_includes,
            request_value: readers::request_value::form_urlencoded_body,
            operator: KeyValueOperator::AND,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringContainsComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "form-urlencoded body",
            // TODO: Make text more understandable for the user (what excludes ? value? key?)
            matcher_method: "form_urlencoded_tuple_excludes",
            expectation: readers::expectations::form_urlencoded_excludes,
            request_value: readers::request_value::form_urlencoded_body,
            operator: KeyValueOperator::IMPLICATION,
            matching_strategy: MatchingStrategy::Absence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringContainsComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "form-urlencoded body",
            matcher_method: "form_urlencoded_tuple_prefix",
            expectation: readers::expectations::form_urlencoded_prefix,
            request_value: readers::request_value::form_urlencoded_body,
            operator: KeyValueOperator::AND,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringPrefixMatchComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "form-urlencoded body",
            matcher_method: "form_urlencoded_tuple_prefix_not",
            expectation: readers::expectations::form_urlencoded_prefix_not,
            request_value: readers::request_value::form_urlencoded_body,
            operator: KeyValueOperator::IMPLICATION,
            matching_strategy: MatchingStrategy::Absence,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringEqualsComparator::new(false, false)),
            value_comparator: Box::new(StringPrefixMatchComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "form-urlencoded body",
            matcher_method: "form_urlencoded_tuple_suffix",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::form_urlencoded_suffix,
            request_value: readers::request_value::form_urlencoded_body,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringSuffixMatchComparator::new(true, false)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "form-urlencoded body",
            matcher_method: "form_urlencoded_tuple_suffix_not",
            expectation: readers::expectations::form_urlencoded_suffix_not,
            request_value: readers::request_value::form_urlencoded_body,
            operator: KeyValueOperator::IMPLICATION,
            matching_strategy: MatchingStrategy::Absence,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringEqualsComparator::new(true, false)),
            value_comparator: Box::new(StringSuffixMatchComparator::new(true, true)),
            weight: 1,
        }),
        Box::new(MultiValueMatcher {
            entity_name: "form-urlencoded body",
            matcher_method: "form_urlencoded_tuple_matches",
            operator: KeyValueOperator::AND,
            expectation: readers::expectations::form_urlencoded_matches,
            request_value: readers::request_value::form_urlencoded_body,
            matching_strategy: MatchingStrategy::Presence,
            key_required: true,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            value_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            weight: 1,
        }),
        Box::new(MultiValueCountMatcher {
            entity_name: "form-urlencoded body",
            matcher_method: "form_urlencoded_tuple_count",
            expectation: readers::expectations::form_urlencoded_key_value_count,
            request_value: readers::request_value::form_urlencoded_body,
            // TODO: ATTENTION: still false, because it is expected that the key appears in the request!
            key_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            value_comparator: Box::new(StringPatternMatchComparator::new(false, true)),
            weight: 1,
        }),
    ]
}

pub trait Matcher {
    fn matches(&self, req: &HttpMockRequest, mock: &RequestRequirements) -> bool;
    fn distance(&self, req: &HttpMockRequest, mock: &RequestRequirements) -> usize;
    fn mismatches(&self, req: &HttpMockRequest, mock: &RequestRequirements) -> Vec<Mismatch>;
}
