//
//  CLIServerServiceTests.swift
//  oAITests
//
// SPDX-License-Identifier: PolyForm-Noncommercial-1.0.0
// Copyright (C) 2026 Rune Olsen

import Testing
import Foundation
@testable import Confab

@Suite("CLIServerService pure HTTP helpers")
struct CLIServerServiceTests {

    // MARK: - parseRequestBody

    @Test("Returns the body once headers and full Content-Length body have arrived")
    func parsesCompleteRequest() {
        let body = "{\"prompt\":\"hi\"}"
        let raw = "POST / HTTP/1.1\r\nHost: localhost\r\nContent-Length: \(body.utf8.count)\r\n\r\n\(body)"
        let result = CLIServerService.parseRequestBody(from: Data(raw.utf8))
        #expect(result.map { String(data: $0, encoding: .utf8) } == body)
    }

    @Test("Returns nil when the header block hasn't fully arrived yet")
    func returnsNilForIncompleteHeaders() {
        let raw = "POST / HTTP/1.1\r\nHost: localhost\r\nContent-Length: 5"
        #expect(CLIServerService.parseRequestBody(from: Data(raw.utf8)) == nil)
    }

    @Test("Returns nil when the body hasn't fully arrived yet")
    func returnsNilForIncompleteBody() {
        let raw = "POST / HTTP/1.1\r\nContent-Length: 20\r\n\r\n{\"prompt\":\"hi\"}"
        #expect(CLIServerService.parseRequestBody(from: Data(raw.utf8)) == nil)
    }

    @Test("Content-Length header name match is case-insensitive")
    func headerNameIsCaseInsensitive() {
        let body = "abc"
        let raw = "POST / HTTP/1.1\r\ncontent-length: 3\r\n\r\n\(body)"
        let result = CLIServerService.parseRequestBody(from: Data(raw.utf8))
        #expect(result.map { String(data: $0, encoding: .utf8) } == body)
    }

    @Test("Missing Content-Length is treated as a zero-length body")
    func missingContentLengthIsEmptyBody() {
        let raw = "GET / HTTP/1.1\r\nHost: localhost\r\n\r\n"
        let result = CLIServerService.parseRequestBody(from: Data(raw.utf8))
        #expect(result?.isEmpty == true)
    }

    @Test("Extra trailing bytes beyond Content-Length don't prevent extraction (pipelined data ignored)")
    func extraTrailingBytesStillExtractsBody() {
        let body = "abc"
        let raw = "POST / HTTP/1.1\r\nContent-Length: 3\r\n\r\n\(body)EXTRA"
        let result = CLIServerService.parseRequestBody(from: Data(raw.utf8))
        #expect(result.map { String(data: $0, encoding: .utf8) } == body)
    }

    // MARK: - Response encoding

    @Test("successResponse produces a 200 with the response field set")
    func successResponseShape() throws {
        let data = CLIServerService.successResponse("hello world")
        let text = String(data: data, encoding: .utf8)!
        #expect(text.hasPrefix("HTTP/1.1 200 OK\r\n"))
        #expect(text.contains("Content-Type: application/json"))

        let bodyStart = text.range(of: "\r\n\r\n")!.upperBound
        let bodyJSON = Data(text[bodyStart...].utf8)
        let decoded = try JSONDecoder().decode(CLIServerService.AskResponseBody.self, from: bodyJSON)
        #expect(decoded.response == "hello world")
        #expect(decoded.error == nil)
    }

    @Test("errorResponse defaults to 400 Bad Request")
    func errorResponseDefaultStatus() throws {
        let data = CLIServerService.errorResponse("bad input")
        let text = String(data: data, encoding: .utf8)!
        #expect(text.hasPrefix("HTTP/1.1 400 Bad Request\r\n"))

        let bodyStart = text.range(of: "\r\n\r\n")!.upperBound
        let bodyJSON = Data(text[bodyStart...].utf8)
        let decoded = try JSONDecoder().decode(CLIServerService.AskResponseBody.self, from: bodyJSON)
        #expect(decoded.error == "bad input")
        #expect(decoded.response == nil)
    }

    @Test("errorResponse supports a custom status code")
    func errorResponseCustomStatus() {
        let data = CLIServerService.errorResponse("provider failed", statusCode: 500)
        let text = String(data: data, encoding: .utf8)!
        #expect(text.hasPrefix("HTTP/1.1 500 Internal Server Error\r\n"))
    }

    @Test("Content-Length in the response header matches the actual JSON body byte count")
    func responseContentLengthMatchesBody() {
        let data = CLIServerService.successResponse("hello")
        let text = String(data: data, encoding: .utf8)!
        let headerEnd = text.range(of: "\r\n\r\n")!.upperBound
        let bodyByteCount = Data(text[headerEnd...].utf8).count

        let lengthLine = text.split(separator: "\r\n").first { $0.hasPrefix("Content-Length:") }!
        let declaredLength = Int(lengthLine.split(separator: " ")[1])!
        #expect(declaredLength == bodyByteCount)
    }
}
