use sark_core::http::codec::Parse;
use sark_core::http::codec::decode::BodyKind;

#[test]
fn scan_empty_headers() {
    let headers: &[httparse::Header<'_>] = &[];
    let s = Parse::header_scan(headers).unwrap();
    assert_eq!(s.content_length, None);
    assert!(!s.has_transfer_encoding);
    assert!(!s.is_chunked_transfer);
    assert!(!s.expect_continue);
    assert!(!s.duplicate_content_length);
}

#[test]
fn scan_single_content_length() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"42",
    }];
    let s = Parse::header_scan(headers).unwrap();
    assert_eq!(s.content_length, Some(42));
    assert!(!s.duplicate_content_length);
}

#[test]
fn scan_duplicate_content_length_same_value() {
    let headers = &[
        httparse::Header {
            name: "Content-Length",
            value: b"42",
        },
        httparse::Header {
            name: "Content-Length",
            value: b"42",
        },
    ];
    let s = Parse::header_scan(headers).unwrap();
    assert!(s.duplicate_content_length);
    assert_eq!(s.content_length, Some(42));
}

#[test]
fn scan_duplicate_content_length_different_values() {
    let headers = &[
        httparse::Header {
            name: "Content-Length",
            value: b"42",
        },
        httparse::Header {
            name: "Content-Length",
            value: b"99",
        },
    ];
    let s = Parse::header_scan(headers).unwrap();
    assert!(s.duplicate_content_length);
    assert_eq!(s.content_length, Some(99));
}

#[test]
fn scan_chunked_transfer_encoding() {
    let headers = &[httparse::Header {
        name: "Transfer-Encoding",
        value: b"chunked",
    }];
    let s = Parse::header_scan(headers).unwrap();
    assert!(s.has_transfer_encoding);
    assert!(s.is_chunked_transfer);
}

#[test]
fn scan_non_chunked_transfer_encoding() {
    let headers = &[httparse::Header {
        name: "Transfer-Encoding",
        value: b"gzip",
    }];
    let s = Parse::header_scan(headers).unwrap();
    assert!(s.has_transfer_encoding);
    assert!(!s.is_chunked_transfer);
}

#[test]
fn scan_expect_continue() {
    let headers = &[httparse::Header {
        name: "Expect",
        value: b"100-continue",
    }];
    let s = Parse::header_scan(headers).unwrap();
    assert!(s.expect_continue);
}

#[test]
fn scan_expect_non_continue() {
    let headers = &[httparse::Header {
        name: "Expect",
        value: b"some-other",
    }];
    let s = Parse::header_scan(headers).unwrap();
    assert!(!s.expect_continue);
}

#[test]
fn scan_content_length_non_utf8() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: &[0xFF, 0xFE],
    }];
    assert!(Parse::header_scan(headers).is_err());
}

#[test]
fn scan_content_length_non_numeric() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"abc",
    }];
    assert!(Parse::header_scan(headers).is_err());
}

#[test]
fn scan_content_length_negative() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"-5",
    }];
    assert!(Parse::header_scan(headers).is_err());
}

#[test]
fn scan_content_length_overflow() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"99999999999999999999999999999",
    }];
    assert!(Parse::header_scan(headers).is_err());
}

#[test]
fn scan_content_length_with_whitespace() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"  42  ",
    }];
    assert!(Parse::header_scan(headers).is_err());
}

#[test]
fn scan_content_length_empty_value() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"",
    }];
    assert!(Parse::header_scan(headers).is_err());
}

#[test]
fn scan_content_length_only_whitespace() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"   ",
    }];
    assert!(Parse::header_scan(headers).is_err());
}

#[test]
fn scan_empty_header_name_skipped() {
    let headers = &[
        httparse::Header {
            name: "",
            value: b"ignored",
        },
        httparse::Header {
            name: "Content-Length",
            value: b"5",
        },
    ];
    let s = Parse::header_scan(headers).unwrap();
    assert_eq!(s.content_length, Some(5));
}

#[test]
fn scan_case_insensitive_all_headers() {
    let headers = &[
        httparse::Header {
            name: "CONTENT-LENGTH",
            value: b"42",
        },
        httparse::Header {
            name: "TRANSFER-ENCODING",
            value: b"CHUNKED",
        },
        httparse::Header {
            name: "EXPECT",
            value: b"100-Continue",
        },
    ];
    let s = Parse::header_scan(headers).unwrap();
    assert_eq!(s.content_length, Some(42));
    assert!(s.has_transfer_encoding);
    assert!(s.is_chunked_transfer);
    assert!(s.expect_continue);
}

#[test]
fn scan_content_length_zero() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"0",
    }];
    let s = Parse::header_scan(headers).unwrap();
    assert_eq!(s.content_length, Some(0));
}

#[test]
fn scan_content_length_with_leading_zeros() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"0042",
    }];
    let s = Parse::header_scan(headers).unwrap();
    assert_eq!(s.content_length, Some(42));
}

#[test]
fn parse_cl_absent() {
    let headers: &[httparse::Header<'_>] = &[];
    assert_eq!(Parse::content_length(headers).unwrap(), None);
}

#[test]
fn parse_cl_valid() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"123",
    }];
    assert_eq!(Parse::content_length(headers).unwrap(), Some(123));
}

#[test]
fn parse_cl_zero() {
    let headers = &[httparse::Header {
        name: "content-length",
        value: b"0",
    }];
    assert_eq!(Parse::content_length(headers).unwrap(), Some(0));
}

#[test]
fn parse_cl_non_utf8() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: &[0xFF, 0xFE],
    }];
    assert!(Parse::content_length(headers).is_err());
}

#[test]
fn parse_cl_non_numeric() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"abc",
    }];
    assert!(Parse::content_length(headers).is_err());
}

#[test]
fn parse_cl_with_whitespace() {
    let headers = &[httparse::Header {
        name: "Content-Length",
        value: b"  42  ",
    }];
    assert!(Parse::content_length(headers).is_err());
}

#[test]
fn parse_cl_returns_first_match() {
    let headers = &[
        httparse::Header {
            name: "Content-Length",
            value: b"10",
        },
        httparse::Header {
            name: "Content-Length",
            value: b"20",
        },
    ];
    assert_eq!(Parse::content_length(headers).unwrap(), Some(10));
}

#[test]
fn parse_cl_skips_other_headers() {
    let headers = &[
        httparse::Header {
            name: "X-Custom",
            value: b"123",
        },
        httparse::Header {
            name: "Content-Length",
            value: b"77",
        },
    ];
    assert_eq!(Parse::content_length(headers).unwrap(), Some(77));
}

#[test]
fn is_chunked_true() {
    let headers = &[httparse::Header {
        name: "Transfer-Encoding",
        value: b"chunked",
    }];
    assert!(Parse::is_chunked(headers));
}

#[test]
fn is_chunked_case_insensitive() {
    let headers = &[httparse::Header {
        name: "transfer-encoding",
        value: b"CHUNKED",
    }];
    assert!(Parse::is_chunked(headers));
}

#[test]
fn is_chunked_false_gzip() {
    let headers = &[httparse::Header {
        name: "Transfer-Encoding",
        value: b"gzip",
    }];
    assert!(!Parse::is_chunked(headers));
}

#[test]
fn is_chunked_false_no_te() {
    let headers: &[httparse::Header<'_>] = &[];
    assert!(!Parse::is_chunked(headers));
}

#[test]
fn is_chunked_name_case_insensitive() {
    let headers = &[httparse::Header {
        name: "TRANSFER-ENCODING",
        value: b"chunked",
    }];
    assert!(Parse::is_chunked(headers));
}

#[test]
fn decode_head_200_content_length() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello";
    let head = Parse::head(raw, sark_core::http::codec::DecodeMode::Response)
        .unwrap()
        .unwrap();
    assert_eq!(head.status, http::StatusCode::OK);
    assert!(matches!(head.body_kind, BodyKind::ContentLength(5)));
    assert!(!head.headers.is_empty());
}

#[test]
fn decode_head_chunked() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n";
    let head = Parse::head(raw, sark_core::http::codec::DecodeMode::Response)
        .unwrap()
        .unwrap();
    assert!(matches!(head.body_kind, BodyKind::Chunked));
}

#[test]
fn decode_head_204_no_body() {
    let raw = b"HTTP/1.1 204 No Content\r\n\r\n";
    let head = Parse::head(raw, sark_core::http::codec::DecodeMode::Response)
        .unwrap()
        .unwrap();
    assert_eq!(head.status, http::StatusCode::NO_CONTENT);
    assert!(matches!(head.body_kind, BodyKind::NoBody));
}

#[test]
fn decode_head_304_no_body() {
    let raw = b"HTTP/1.1 304 Not Modified\r\n\r\n";
    let head = Parse::head(raw, sark_core::http::codec::DecodeMode::Response)
        .unwrap()
        .unwrap();
    assert_eq!(head.status, http::StatusCode::NOT_MODIFIED);
    assert!(matches!(head.body_kind, BodyKind::NoBody));
}

#[test]
fn decode_head_is_head_request() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello";
    let head = Parse::head(raw, sark_core::http::codec::DecodeMode::Head)
        .unwrap()
        .unwrap();
    assert!(matches!(head.body_kind, BodyKind::NoBody));
}

#[test]
fn decode_head_until_eof() {
    let raw = b"HTTP/1.1 200 OK\r\n\r\n";
    let head = Parse::head(raw, sark_core::http::codec::DecodeMode::Response)
        .unwrap()
        .unwrap();
    assert!(matches!(head.body_kind, BodyKind::UntilEof));
}

#[test]
fn decode_head_partial() {
    let raw = b"HTTP/1.1 200 OK\r\n";
    assert!(
        Parse::head(raw, sark_core::http::codec::DecodeMode::Response)
            .unwrap()
            .is_none()
    );
}

#[test]
fn decode_head_1xx_no_body() {
    let raw = b"HTTP/1.1 100 Continue\r\n\r\n";
    let head = Parse::head(raw, sark_core::http::codec::DecodeMode::Response)
        .unwrap()
        .unwrap();
    assert_eq!(head.status, http::StatusCode::CONTINUE);
    assert!(matches!(head.body_kind, BodyKind::NoBody));
}

#[test]
fn decode_head_header_len_correct() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello";
    let head = Parse::head(raw, sark_core::http::codec::DecodeMode::Response)
        .unwrap()
        .unwrap();
    assert_eq!(&raw[head.header_len..], b"hello");
}

#[test]
fn decode_head_empty_returns_none() {
    assert!(
        Parse::head(b"", sark_core::http::codec::DecodeMode::Response)
            .unwrap()
            .is_none()
    );
}

#[test]
fn chunked_body_single_chunk() {
    let data = b"5\r\nhello\r\n0\r\n\r\n";
    let body = Parse::chunked_body(data).unwrap().unwrap();
    assert_eq!(&body, b"hello");
}

#[test]
fn chunked_body_empty() {
    let data = b"0\r\n\r\n";
    let body = Parse::chunked_body(data).unwrap().unwrap();
    assert!(body.is_empty());
}

#[test]
fn chunked_body_multiple_chunks() {
    let data = b"3\r\nabc\r\n4\r\ndefg\r\n0\r\n\r\n";
    let body = Parse::chunked_body(data).unwrap().unwrap();
    assert_eq!(&body, b"abcdefg");
}

#[test]
fn chunked_body_partial_returns_none() {
    let data = b"5\r\nhel";
    assert!(Parse::chunked_body(data).unwrap().is_none());
}

#[test]
fn chunked_body_invalid_hex() {
    let data = b"xyz\r\nhello\r\n0\r\n\r\n";
    assert!(Parse::chunked_body(data).is_err());
}

#[test]
fn chunked_body_partial_terminator() {
    let data = b"5\r\nhello\r\n0\r\n";
    assert!(Parse::chunked_body(data).unwrap().is_none());
}

#[test]
fn chunked_body_oversized() {
    let data = b"FFFFFFFF\r\nhello\r\n0\r\n\r\n";
    assert!(Parse::chunked_body(data).is_err());
}

#[test]
fn chunked_body_hex_upper_and_lower() {
    let data = b"a\r\n0123456789\r\n0\r\n\r\n";
    let body = Parse::chunked_body(data).unwrap().unwrap();
    assert_eq!(&body, b"0123456789");

    let data = b"A\r\n0123456789\r\n0\r\n\r\n";
    let body = Parse::chunked_body(data).unwrap().unwrap();
    assert_eq!(&body, b"0123456789");
}

#[test]
fn chunked_body_with_extension() {
    let data = b"5;ext=val\r\nhello\r\n0\r\n\r\n";
    let body = Parse::chunked_body(data).unwrap().unwrap();
    assert_eq!(&body, b"hello");
}

#[test]
fn chunked_body_crlf_in_data() {
    let data = b"6\r\nab\r\ncd\r\n0\r\n\r\n";
    let body = Parse::chunked_body(data).unwrap().unwrap();
    assert_eq!(&body, b"ab\r\ncd");
}

#[test]
fn response_only_crlf() {
    let raw = b"\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err() || result.unwrap().is_none());
}

#[test]
fn response_all_whitespace() {
    let raw = b"    \r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err() || result.unwrap().is_none());
}

#[test]
fn response_lf_only_line_endings() {
    let raw = b"HTTP/1.1 200 OK\nContent-Length: 0\n\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    let _ = result;
}

#[test]
fn chunked_negative_size() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n-1\r\nX\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn chunked_empty_size_line() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n\r\nhello\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn status_code_000() {
    let raw = b"HTTP/1.1 000 Nothing\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn status_code_099() {
    let raw = b"HTTP/1.1 099 Low\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn status_code_600() {
    let raw = b"HTTP/1.1 600 Custom\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.status().as_u16(), 600);
}

#[test]
fn header_name_with_space() {
    let raw = b"HTTP/1.1 200 OK\r\nBad Name: value\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn response_binary_body() {
    let body: Vec<u8> = (0u8..=255).collect();
    let raw = format!("HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n", body.len());
    let mut buf = raw.into_bytes();
    buf.extend_from_slice(&body);

    let resp = Parse::response(&buf, sark_core::http::codec::DecodeMode::Response)
        .unwrap()
        .unwrap();
    assert_eq!(resp.body(), &body[..]);
}

#[test]
fn content_length_max_usize() {
    let max = usize::MAX.to_string();
    let raw = format!("HTTP/1.1 200 OK\r\nContent-Length: {}\r\n\r\n", max);
    let result = Parse::response(raw.as_bytes(), sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    assert!(result.unwrap().is_none());
}

#[test]
fn multiple_transfer_encoding_headers() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: gzip\r\nTransfer-Encoding: chunked\r\n\r\n5\r\nhello\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    if let Ok(Some(resp)) = result {
        assert_eq!(resp.body_str(), Some("hello"));
    }
}

#[test]
fn response_tab_in_header_value() {
    let raw = b"HTTP/1.1 200 OK\r\nX-Tab: val\tue\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.headers().get("x-tab").unwrap().as_bytes(), b"val\tue");
}

#[test]
fn content_length_with_plus_sign() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: +5\r\n\r\nhello";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn decode_after_eof_empty_buffer() {
    let result = Parse::response_after_eof(b"");
    assert!(result.is_err());
}

#[test]
fn decode_after_eof_just_status_line() {
    let result = Parse::response_after_eof(b"HTTP/1.1 200 OK\r\n\r\n");
    assert!(result.is_ok());
    let resp = result.unwrap();
    assert!(resp.body().is_empty());
}

#[test]
fn response_with_many_colons_in_header_value() {
    let raw = b"HTTP/1.1 200 OK\r\nX-Data: a:b:c:d:e\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.headers().get("x-data").unwrap(), "a:b:c:d:e");
}

#[test]
fn response_empty_reason_phrase() {
    let raw = b"HTTP/1.1 200 \r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(resp.status(), http::StatusCode::OK);
}

#[test]
fn chunked_with_trailing_whitespace_in_size() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked\r\n\r\n 5 \r\nhello\r\n0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn response_header_value_with_equals_semicolons() {
    let raw =
        b"HTTP/1.1 200 OK\r\nSet-Cookie: id=abc; Path=/; HttpOnly\r\nContent-Length: 0\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    let resp = result.unwrap().unwrap();
    assert_eq!(
        resp.headers().get("set-cookie").unwrap(),
        "id=abc; Path=/; HttpOnly"
    );
}

#[test]
fn decode_head_with_both_cl_and_te_chunked() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 100\r\nTransfer-Encoding: chunked\r\n\r\n";
    let result = Parse::head(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn response_with_conflicting_content_length_values_is_error() {
    let raw = b"HTTP/1.1 200 OK\r\nContent-Length: 5\r\nContent-Length: 7\r\n\r\nhello!!";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn response_with_chunked_not_last_transfer_coding_is_error() {
    let raw = b"HTTP/1.1 200 OK\r\nTransfer-Encoding: chunked, gzip\r\n\r\n";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_err());
}

#[test]
fn response_just_version() {
    let raw = b"HTTP/1.1";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    assert!(result.unwrap().is_none());
}

#[test]
fn response_incomplete_status_line() {
    let raw = b"HTTP/1.1 200";
    let result = Parse::response(raw, sark_core::http::codec::DecodeMode::Response);
    assert!(result.is_ok());
    assert!(result.unwrap().is_none());
}
