// SPDX-FileCopyrightText: 2026 John Burwell and contributors
// SPDX-License-Identifier: AGPL-3.0-or-later

import SwiftUI

struct ConnectedDeviceSourcePickerView: View {
    let deviceDiscoveryService: ConnectedDeviceSourceAccessMethod
    let sourceFactory: ConnectedDeviceSourceFactory
    let onAddSource: (MinecraftSource) -> Void

    @Environment(\.dismiss) private var dismiss
    @State private var devices: [ConnectedDevice] = []
    @State private var containers: [DeviceAppContainer] = []
    @State private var selectedDeviceID: ConnectedDevice.ID?
    @State private var selectedContainerID: DeviceAppContainer.ID?
    @State private var preferredMinecraftSubpath = "Documents/games/com.mojang"
    @State private var isLoadingDevices = false
    @State private var isLoadingContainers = false
    @State private var availabilityMessage: String?
    @State private var errorMessage: String?

    var body: some View {
        VStack(alignment: .leading, spacing: 18) {
            header

            if let availabilityMessage {
                Text(availabilityMessage)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
                    .padding(.horizontal, 18)
            }

            if let errorMessage {
                Text(errorMessage)
                    .font(.footnote)
                    .foregroundStyle(.red)
                    .padding(.horizontal, 18)
            }

            HStack(alignment: .top, spacing: 18) {
                deviceColumn
                containerColumn
            }
            .frame(maxHeight: .infinity, alignment: .top)

            VStack(alignment: .leading, spacing: 8) {
                Text("Minecraft Subpath")
                    .font(.subheadline.weight(.semibold))
                TextField("Documents/games/com.mojang", text: $preferredMinecraftSubpath)
                    .textFieldStyle(.roundedBorder)
                Text("Used after the app container is mounted. The current proven path is `Documents/games/com.mojang`.")
                    .font(.footnote)
                    .foregroundStyle(.secondary)
            }
            .padding(.horizontal, 18)

            footer
        }
        .frame(minWidth: 760, minHeight: 420)
        .padding(.vertical, 18)
        .task {
            await loadDevices()
        }
        .onChange(of: selectedDeviceID) { _, _ in
            Task {
                await loadContainersForSelectedDevice()
            }
        }
    }

    private var header: some View {
        VStack(alignment: .leading, spacing: 6) {
            Text("Add Connected Device Source")
                .font(.title2.weight(.semibold))
            Text("Choose a connected device source and scan its Minecraft documents just like a local folder.")
                .font(.subheadline)
                .foregroundStyle(.secondary)
        }
        .padding(.horizontal, 18)
    }

    private var deviceColumn: some View {
        VStack(alignment: .leading, spacing: 10) {
            HStack {
                Text("Devices")
                    .font(.headline)

                Spacer()

                Button("Refresh") {
                    Task {
                        await loadDevices()
                    }
                }
                .disabled(isLoadingDevices)
            }

            Group {
                if isLoadingDevices {
                    loadingState("Searching for connected devices...")
                } else if devices.isEmpty {
                    emptyState("No devices found")
                } else {
                    List(devices, selection: $selectedDeviceID) { device in
                        VStack(alignment: .leading, spacing: 4) {
                            Text(device.name)
                                .font(.subheadline.weight(.semibold))
                            Text(deviceSubtitle(device))
                                .font(.footnote)
                                .foregroundStyle(.secondary)
                        }
                        .tag(device.id)
                    }
                    .listStyle(.inset)
                }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        .padding(.horizontal, 18)
    }

    private var containerColumn: some View {
        VStack(alignment: .leading, spacing: 10) {
            Text("Accessible Apps")
                .font(.headline)

            Group {
                if selectedDevice == nil {
                    emptyState("Select a device to list file-sharing apps")
                } else if isLoadingContainers {
                    loadingState("Listing accessible app containers...")
                } else if containers.isEmpty {
                    emptyState("No accessible apps found for this device")
                } else {
                    List(containers, selection: $selectedContainerID) { container in
                        VStack(alignment: .leading, spacing: 4) {
                            Text(container.appName)
                                .font(.subheadline.weight(.semibold))
                            Text(container.appID)
                                .font(.footnote)
                                .foregroundStyle(.secondary)
                        }
                        .tag(container.id)
                    }
                    .listStyle(.inset)
                }
            }
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
        .padding(.horizontal, 18)
    }

    private var footer: some View {
        HStack {
            Button("Cancel") {
                dismiss()
            }

            Spacer()

            Button("Add Device Source") {
                guard let selectedDevice, let selectedContainer else {
                    return
                }

                var adjustedContainer = selectedContainer
                let trimmedSubpath = preferredMinecraftSubpath.trimmingCharacters(in: .whitespacesAndNewlines)
                adjustedContainer.minecraftFolderRelativePath = trimmedSubpath.isEmpty ? nil : trimmedSubpath
                let source = sourceFactory.makeSource(device: selectedDevice, container: adjustedContainer)
                onAddSource(source)
            }
            .buttonStyle(.borderedProminent)
            .disabled(selectedDevice == nil || selectedContainer == nil)
        }
        .padding(.horizontal, 18)
    }

    private var selectedDevice: ConnectedDevice? {
        devices.first(where: { $0.id == selectedDeviceID })
    }

    private var selectedContainer: DeviceAppContainer? {
        containers.first(where: { $0.id == selectedContainerID })
    }

    private func loadDevices() async {
        isLoadingDevices = true
        availabilityMessage = nil
        errorMessage = nil
        let previousSelectedDeviceID = selectedDeviceID

        do {
            let devices = try await deviceDiscoveryService.listConnectedDevices()
            let resolvedSelectedDeviceID = devices.contains(where: { $0.id == previousSelectedDeviceID })
                ? previousSelectedDeviceID
                : devices.first?.id
            let shouldReloadContainers = resolvedSelectedDeviceID != nil && resolvedSelectedDeviceID == previousSelectedDeviceID

            await MainActor.run {
                self.devices = devices
                self.selectedDeviceID = resolvedSelectedDeviceID
                if devices.isEmpty {
                    self.containers = []
                    self.selectedContainerID = nil
                }
                self.isLoadingDevices = false
            }

            if shouldReloadContainers {
                await loadContainersForSelectedDevice()
            }
        } catch {
            await MainActor.run {
                self.devices = []
                self.containers = []
                self.selectedDeviceID = nil
                self.selectedContainerID = nil
                self.errorMessage = error.localizedDescription
                self.isLoadingDevices = false
                self.isLoadingContainers = false
            }
        }
    }

    private func loadContainersForSelectedDevice() async {
        guard let selectedDevice else {
            await MainActor.run {
                containers = []
                selectedContainerID = nil
                isLoadingContainers = false
            }
            return
        }

        isLoadingContainers = true
        availabilityMessage = nil
        errorMessage = nil

        do {
            let containers = try await deviceDiscoveryService.listAccessibleContainers(for: selectedDevice)
            await MainActor.run {
                self.containers = containers
                self.selectedContainerID = preferredContainerID(in: containers)
                self.isLoadingContainers = false
            }
        } catch {
            await MainActor.run {
                self.containers = []
                self.selectedContainerID = nil
                self.errorMessage = error.localizedDescription
                self.isLoadingContainers = false
            }
        }
    }

    private func preferredContainerID(in containers: [DeviceAppContainer]) -> DeviceAppContainer.ID? {
        if let minecraftContainer = containers.first(where: { $0.appID == "com.mojang.minecraftpe" }) {
            return minecraftContainer.id
        }

        return containers.first?.id
    }

    private func deviceSubtitle(_ device: ConnectedDevice) -> String {
        [
            device.productType,
            device.osVersion.map { "iOS/iPadOS \($0)" },
            device.connection == .network ? "Network" : "USB"
        ]
        .compactMap { $0 }
        .joined(separator: " • ")
    }

    private func loadingState(_ title: String) -> some View {
        VStack(spacing: 10) {
            ProgressView()
                .controlSize(.small)
            Text(title)
                .font(.footnote)
                .foregroundStyle(.secondary)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .appCardSurface(.placeholder)
    }

    private func emptyState(_ title: String) -> some View {
        VStack(spacing: 10) {
            Image(systemName: "externaldrive.badge.questionmark")
                .font(.title2)
                .foregroundStyle(.secondary)
            Text(title)
                .font(.footnote)
                .foregroundStyle(.secondary)
                .multilineTextAlignment(.center)
        }
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .appCardSurface(.placeholder)
    }
}
