import XCTest

final class CableUITestsScreenshot: XCTestCase {
    private let springboard = XCUIApplication(bundleIdentifier: "com.apple.springboard")

    private enum UIStringKey: String {
        case addLoad
        case browseLibrary
        case library
        case overviewTab
        case componentsTab
        case batteriesTab
        case chargersTab
        case close
        case cancel
        case settings
        case defaultLoadName
        case billOfMaterials
        case systemEditorTitle
        case systemsTitle
    }

    private let translations: [UIStringKey: [String: String]] = [
        .addLoad: [
            "en": "Add Load",
            "de": "Verbraucher hinzufügen",
            "es": "Añadir carga",
            "fr": "Ajouter une charge",
            "nl": "Belasting toevoegen",
        ],
        .browseLibrary: [
            "en": "Browse Library",
            "de": "Bibliothek durchsuchen",
            "es": "Explorar biblioteca",
            "fr": "Parcourir la bibliothèque",
            "nl": "Bibliotheek bekijken",
        ],
        .library: [
            "en": "Library",
            "de": "Bibliothek",
            "es": "Biblioteca",
            "fr": "Bibliothèque",
            "nl": "Bibliotheek",
        ],
        .overviewTab: [
            "en": "Overview",
            "de": "Übersicht",
            "es": "Resumen",
            "fr": "Aperçu",
            "nl": "Overzicht",
        ],
        .componentsTab: [
            "en": "Components",
            "de": "Verbraucher",
            "es": "Componentes",
            "fr": "Composants",
            "nl": "Componenten",
        ],
        .batteriesTab: [
            "en": "Batteries",
            "de": "Batterien",
            "es": "Baterías",
            "fr": "Batteries",
            "nl": "Batterijen",
        ],
        .chargersTab: [
            "en": "Chargers",
            "de": "Ladegeräte",
            "es": "Cargadores",
            "fr": "Chargeurs",
            "nl": "Laders",
        ],
        .close: [
            "en": "Close",
            "de": "Schließen",
            "es": "Cerrar",
            "fr": "Fermer",
            "nl": "Sluiten",
        ],
        .cancel: [
            "en": "Cancel",
            "de": "Abbrechen",
            "es": "Cancelar",
            "fr": "Annuler",
            "nl": "Annuleren",
        ],
        .settings: [
            "en": "Settings",
            "de": "Einstellungen",
            "es": "Configuración",
            "fr": "Réglages",
            "nl": "Instellingen",
        ],
        .defaultLoadName: [
            "en": "New Load",
            "de": "Neuer Verbraucher",
            "es": "Carga nueva",
            "fr": "Nouvelle charge",
            "nl": "Nieuwe last",
        ],
        .billOfMaterials: [
            "en": "Bill of Materials",
            "de": "Stückliste",
            "es": "Lista de materiales",
            "fr": "Liste de matériel",
            "nl": "Stuklijst",
        ],
        .systemEditorTitle: [
            "en": "Edit System",
            "de": "System bearbeiten",
            "es": "Editar sistema",
            "fr": "Modifier le système",
            "nl": "Systeem bewerken",
        ],
        .systemsTitle: [
            "en": "Systems",
            "de": "Systeme",
            "es": "Sistemas",
            "fr": "Systèmes",
            "nl": "Systemen",
        ],
    ]

    override func setUpWithError() throws {
        try super.setUpWithError()
        continueAfterFailure = false
        XCUIDevice.shared.orientation = .portrait
    }

    // MARK: - Onboarding Screenshots

    @MainActor
    func testOnboardingScreenshots() throws {
        let app = launchApp(arguments: ["--uitest-reset-data"])
        // Wait for Apple Intelligence and other system notifications to appear, then dismiss
        RunLoop.current.run(until: Date().addingTimeInterval(6))
        dismissNotificationBannersIfNeeded()

        let createSystemButton = app.buttons["create-system-button"]
        XCTAssertTrue(createSystemButton.waitForExistence(timeout: 8))
        dismissNotificationBannersIfNeeded()
        waitForStability()
        takeScreenshot(named: "01-OnboardingSystemsView")

        createSystemButton.tap()

        let addLoadButton = button(in: app.buttons, for: .addLoad)
        XCTAssertTrue(addLoadButton.waitForExistence(timeout: 8))
        let browseLibraryButton = button(in: app.buttons, for: .browseLibrary)
        XCTAssertTrue(browseLibraryButton.waitForExistence(timeout: 4))
        dismissNotificationBannersIfNeeded()
        takeScreenshot(named: "02-OnboardingSystemView")

        browseLibraryButton.tap()
        let libraryCloseButton = app.buttons["library-view-close-button"]
        XCTAssertTrue(libraryCloseButton.waitForExistence(timeout: 8))
        // Wait for library items AND remote icons to load from PocketBase
        RunLoop.current.run(until: Date().addingTimeInterval(15))
        dismissNotificationBannersIfNeeded()
        takeScreenshot(named: "04-ComponentSelectorView")
        libraryCloseButton.tap()

        XCTAssertTrue(addLoadButton.waitForExistence(timeout: 4))
        addLoadButton.tap()

        let newLoadButton = button(in: app.buttons, for: .defaultLoadName)
        XCTAssertTrue(newLoadButton.waitForExistence(timeout: 8))
        waitForStability()
        dismissNotificationBannersIfNeeded()
        takeScreenshot(named: "03-LoadEditorView")
    }

    // MARK: - Sample Data Screenshots

    @MainActor
    func testSampleDataScreenshots() throws {
        let app = launchAppWithSampleData()
        dismissNotificationBannersIfNeeded()

        // Systems list
        let systemsList = resolvedSystemsList(in: app)
        XCTAssertTrue(systemsList.waitForExistence(timeout: 4))
        dismissNotificationBannersIfNeeded()
        takeScreenshot(named: "05-SystemsWithSampleData")

        // Navigate to first system
        openFirstSystem(in: app, systemsList: systemsList)

        // Overview tab — wait for navigation animation to complete
        waitForStability()
        dismissNotificationBannersIfNeeded()
        takeScreenshot(named: "06-AdventureVanOverview")

        // Bill of Materials
        let bomElement = resolveBillOfMaterialsElement(in: app)
        if !bomElement.waitForExistence(timeout: 6) {
            bringElementIntoView(bomElement, in: app)
        }
        XCTAssertTrue(bomElement.exists)
        if !bomElement.isHittable {
            bringElementIntoView(bomElement, in: app, requireHittable: true)
        }
        tapElement(bomElement)
        waitForStability()
        dismissNotificationBannersIfNeeded()
        takeScreenshot(named: "08-BillOfMaterials")

        let closeButton = app.buttons["system-bom-close-button"]
        XCTAssertTrue(closeButton.waitForExistence(timeout: 6))
        closeButton.tap()

        // Components tab
        tapTab(.componentsTab, in: app)
        let loadsList = resolvedLoadsList(in: app)
        XCTAssertTrue(loadsList.waitForExistence(timeout: 4))
        dismissNotificationBannersIfNeeded()
        takeScreenshot(named: "07-AdventureVanLoads")

        // Open first load → Calculator
        let firstLoad = loadsList.cells.element(boundBy: 0)
        XCTAssertTrue(firstLoad.waitForExistence(timeout: 2))
        firstLoad.tap()
        waitForStability()
        dismissNotificationBannersIfNeeded()
        takeScreenshot(named: "09-AdventureVanCalculator")

        // Navigate back to system tabs
        app.navigationBars.buttons.element(boundBy: 0).tap()
        waitForStability()

        // Batteries tab
        tapTab(.batteriesTab, in: app)
        waitForStability()
        dismissNotificationBannersIfNeeded()
        takeScreenshot(named: "10-AdventureVanBatteries")

        // Open first battery → Battery Editor
        let batteriesList = resolvedBatteriesList(in: app)
        if batteriesList.waitForExistence(timeout: 4) {
            let firstBattery = batteriesList.cells.element(boundBy: 0)
            if firstBattery.waitForExistence(timeout: 2) {
                firstBattery.tap()
                waitForStability()
                dismissNotificationBannersIfNeeded()
                takeScreenshot(named: "11-BatteryEditor")
                // Navigate back
                app.navigationBars.buttons.element(boundBy: 0).tap()
                waitForStability()
            }
        }

        // Chargers tab
        tapTab(.chargersTab, in: app)
        waitForStability()
        dismissNotificationBannersIfNeeded()
        takeScreenshot(named: "12-AdventureVanChargers")

        // Open first charger → Charger Editor
        let chargersList = resolvedChargersList(in: app)
        if chargersList.waitForExistence(timeout: 4) {
            let firstCharger = chargersList.cells.element(boundBy: 0)
            if firstCharger.waitForExistence(timeout: 2) {
                firstCharger.tap()
                waitForStability()
                dismissNotificationBannersIfNeeded()
                takeScreenshot(named: "13-ChargerEditor")
            }
        }
    }

    // MARK: - App Launch

    private func launchApp(arguments: [String]) -> XCUIApplication {
        let app = XCUIApplication()
        var launchArguments = ["--uitest-reset-data"]
        launchArguments.append(contentsOf: arguments)
        app.launchArguments = launchArguments
        app.launch()
        return app
    }

    private func launchAppWithSampleData() -> XCUIApplication {
        let app = XCUIApplication()
        app.launchArguments = ["--uitest-reset-data", "--uitest-sample-data"]
        app.launch()
        return app
    }

    // MARK: - Navigation Helpers

    private func openFirstSystem(in app: XCUIApplication, systemsList: XCUIElement) {
        let firstSystemCell = systemsList.cells.element(boundBy: 0)
        XCTAssertTrue(firstSystemCell.waitForExistence(timeout: 2))
        let systemName = firstSystemCell.staticTexts.firstMatch.label

        let systemButton = firstSystemCell.buttons.firstMatch
        if systemButton.exists {
            systemButton.tap()
        } else {
            firstSystemCell.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
        }

        var detailVisible = waitForSystemDetail(named: systemName, in: app)
        if !detailVisible {
            firstSystemCell.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
            detailVisible = waitForSystemDetail(named: systemName, in: app)
        }
        XCTAssertTrue(detailVisible)
    }

    private func tapTab(_ key: UIStringKey, in app: XCUIApplication) {
        let identifierMap: [UIStringKey: String] = [
            .overviewTab: "overview-tab",
            .componentsTab: "components-tab",
            .batteriesTab: "batteries-tab",
            .chargersTab: "chargers-tab",
        ]

        // Use .matching + .firstMatch to avoid "multiple matches" error
        // with iOS 26 floating tab bar (which creates duplicate elements)
        if let identifier = identifierMap[key] {
            let tabButton = app.buttons.matching(identifier: identifier).firstMatch
            if tabButton.waitForExistence(timeout: 3) {
                tapElement(tabButton)
                return
            }
        }

        let tabButton = button(in: app.buttons, for: key)
        XCTAssertTrue(tabButton.waitForExistence(timeout: 3))
        tapElement(tabButton)
    }

    private func tapElement(_ element: XCUIElement) {
        if element.isHittable {
            element.tap()
        } else {
            element.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()
        }
    }

    // MARK: - Element Resolution

    private func resolvedSystemsList(in app: XCUIApplication) -> XCUIElement {
        let collection = app.collectionViews["systems-list"]
        if collection.waitForExistence(timeout: 6) {
            return collection
        }

        if app.collectionViews.firstMatch.waitForExistence(timeout: 2) {
            return app.collectionViews.firstMatch
        }

        let table = app.tables["systems-list"]
        if table.waitForExistence(timeout: 6) {
            return table
        }

        XCTAssertTrue(app.tables.firstMatch.waitForExistence(timeout: 2))
        return app.tables.firstMatch
    }

    private func resolvedLoadsList(in app: XCUIApplication) -> XCUIElement {
        let collection = app.collectionViews["loads-list"]
        if collection.waitForExistence(timeout: 6) {
            return collection
        }

        let table = app.tables["loads-list"]
        XCTAssertTrue(table.waitForExistence(timeout: 6))
        return table
    }

    private func resolvedBatteriesList(in app: XCUIApplication) -> XCUIElement {
        let collection = app.collectionViews["batteries-list"]
        if collection.waitForExistence(timeout: 6) {
            return collection
        }

        let table = app.tables["batteries-list"]
        if table.waitForExistence(timeout: 6) {
            return table
        }

        // Fallback: any list on screen
        if app.collectionViews.firstMatch.waitForExistence(timeout: 2) {
            return app.collectionViews.firstMatch
        }
        return app.tables.firstMatch
    }

    private func resolvedChargersList(in app: XCUIApplication) -> XCUIElement {
        let collection = app.collectionViews["chargers-list"]
        if collection.waitForExistence(timeout: 6) {
            return collection
        }

        let table = app.tables["chargers-list"]
        if table.waitForExistence(timeout: 6) {
            return table
        }

        if app.collectionViews.firstMatch.waitForExistence(timeout: 2) {
            return app.collectionViews.firstMatch
        }
        return app.tables.firstMatch
    }

    private func resolveBillOfMaterialsElement(in app: XCUIApplication) -> XCUIElement {
        let identifier = "system-bom-button"
        let buttonByIdentifier = app.buttons.matching(identifier: identifier).firstMatch
        if buttonByIdentifier.exists { return buttonByIdentifier }

        let elementByIdentifier = app.otherElements.matching(identifier: identifier).firstMatch
        if elementByIdentifier.exists { return elementByIdentifier }

        let candidates = candidateStrings(for: .billOfMaterials)
        for candidate in candidates {
            let button = app.buttons[candidate]
            if button.exists { return button }
            let other = app.otherElements[candidate]
            if other.exists { return other }
        }

        return buttonByIdentifier
    }

    // MARK: - Screenshots & Stability

    private func takeScreenshot(named name: String) {
        let screenshot = XCUIScreen.main.screenshot()
        let attachment = XCTAttachment(screenshot: screenshot)
        attachment.name = name
        attachment.lifetime = .keepAlways
        add(attachment)
    }

    private func waitForStability(long: Bool = false) {
        RunLoop.current.run(until: Date().addingTimeInterval(long ? 3.0 : 0.5))
    }

    private func waitForSystemDetail(named systemName: String, in app: XCUIApplication, timeout: TimeInterval = 6) -> Bool {
        let deadline = Date().addingTimeInterval(timeout)
        while Date() < deadline {
            if app.otherElements["system-overview"].exists {
                return true
            }
            let navBar = app.navigationBars.firstMatch
            if navBar.buttons[systemName].exists || navBar.staticTexts[systemName].exists {
                return true
            }
            RunLoop.current.run(until: Date().addingTimeInterval(0.25))
        }
        return app.otherElements["system-overview"].exists
    }

    private func bringElementIntoView(
        _ element: XCUIElement,
        in app: XCUIApplication,
        requireHittable: Bool = false,
        attempts: Int = 8
    ) {
        let scrollContainer = app.scrollViews.firstMatch.exists ? app.scrollViews.firstMatch : app.tables.firstMatch
        for _ in 0..<attempts {
            if element.exists, (!requireHittable || element.isHittable) {
                return
            }
            if scrollContainer.exists {
                scrollContainer.swipeUp()
            } else {
                app.swipeUp()
            }
            waitForStability()
            _ = element.waitForExistence(timeout: 2)
        }
    }

    // MARK: - Notification Dismissal

    private func dismissNotificationBannersIfNeeded() {
        // Try multiple times — notifications can appear with a delay
        for _ in 0..<3 {
            let banner = springboard.otherElements.matching(identifier: "NotificationShortLookView").firstMatch
            guard banner.waitForExistence(timeout: 1) else { return }
            if banner.isHittable {
                banner.swipeUp()
            } else {
                let start = banner.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5))
                let end = banner.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: -1.5))
                start.press(forDuration: 0.05, thenDragTo: end)
            }
            waitForStability()
        }
    }

    // MARK: - Localized Element Matching

    private func candidateStrings(for key: UIStringKey) -> [String] {
        var values = Set<String>()
        if let languageCode = Locale.preferredLanguages.first.flatMap({ String($0.prefix(2)) }),
           let localized = translations[key]?[languageCode] {
            values.insert(localized)
        }
        if let english = translations[key]?["en"] {
            values.insert(english)
        }
        if let others = translations[key]?.values {
            values.formUnion(others)
        }
        return Array(values)
    }

    private func button(in query: XCUIElementQuery, for key: UIStringKey) -> XCUIElement {
        let candidates = candidateStrings(for: key)
        for candidate in candidates {
            let element = query[candidate]
            if element.exists {
                return element
            }
        }
        let predicate = NSPredicate(
            format: "label IN %@ OR identifier IN %@",
            NSArray(array: candidates),
            NSArray(array: candidates)
        )
        return query.matching(predicate).firstMatch
    }
}
