import Testing
import Foundation
@testable import nahbar

// MARK: - OnboardingCoordinator Tests

@Suite("OnboardingCoordinator – Validierung")
struct OnboardingCoordinatorValidationTests {

    @Test("Leerer Vorname → isProfileValid ist false")
    @MainActor func emptyFirstNameIsInvalid() {
        let coord = OnboardingCoordinator()
        coord.firstName = ""
        #expect(!coord.isProfileValid)
    }

    @Test("gender startet als leerer String")
    @MainActor func genderStartsEmpty() {
        let coord = OnboardingCoordinator()
        #expect(coord.gender.isEmpty)
    }

    @Test("gender kann auf Männlich, Weiblich oder Divers gesetzt werden")
    @MainActor func genderAcceptsValidValues() {
        let coord = OnboardingCoordinator()
        for value in ["Männlich", "Weiblich", "Divers"] {
            coord.gender = value
            #expect(coord.gender == value)
        }
    }

    @Test("gender kann zurückgesetzt werden")
    @MainActor func genderCanBeCleared() {
        let coord = OnboardingCoordinator()
        coord.gender = "Männlich"
        coord.gender = ""
        #expect(coord.gender.isEmpty)
    }

    @Test("Nur Leerzeichen → isProfileValid ist false")
    @MainActor func whitespaceFirstNameIsInvalid() {
        let coord = OnboardingCoordinator()
        coord.firstName = "   "
        #expect(!coord.isProfileValid)
    }

    @Test("Nicht-leerer Vorname → isProfileValid ist true")
    @MainActor func nonEmptyFirstNameIsValid() {
        let coord = OnboardingCoordinator()
        coord.firstName = "Max"
        #expect(coord.isProfileValid)
    }

    @Test("Vorname mit führenden Leerzeichen gilt als gültig")
    @MainActor func firstNameWithLeadingSpaceIsValid() {
        let coord = OnboardingCoordinator()
        coord.firstName = " A"
        // "A" bleibt nach trim – valid
        #expect(coord.isProfileValid)
    }
}

@Suite("OnboardingCoordinator – Schrittnavigation")
struct OnboardingCoordinatorNavigationTests {

    @Test("Startzustand ist .profile")
    @MainActor func initialStepIsProfile() {
        let coord = OnboardingCoordinator()
        #expect(coord.currentStep == .profile)
    }

    @Test("advanceToContacts ohne Vorname bleibt auf .profile")
    @MainActor func advanceToContactsWithoutNameStaysOnProfile() {
        let coord = OnboardingCoordinator()
        coord.firstName = ""
        coord.advanceToContacts()
        #expect(coord.currentStep == .profile)
    }

    @Test("advanceToContacts mit gültigem Vorname → .contacts")
    @MainActor func advanceToContactsWithNameGoesToContacts() {
        let coord = OnboardingCoordinator()
        coord.firstName = "Anna"
        coord.advanceToContacts()
        #expect(coord.currentStep == .contacts)
    }

    @Test("completeOnboarding setzt Schritt auf .complete")
    @MainActor func completeOnboardingSetsComplete() {
        let coord = OnboardingCoordinator()
        coord.completeOnboarding()
        #expect(coord.currentStep == .complete)
    }
}

// MARK: - NahbarContact Tests

@Suite("NahbarContact – Initialisierung")
struct NahbarContactInitTests {

    @Test("fullName kombiniert Vor- und Nachname")
    func fullNameCombinesNames() {
        let contact = NahbarContact(givenName: "Anna", familyName: "Schmidt")
        #expect(contact.fullName == "Anna Schmidt")
    }

    @Test("fullName mit leerem Nachnamen gibt nur Vornamen zurück")
    func fullNameWithEmptyFamilyName() {
        let contact = NahbarContact(givenName: "Cher", familyName: "")
        #expect(contact.fullName == "Cher")
    }

    @Test("fullName mit leerem Vornamen gibt nur Nachnamen zurück")
    func fullNameWithEmptyGivenName() {
        let contact = NahbarContact(givenName: "", familyName: "Prince")
        #expect(contact.fullName == "Prince")
    }

    @Test("fullName mit beiden leeren Teilen ist leerer String")
    func fullNameBothEmpty() {
        let contact = NahbarContact(givenName: "", familyName: "")
        #expect(contact.fullName == "")
    }
}

@Suite("NahbarContact – Initialen")
struct NahbarContactInitialsTests {

    @Test("Initialen aus Vor- und Nachname")
    func initialsFromBothNames() {
        let contact = NahbarContact(givenName: "Anna", familyName: "Schmidt")
        #expect(contact.initials == "AS")
    }

    @Test("Initialen nur Vorname")
    func initialsFromGivenNameOnly() {
        let contact = NahbarContact(givenName: "Cher", familyName: "")
        #expect(contact.initials == "C")
    }

    @Test("Initialen nur Nachname")
    func initialsFromFamilyNameOnly() {
        let contact = NahbarContact(givenName: "", familyName: "Prince")
        #expect(contact.initials == "P")
    }

    @Test("Initialen beide leer → ?")
    func initialsBothEmpty() {
        let contact = NahbarContact(givenName: "", familyName: "")
        #expect(contact.initials == "?")
    }

    @Test("Initialen sind uppercase")
    func initialsAreUppercase() {
        let contact = NahbarContact(givenName: "anna", familyName: "bach")
        #expect(contact.initials == "AB")
    }
}

@Suite("NahbarContact – Codable")
struct NahbarContactCodableTests {

    @Test("Codable Roundtrip erhält alle Felder")
    func codableRoundtrip() throws {
        let original = NahbarContact(
            id: UUID(),
            givenName: "Max",
            familyName: "Mustermann",
            phoneNumbers: ["+49 123 456"],
            notes: "Freund",
            cnIdentifier: "abc-123"
        )
        let data    = try JSONEncoder().encode(original)
        let decoded = try JSONDecoder().decode(NahbarContact.self, from: data)

        #expect(decoded.id          == original.id)
        #expect(decoded.givenName   == original.givenName)
        #expect(decoded.familyName  == original.familyName)
        #expect(decoded.phoneNumbers == original.phoneNumbers)
        #expect(decoded.notes       == original.notes)
        #expect(decoded.cnIdentifier == original.cnIdentifier)
    }

    @Test("Codable Roundtrip mit nil cnIdentifier")
    func codableRoundtripNilCnIdentifier() throws {
        let original = NahbarContact(givenName: "Test", familyName: "User", cnIdentifier: nil)
        let data    = try JSONEncoder().encode(original)
        let decoded = try JSONDecoder().decode(NahbarContact.self, from: data)
        #expect(decoded.cnIdentifier == nil)
    }

    @Test("Mehrere Kontakte als Array codierbar")
    func arrayOfContactsCodable() throws {
        let contacts = [
            NahbarContact(givenName: "Alice", familyName: "A"),
            NahbarContact(givenName: "Bob",   familyName: "B")
        ]
        let data    = try JSONEncoder().encode(contacts)
        let decoded = try JSONDecoder().decode([NahbarContact].self, from: data)
        #expect(decoded.count == 2)
        #expect(decoded[0].givenName == "Alice")
        #expect(decoded[1].givenName == "Bob")
    }
}

// MARK: - OnboardingStep Tests

@Suite("OnboardingStep – RawValue")
struct OnboardingStepTests {

    @Test("RawValues sind aufsteigend 0–2")
    func rawValuesAreSequential() {
        #expect(OnboardingStep.profile.rawValue  == 0)
        #expect(OnboardingStep.contacts.rawValue == 1)
        #expect(OnboardingStep.complete.rawValue == 2)
    }

    @Test("allCases enthält genau 3 Schritte")
    func allCasesCount() {
        #expect(OnboardingStep.allCases.count == 3)
    }

    @Test("Reihenfolge von allCases stimmt mit rawValue überein")
    func allCasesOrder() {
        let cases = OnboardingStep.allCases
        for (i, step) in cases.enumerated() {
            #expect(step.rawValue == i)
        }
    }
}
