//
//  ValidationHelpersTests.swift
//  honeyDueTests
//
//  Unit tests for ValidationHelpers, FormValidator, and related types.
//

import Testing
@testable import honeyDue

// MARK: - ValidationResult Tests

struct ValidationResultTests {

    @Test func validResultIsValid() {
        let result = ValidationResult.valid
        #expect(result.isValid)
        #expect(result.errorMessage == nil)
    }

    @Test func invalidResultIsNotValid() {
        let result = ValidationResult.invalid("Error")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Error")
    }
}

// MARK: - Email Validation Tests

struct EmailValidationTests {

    @Test func validEmailPasses() {
        let result = ValidationHelpers.validateEmail("user@example.com")
        #expect(result.isValid)
    }

    @Test func emptyEmailFails() {
        let result = ValidationHelpers.validateEmail("")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Email is required")
    }

    @Test func whitespaceOnlyEmailFails() {
        let result = ValidationHelpers.validateEmail("   ")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Email is required")
    }

    @Test func emailWithoutAtSignFails() {
        let result = ValidationHelpers.validateEmail("userexample.com")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Please enter a valid email address")
    }

    @Test func emailWithoutDomainFails() {
        let result = ValidationHelpers.validateEmail("user@")
        #expect(!result.isValid)
    }

    @Test func emailWithSubdomainPasses() {
        let result = ValidationHelpers.validateEmail("user@mail.example.com")
        #expect(result.isValid)
    }

    @Test func emailWithPlusAddressingPasses() {
        let result = ValidationHelpers.validateEmail("user+tag@example.com")
        #expect(result.isValid)
    }

    @Test func emailWithDotsInLocalPartPasses() {
        let result = ValidationHelpers.validateEmail("first.last@example.com")
        #expect(result.isValid)
    }
}

// MARK: - Password Validation Tests

struct PasswordValidationTests {

    @Test func validPasswordPasses() {
        let result = ValidationHelpers.validatePassword("StrongPass1")
        #expect(result.isValid)
    }

    @Test func emptyPasswordFails() {
        let result = ValidationHelpers.validatePassword("")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Password is required")
    }

    @Test func shortPasswordFails() {
        let result = ValidationHelpers.validatePassword("short")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Password must be at least 8 characters")
    }

    @Test func exactMinLengthPasswordPasses() {
        let result = ValidationHelpers.validatePassword("12345678")
        #expect(result.isValid)
    }

    @Test func customMinLengthEnforced() {
        let result = ValidationHelpers.validatePassword("1234", minLength: 6)
        #expect(!result.isValid)
        #expect(result.errorMessage == "Password must be at least 6 characters")
    }

    @Test func customMinLengthPassesWhenMet() {
        let result = ValidationHelpers.validatePassword("123456", minLength: 6)
        #expect(result.isValid)
    }
}

// MARK: - Password Confirmation Tests

struct PasswordConfirmationTests {

    @Test func matchingPasswordsPasses() {
        let result = ValidationHelpers.validatePasswordConfirmation("password123", confirmation: "password123")
        #expect(result.isValid)
    }

    @Test func mismatchedPasswordsFails() {
        let result = ValidationHelpers.validatePasswordConfirmation("password123", confirmation: "password456")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Passwords do not match")
    }

    @Test func emptyConfirmationFails() {
        let result = ValidationHelpers.validatePasswordConfirmation("password123", confirmation: "")
        #expect(!result.isValid)
    }

    @Test func caseSensitiveMismatchFails() {
        let result = ValidationHelpers.validatePasswordConfirmation("Password", confirmation: "password")
        #expect(!result.isValid)
    }
}

// MARK: - Name Validation Tests

struct NameValidationTests {

    @Test func validNamePasses() {
        let result = ValidationHelpers.validateName("John")
        #expect(result.isValid)
    }

    @Test func emptyNameFails() {
        let result = ValidationHelpers.validateName("")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Name is required")
    }

    @Test func singleCharNameFails() {
        let result = ValidationHelpers.validateName("J")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Name must be at least 2 characters")
    }

    @Test func twoCharNamePasses() {
        let result = ValidationHelpers.validateName("Jo")
        #expect(result.isValid)
    }

    @Test func customFieldNameInError() {
        let result = ValidationHelpers.validateName("", fieldName: "Username")
        #expect(result.errorMessage == "Username is required")
    }

    @Test func whitespaceOnlyNameFails() {
        let result = ValidationHelpers.validateName("   ")
        #expect(!result.isValid)
    }
}

// MARK: - Phone Validation Tests

struct PhoneValidationTests {

    @Test func validUSPhonePasses() {
        let result = ValidationHelpers.validatePhone("(555) 123-4567")
        #expect(result.isValid)
    }

    @Test func emptyPhoneFails() {
        let result = ValidationHelpers.validatePhone("")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Phone number is required")
    }

    @Test func shortPhoneFails() {
        let result = ValidationHelpers.validatePhone("12345")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Please enter a valid phone number")
    }

    @Test func phoneWithCountryCodePasses() {
        let result = ValidationHelpers.validatePhone("+1 555-123-4567")
        #expect(result.isValid)
    }

    @Test func digitsOnlyPhonePasses() {
        let result = ValidationHelpers.validatePhone("5551234567")
        #expect(result.isValid)
    }
}

// MARK: - Required Field Validation Tests

struct RequiredFieldValidationTests {

    @Test func nonEmptyPasses() {
        let result = ValidationHelpers.validateRequired("value", fieldName: "Field")
        #expect(result.isValid)
    }

    @Test func emptyFails() {
        let result = ValidationHelpers.validateRequired("", fieldName: "Title")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Title is required")
    }

    @Test func whitespaceOnlyFails() {
        let result = ValidationHelpers.validateRequired("   ", fieldName: "Description")
        #expect(!result.isValid)
    }
}

// MARK: - Number Validation Tests

struct NumberValidationTests {

    @Test func validNumberPasses() {
        let result = ValidationHelpers.validateNumber("42.5", fieldName: "Cost")
        #expect(result.isValid)
    }

    @Test func emptyNumberFails() {
        let result = ValidationHelpers.validateNumber("", fieldName: "Cost")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Cost is required")
    }

    @Test func nonNumericFails() {
        let result = ValidationHelpers.validateNumber("abc", fieldName: "Cost")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Cost must be a valid number")
    }

    @Test func belowMinFails() {
        let result = ValidationHelpers.validateNumber("5", fieldName: "Cost", min: 10)
        #expect(!result.isValid)
        #expect(result.errorMessage == "Cost must be at least 10.0")
    }

    @Test func aboveMaxFails() {
        let result = ValidationHelpers.validateNumber("100", fieldName: "Cost", max: 50)
        #expect(!result.isValid)
        #expect(result.errorMessage == "Cost must be at most 50.0")
    }

    @Test func withinRangePasses() {
        let result = ValidationHelpers.validateNumber("25", fieldName: "Cost", min: 10, max: 50)
        #expect(result.isValid)
    }

    @Test func negativeNumberValidated() {
        let result = ValidationHelpers.validateNumber("-5", fieldName: "Value", min: 0)
        #expect(!result.isValid)
    }
}

// MARK: - Integer Validation Tests

struct IntegerValidationTests {

    @Test func validIntegerPasses() {
        let result = ValidationHelpers.validateInteger("42", fieldName: "Bedrooms")
        #expect(result.isValid)
    }

    @Test func decimalFails() {
        let result = ValidationHelpers.validateInteger("3.5", fieldName: "Bedrooms")
        #expect(!result.isValid)
        #expect(result.errorMessage == "Bedrooms must be a whole number")
    }

    @Test func belowMinFails() {
        let result = ValidationHelpers.validateInteger("0", fieldName: "Bedrooms", min: 1)
        #expect(!result.isValid)
    }

    @Test func aboveMaxFails() {
        let result = ValidationHelpers.validateInteger("100", fieldName: "Bedrooms", max: 50)
        #expect(!result.isValid)
    }
}

// MARK: - Length Validation Tests

struct LengthValidationTests {

    @Test func withinLengthPasses() {
        let result = ValidationHelpers.validateLength("hello", fieldName: "Title", min: 2, max: 100)
        #expect(result.isValid)
    }

    @Test func tooShortFails() {
        let result = ValidationHelpers.validateLength("a", fieldName: "Title", min: 3)
        #expect(!result.isValid)
        #expect(result.errorMessage == "Title must be at least 3 characters")
    }

    @Test func tooLongFails() {
        let result = ValidationHelpers.validateLength("abcdef", fieldName: "Code", max: 5)
        #expect(!result.isValid)
        #expect(result.errorMessage == "Code must be at most 5 characters")
    }

    @Test func emptyWithNoMinPasses() {
        let result = ValidationHelpers.validateLength("", fieldName: "Notes")
        #expect(result.isValid)
    }
}

// MARK: - URL Validation Tests

struct URLValidationTests {

    @Test func validURLPasses() {
        let result = ValidationHelpers.validateURL("https://example.com")
        #expect(result.isValid)
    }

    @Test func emptyURLFails() {
        let result = ValidationHelpers.validateURL("")
        #expect(!result.isValid)
        #expect(result.errorMessage == "URL is required")
    }

    @Test func httpURLPasses() {
        let result = ValidationHelpers.validateURL("http://example.com/path")
        #expect(result.isValid)
    }
}

// MARK: - Custom Validation Tests

struct CustomValidationTests {

    @Test func passingCustomValidatorPasses() {
        let result = ValidationHelpers.validateCustom(
            "abc123",
            fieldName: "Code",
            validator: { $0.count == 6 },
            errorMessage: "Code must be exactly 6 characters"
        )
        #expect(result.isValid)
    }

    @Test func failingCustomValidatorFails() {
        let result = ValidationHelpers.validateCustom(
            "abc",
            fieldName: "Code",
            validator: { $0.count == 6 },
            errorMessage: "Code must be exactly 6 characters"
        )
        #expect(!result.isValid)
        #expect(result.errorMessage == "Code must be exactly 6 characters")
    }

    @Test func emptyValueInCustomValidatorFails() {
        let result = ValidationHelpers.validateCustom(
            "",
            fieldName: "Code",
            validator: { _ in true },
            errorMessage: "Unused"
        )
        #expect(!result.isValid)
        #expect(result.errorMessage == "Code is required")
    }
}

// MARK: - FormValidator Tests

struct FormValidatorTests {

    @Test func allValidFieldsPass() {
        let validator = FormValidator()
        validator.add(fieldName: "email") { ValidationHelpers.validateEmail("user@example.com") }
        validator.add(fieldName: "name") { ValidationHelpers.validateName("John") }

        let result = validator.validate()
        #expect(result.isValid)
        #expect(result.errors.isEmpty)
    }

    @Test func singleInvalidFieldFails() {
        let validator = FormValidator()
        validator.add(fieldName: "email") { ValidationHelpers.validateEmail("") }
        validator.add(fieldName: "name") { ValidationHelpers.validateName("John") }

        let result = validator.validate()
        #expect(!result.isValid)
        #expect(result.errors.count == 1)
        #expect(result.errors["email"] != nil)
    }

    @Test func multipleInvalidFieldsFail() {
        let validator = FormValidator()
        validator.add(fieldName: "email") { ValidationHelpers.validateEmail("") }
        validator.add(fieldName: "password") { ValidationHelpers.validatePassword("") }

        let result = validator.validate()
        #expect(!result.isValid)
        #expect(result.errors.count == 2)
    }

    @Test func clearRemovesValidations() {
        let validator = FormValidator()
        validator.add(fieldName: "email") { ValidationHelpers.validateEmail("") }
        validator.clear()

        let result = validator.validate()
        #expect(result.isValid)
    }
}

// MARK: - FormValidationResult Tests

struct FormValidationResultTests {

    @Test func validResultHasNoErrors() {
        let result = FormValidationResult.valid
        #expect(result.isValid)
        #expect(result.errors.isEmpty)
    }

    @Test func invalidResultHasErrors() {
        let result = FormValidationResult.invalid(["email": "Email is required"])
        #expect(!result.isValid)
        #expect(result.errors["email"] == "Email is required")
    }
}
