import CoreGraphics
import Foundation
import Testing
@testable import nahbar

// MARK: - TourCoordinator Tests

/// Builds an isolated coordinator with a known tour set and fresh UserDefaults.
private func makeCoordinator(
    tours: [Tour] = [TourCatalog.onboarding],
    version: String = "1.0"
) -> TourCoordinator {
    let suiteName = "test.coordinator.\(UUID().uuidString)"
    let defaults = UserDefaults(suiteName: suiteName)!
    let store = TourSeenStore(defaults: defaults)
    return TourCoordinator(tours: tours, seenStore: store, appVersionProvider: { version })
}

@Suite("TourCoordinator – Starten & Navigieren")
struct TourCoordinatorNavigationTests {

    @Test("start setzt activeTour und currentStepIndex = 0")
    func startSetsTourAndIndex() {
        let coordinator = makeCoordinator()
        coordinator.start(.onboarding)
        #expect(coordinator.activeTour?.id == .onboarding)
        #expect(coordinator.currentStepIndex == 0)
    }

    @Test("isActive ist false vor dem Start")
    func isActiveIsFalseBeforeStart() {
        let coordinator = makeCoordinator()
        #expect(!coordinator.isActive)
    }

    @Test("isActive ist true nach dem Start")
    func isActiveIsTrueAfterStart() {
        let coordinator = makeCoordinator()
        coordinator.start(.onboarding)
        #expect(coordinator.isActive)
    }

    @Test("next erhöht currentStepIndex")
    func nextIncrementsIndex() {
        let coordinator = makeCoordinator()
        coordinator.start(.onboarding)
        coordinator.next()
        #expect(coordinator.currentStepIndex == 1)
    }

    @Test("previous verringert currentStepIndex")
    func previousDecrementsIndex() {
        let coordinator = makeCoordinator()
        coordinator.start(.onboarding)
        coordinator.next()
        coordinator.previous()
        #expect(coordinator.currentStepIndex == 0)
    }

    @Test("previous am ersten Step tut nichts")
    func previousOnFirstStepDoesNothing() {
        let coordinator = makeCoordinator()
        coordinator.start(.onboarding)
        coordinator.previous()
        #expect(coordinator.currentStepIndex == 0)
    }

    @Test("next am letzten Step schließt die Tour")
    func nextOnLastStepClosesTour() {
        let coordinator = makeCoordinator()
        coordinator.start(.onboarding)
        let stepCount = TourCatalog.onboarding.steps.count
        for _ in 0..<(stepCount - 1) {
            coordinator.next()
        }
        #expect(coordinator.isLastStep)
        coordinator.next()
        #expect(!coordinator.isActive)
        #expect(coordinator.activeTour == nil)
    }

    @Test("isFirstStep ist true nur bei Index 0")
    func isFirstStepOnlyAtIndexZero() {
        let coordinator = makeCoordinator()
        coordinator.start(.onboarding)
        #expect(coordinator.isFirstStep)
        coordinator.next()
        #expect(!coordinator.isFirstStep)
    }

    @Test("isLastStep ist true nur am letzten Step")
    func isLastStepOnlyAtEnd() {
        let coordinator = makeCoordinator()
        coordinator.start(.onboarding)
        #expect(!coordinator.isLastStep)
        let stepCount = TourCatalog.onboarding.steps.count
        for _ in 0..<(stepCount - 1) {
            coordinator.next()
        }
        #expect(coordinator.isLastStep)
    }

    @Test("currentStep gibt den korrekten Step zurück")
    func currentStepMatchesIndex() {
        let coordinator = makeCoordinator()
        coordinator.start(.onboarding)
        let firstStep = coordinator.currentStep
        #expect(firstStep != nil)
        #expect(firstStep?.id == TourCatalog.onboarding.steps[0].id)
    }
}

@Suite("TourCoordinator – Beenden & Seen-Status")
struct TourCoordinatorCompletionTests {

    @Test("skip markiert Tour als gesehen und schließt sie")
    func skipMarksSeen() {
        let suiteName = "test.coordinator.skip.\(UUID().uuidString)"
        let defaults = UserDefaults(suiteName: suiteName)!
        let store = TourSeenStore(defaults: defaults)
        let coordinator = TourCoordinator(tours: [TourCatalog.onboarding], seenStore: store, appVersionProvider: { "1.0" })

        coordinator.start(.onboarding)
        coordinator.skip()

        #expect(!coordinator.isActive)
        #expect(store.hasSeen(.onboarding))
    }

    @Test("close markiert Tour als gesehen und schließt sie")
    func closeMarksSeen() {
        let suiteName = "test.coordinator.close.\(UUID().uuidString)"
        let defaults = UserDefaults(suiteName: suiteName)!
        let store = TourSeenStore(defaults: defaults)
        let coordinator = TourCoordinator(tours: [TourCatalog.onboarding], seenStore: store, appVersionProvider: { "1.0" })

        coordinator.start(.onboarding)
        coordinator.close()

        #expect(!coordinator.isActive)
        #expect(store.hasSeen(.onboarding))
    }

    @Test("onComplete-Callback wird nach Tour-Ende aufgerufen")
    func onCompleteCalledAfterTourEnds() {
        let coordinator = makeCoordinator()
        var callbackCalled = false
        coordinator.start(.onboarding) { callbackCalled = true }
        coordinator.skip()
        #expect(callbackCalled)
    }

    @Test("onComplete-Callback wird nur einmal aufgerufen")
    func onCompleteCalledOnce() {
        let coordinator = makeCoordinator()
        var callCount = 0
        coordinator.start(.onboarding) { callCount += 1 }
        coordinator.skip()
        // Attempting to skip again on an inactive tour does nothing
        coordinator.skip()
        #expect(callCount == 1)
    }

    @Test("Nach Tour-Ende ist currentStepIndex wieder 0")
    func indexResetAfterCompletion() {
        let coordinator = makeCoordinator()
        coordinator.start(.onboarding)
        coordinator.next()
        coordinator.skip()
        #expect(coordinator.currentStepIndex == 0)
    }
}

@Suite("TourCoordinator – Target Frames")
struct TourCoordinatorTargetFrameTests {

    @Test("updateTargetFrame speichert Frame korrekt")
    func updateTargetFrameStoresFrame() {
        let coordinator = makeCoordinator()
        let frame = CGRect(x: 10, y: 20, width: 100, height: 50)
        coordinator.targetFrames[.addContactButton] = frame
        #expect(coordinator.targetFrames[.addContactButton] == frame)
    }

    @Test("clearTargetFrame entfernt Frame")
    func clearTargetFrameRemovesFrame() {
        let coordinator = makeCoordinator()
        coordinator.targetFrames[.addContactButton] = CGRect(x: 0, y: 0, width: 44, height: 44)
        coordinator.targetFrames.removeValue(forKey: .addContactButton)
        #expect(coordinator.targetFrames[.addContactButton] == nil)
    }
}
