import Testing
import Foundation
@testable import CalcPad

@Suite("FFI JSON Model Decoding Tests")
struct FFIModelTests {

    // MARK: - Single Line Response

    @Test("Decode number result")
    func decodeNumberResult() throws {
        let json = """
        {
          "schema_version": "1.0",
          "result": {
            "value": { "kind": "Number", "value": 42.0 },
            "metadata": {
              "span": { "start": 0, "end": 5 },
              "result_type": "Number",
              "display": "42",
              "raw_value": 42.0
            }
          }
        }
        """
        let response = try JSONDecoder().decode(FFIResponse.self, from: Data(json.utf8))
        #expect(response.schemaVersion == "1.0")
        #expect(response.result.metadata.display == "42")
        #expect(response.result.metadata.resultType == "Number")
        #expect(response.result.metadata.rawValue == 42.0)
        if case .number(let value) = response.result.value {
            #expect(value == 42.0)
        } else {
            Issue.record("Expected .number variant")
        }
    }

    @Test("Decode unit value result")
    func decodeUnitValueResult() throws {
        let json = """
        {
          "schema_version": "1.0",
          "result": {
            "value": { "kind": "UnitValue", "value": 5.0, "unit": "kg" },
            "metadata": {
              "span": { "start": 0, "end": 4 },
              "result_type": "UnitValue",
              "display": "5 kg",
              "raw_value": 5.0
            }
          }
        }
        """
        let response = try JSONDecoder().decode(FFIResponse.self, from: Data(json.utf8))
        if case .unitValue(let value, let unit) = response.result.value {
            #expect(value == 5.0)
            #expect(unit == "kg")
        } else {
            Issue.record("Expected .unitValue variant")
        }
    }

    @Test("Decode currency value result")
    func decodeCurrencyValueResult() throws {
        let json = """
        {
          "schema_version": "1.0",
          "result": {
            "value": { "kind": "CurrencyValue", "amount": 19.99, "currency": "USD" },
            "metadata": {
              "span": { "start": 0, "end": 6 },
              "result_type": "CurrencyValue",
              "display": "$19.99",
              "raw_value": 19.99
            }
          }
        }
        """
        let response = try JSONDecoder().decode(FFIResponse.self, from: Data(json.utf8))
        if case .currencyValue(let amount, let currency) = response.result.value {
            #expect(amount == 19.99)
            #expect(currency == "USD")
        } else {
            Issue.record("Expected .currencyValue variant")
        }
    }

    @Test("Decode datetime result")
    func decodeDateTimeResult() throws {
        let json = """
        {
          "schema_version": "1.0",
          "result": {
            "value": { "kind": "DateTime", "date": "2024-03-17" },
            "metadata": {
              "span": { "start": 0, "end": 10 },
              "result_type": "DateTime",
              "display": "2024-03-17",
              "raw_value": null
            }
          }
        }
        """
        let response = try JSONDecoder().decode(FFIResponse.self, from: Data(json.utf8))
        if case .dateTime(let date) = response.result.value {
            #expect(date == "2024-03-17")
        } else {
            Issue.record("Expected .dateTime variant")
        }
        #expect(response.result.metadata.rawValue == nil)
    }

    @Test("Decode time delta result")
    func decodeTimeDeltaResult() throws {
        let json = """
        {
          "schema_version": "1.0",
          "result": {
            "value": { "kind": "TimeDelta", "days": 30, "description": "30 days" },
            "metadata": {
              "span": { "start": 0, "end": 8 },
              "result_type": "TimeDelta",
              "display": "30 days",
              "raw_value": 30.0
            }
          }
        }
        """
        let response = try JSONDecoder().decode(FFIResponse.self, from: Data(json.utf8))
        if case .timeDelta(let days, let desc) = response.result.value {
            #expect(days == 30)
            #expect(desc == "30 days")
        } else {
            Issue.record("Expected .timeDelta variant")
        }
    }

    @Test("Decode boolean result")
    func decodeBooleanResult() throws {
        let json = """
        {
          "schema_version": "1.0",
          "result": {
            "value": { "kind": "Boolean", "value": true },
            "metadata": {
              "span": { "start": 0, "end": 5 },
              "result_type": "Boolean",
              "display": "true",
              "raw_value": null
            }
          }
        }
        """
        let response = try JSONDecoder().decode(FFIResponse.self, from: Data(json.utf8))
        if case .boolean(let value) = response.result.value {
            #expect(value == true)
        } else {
            Issue.record("Expected .boolean variant")
        }
    }

    @Test("Decode error result")
    func decodeErrorResult() throws {
        let json = """
        {
          "schema_version": "1.0",
          "result": {
            "value": { "kind": "Error", "message": "unexpected token", "span": { "start": 0, "end": 3 } },
            "metadata": {
              "span": { "start": 0, "end": 3 },
              "result_type": "Error",
              "display": "Error: unexpected token",
              "raw_value": null
            }
          }
        }
        """
        let response = try JSONDecoder().decode(FFIResponse.self, from: Data(json.utf8))
        #expect(response.result.value.isError)
        if case .error(let message) = response.result.value {
            #expect(message == "unexpected token")
        } else {
            Issue.record("Expected .error variant")
        }
    }

    // MARK: - Sheet Response

    @Test("Decode sheet response with multiple results")
    func decodeSheetResponse() throws {
        let json = """
        {
          "schema_version": "1.0",
          "results": [
            {
              "value": { "kind": "Number", "value": 4.0 },
              "metadata": { "span": { "start": 0, "end": 5 }, "result_type": "Number", "display": "4", "raw_value": 4.0 }
            },
            {
              "value": { "kind": "Error", "message": "empty expression", "span": { "start": 0, "end": 0 } },
              "metadata": { "span": { "start": 0, "end": 0 }, "result_type": "Error", "display": "Error: empty expression", "raw_value": null }
            },
            {
              "value": { "kind": "Number", "value": 30.0 },
              "metadata": { "span": { "start": 0, "end": 6 }, "result_type": "Number", "display": "30", "raw_value": 30.0 }
            }
          ]
        }
        """
        let response = try JSONDecoder().decode(FFISheetResponse.self, from: Data(json.utf8))
        #expect(response.results.count == 3)
        #expect(response.results[0].metadata.display == "4")
        #expect(response.results[1].value.isError)
        #expect(response.results[2].metadata.display == "30")
    }

    // MARK: - FFICalcValue.isError

    @Test("isError returns false for non-error values")
    func isErrorFalseForNonError() throws {
        let json = """
        { "kind": "Number", "value": 42.0 }
        """
        let value = try JSONDecoder().decode(FFICalcValue.self, from: Data(json.utf8))
        #expect(value.isError == false)
    }
}
