import Foundation
import SwiftUI

struct GatewayOnboardingView: View {
    var body: some View {
        NavigationStack {
            List {
                Section {
                    Text("Connect to your gateway to get started.")
                        .foregroundStyle(.secondary)
                }

                Section {
                    NavigationLink("Auto detect") {
                        AutoDetectStep()
                    }
                    NavigationLink("Manual entry") {
                        ManualEntryStep()
                    }
                }
            }
            .navigationTitle("Connect Gateway")
        }
        .gatewayTrustPromptAlert()
    }
}

private struct AutoDetectStep: View {
    @Environment(NodeAppModel.self) private var appModel: NodeAppModel
    @Environment(GatewayConnectionController.self) private var gatewayController: GatewayConnectionController
    @AppStorage("gateway.preferredStableID") private var preferredGatewayStableID: String = ""
    @AppStorage("gateway.lastDiscoveredStableID") private var lastDiscoveredGatewayStableID: String = ""

    @State private var connectingGatewayID: String?
    @State private var connectStatusText: String?

    var body: some View {
        Form {
            Section {
                Text("We’ll scan for gateways on your network and connect automatically when we find one.")
                    .foregroundStyle(.secondary)
            }

            Section("Connection status") {
                ConnectionStatusBox(
                    statusLines: self.connectionStatusLines(),
                    secondaryLine: self.connectStatusText)
            }

            Section {
                Button("Retry") {
                    self.resetConnectionState()
                    self.triggerAutoConnect()
                }
                .disabled(self.connectingGatewayID != nil)
            }
        }
        .navigationTitle("Auto detect")
        .onAppear { self.triggerAutoConnect() }
        .onChange(of: self.gatewayController.gateways) { _, _ in
            self.triggerAutoConnect()
        }
    }

    private func triggerAutoConnect() {
        guard self.appModel.gatewayServerName == nil else { return }
        guard self.connectingGatewayID == nil else { return }
        guard let candidate = self.autoCandidate() else { return }

        self.connectingGatewayID = candidate.id
        Task {
            defer { self.connectingGatewayID = nil }
            await self.gatewayController.connect(candidate)
        }
    }

    private func autoCandidate() -> GatewayDiscoveryModel.DiscoveredGateway? {
        let preferred = self.preferredGatewayStableID.trimmingCharacters(in: .whitespacesAndNewlines)
        let lastDiscovered = self.lastDiscoveredGatewayStableID.trimmingCharacters(in: .whitespacesAndNewlines)

        if !preferred.isEmpty,
           let match = self.gatewayController.gateways.first(where: { $0.stableID == preferred })
        {
            return match
        }
        if !lastDiscovered.isEmpty,
           let match = self.gatewayController.gateways.first(where: { $0.stableID == lastDiscovered })
        {
            return match
        }
        if self.gatewayController.gateways.count == 1 {
            return self.gatewayController.gateways.first
        }
        return nil
    }

    private func connectionStatusLines() -> [String] {
        ConnectionStatusBox.defaultLines(appModel: self.appModel, gatewayController: self.gatewayController)
    }

    private func resetConnectionState() {
        self.appModel.disconnectGateway()
        self.connectStatusText = nil
        self.connectingGatewayID = nil
    }
}

private struct ManualEntryStep: View {
    @Environment(NodeAppModel.self) private var appModel: NodeAppModel
    @Environment(GatewayConnectionController.self) private var gatewayController: GatewayConnectionController

    @State private var setupCode: String = ""
    @State private var setupStatusText: String?
    @State private var manualHost: String = ""
    @State private var manualPortText: String = ""
    @State private var manualUseTLS: Bool = true
    @State private var manualToken: String = ""
    @State private var manualPassword: String = ""

    @State private var connectingGatewayID: String?
    @State private var connectStatusText: String?

    var body: some View {
        Form {
            Section("Setup code") {
                Text("Use /pair in your bot to get a setup code.")
                    .font(.footnote)
                    .foregroundStyle(.secondary)

                TextField("Paste setup code", text: self.$setupCode)
                    .textInputAutocapitalization(.never)
                    .autocorrectionDisabled()

                Button("Apply setup code") {
                    self.applySetupCode()
                }
                .disabled(self.setupCode.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty)

                if let setupStatusText, !setupStatusText.isEmpty {
                    Text(setupStatusText)
                        .font(.footnote)
                        .foregroundStyle(.secondary)
                }
            }

            Section {
                TextField("Host", text: self.$manualHost)
                    .textInputAutocapitalization(.never)
                    .autocorrectionDisabled()

                TextField("Port", text: self.$manualPortText)
                    .keyboardType(.numberPad)

                Toggle("Use TLS", isOn: self.$manualUseTLS)

                TextField("Gateway token", text: self.$manualToken)
                    .textInputAutocapitalization(.never)
                    .autocorrectionDisabled()

                SecureField("Gateway password", text: self.$manualPassword)
                    .textInputAutocapitalization(.never)
                    .autocorrectionDisabled()
            }

            Section("Connection status") {
                ConnectionStatusBox(
                    statusLines: self.connectionStatusLines(),
                    secondaryLine: self.connectStatusText)
            }

            Section {
                Button {
                    Task { await self.connectManual() }
                } label: {
                    if self.connectingGatewayID == "manual" {
                        HStack(spacing: 8) {
                            ProgressView()
                                .progressViewStyle(.circular)
                            Text("Connecting…")
                        }
                    } else {
                        Text("Connect")
                    }
                }
                .disabled(self.connectingGatewayID != nil)

                Button("Retry") {
                    self.resetConnectionState()
                    self.resetManualForm()
                }
                .disabled(self.connectingGatewayID != nil)
            }
        }
        .navigationTitle("Manual entry")
    }

    private func connectManual() async {
        let host = self.manualHost.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !host.isEmpty else {
            self.connectStatusText = "Failed: host required"
            return
        }

        if let port = self.manualPortValue(), !(1...65535).contains(port) {
            self.connectStatusText = "Failed: invalid port"
            return
        }

        let defaults = UserDefaults.standard
        defaults.set(true, forKey: "gateway.manual.enabled")
        defaults.set(host, forKey: "gateway.manual.host")
        defaults.set(self.manualPortValue() ?? 0, forKey: "gateway.manual.port")
        defaults.set(self.manualUseTLS, forKey: "gateway.manual.tls")

        if let instanceId = defaults.string(forKey: "node.instanceId")?.trimmingCharacters(in: .whitespacesAndNewlines),
           !instanceId.isEmpty
        {
            let trimmedToken = self.manualToken.trimmingCharacters(in: .whitespacesAndNewlines)
            let trimmedPassword = self.manualPassword.trimmingCharacters(in: .whitespacesAndNewlines)
            if !trimmedToken.isEmpty {
                GatewaySettingsStore.saveGatewayToken(trimmedToken, instanceId: instanceId)
            }
            GatewaySettingsStore.saveGatewayPassword(trimmedPassword, instanceId: instanceId)
        }

        self.connectingGatewayID = "manual"
        defer { self.connectingGatewayID = nil }
        await self.gatewayController.connectManual(
            host: host,
            port: self.manualPortValue() ?? 0,
            useTLS: self.manualUseTLS)
    }

    private func manualPortValue() -> Int? {
        let trimmed = self.manualPortText.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !trimmed.isEmpty else { return nil }
        return Int(trimmed.filter { $0.isNumber })
    }

    private func connectionStatusLines() -> [String] {
        ConnectionStatusBox.defaultLines(appModel: self.appModel, gatewayController: self.gatewayController)
    }

    private func resetConnectionState() {
        self.appModel.disconnectGateway()
        self.connectStatusText = nil
        self.connectingGatewayID = nil
    }

    private func resetManualForm() {
        self.setupCode = ""
        self.setupStatusText = nil
        self.manualHost = ""
        self.manualPortText = ""
        self.manualUseTLS = true
        self.manualToken = ""
        self.manualPassword = ""
    }

    private func applySetupCode() {
        let raw = self.setupCode.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !raw.isEmpty else {
            self.setupStatusText = "Paste a setup code to continue."
            return
        }

        guard let payload = GatewaySetupCode.decode(raw: raw) else {
            self.setupStatusText = "Setup code not recognized."
            return
        }

        if let urlString = payload.url, let url = URL(string: urlString) {
            self.applyURL(url)
        } else if let host = payload.host, !host.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            self.manualHost = host.trimmingCharacters(in: .whitespacesAndNewlines)
            if let port = payload.port {
                self.manualPortText = String(port)
            } else {
                self.manualPortText = ""
            }
            if let tls = payload.tls {
                self.manualUseTLS = tls
            }
        } else if let url = URL(string: raw), url.scheme != nil {
            self.applyURL(url)
        } else {
            self.setupStatusText = "Setup code missing URL or host."
            return
        }

        if let token = payload.token, !token.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            self.manualToken = token.trimmingCharacters(in: .whitespacesAndNewlines)
        }
        if let password = payload.password, !password.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            self.manualPassword = password.trimmingCharacters(in: .whitespacesAndNewlines)
        }

        self.setupStatusText = "Setup code applied."
    }

    private func applyURL(_ url: URL) {
        guard let host = url.host, !host.isEmpty else { return }
        self.manualHost = host
        if let port = url.port {
            self.manualPortText = String(port)
        } else {
            self.manualPortText = ""
        }
        let scheme = (url.scheme ?? "").lowercased()
        if scheme == "wss" || scheme == "https" {
            self.manualUseTLS = true
        } else if scheme == "ws" || scheme == "http" {
            self.manualUseTLS = false
        }
    }

    // (GatewaySetupCode) decode raw setup codes.
}

private struct ConnectionStatusBox: View {
    let statusLines: [String]
    let secondaryLine: String?

    var body: some View {
        VStack(alignment: .leading, spacing: 6) {
            ForEach(self.statusLines, id: \.self) { line in
                Text(line)
                    .font(.system(size: 12, weight: .regular, design: .monospaced))
                    .foregroundStyle(.secondary)
            }
            if let secondaryLine, !secondaryLine.isEmpty {
                Text(secondaryLine)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(10)
        .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 10, style: .continuous))
    }

    static func defaultLines(
        appModel: NodeAppModel,
        gatewayController: GatewayConnectionController
    ) -> [String] {
        var lines: [String] = [
            "gateway: \(appModel.gatewayStatusText)",
            "discovery: \(gatewayController.discoveryStatusText)",
        ]
        lines.append("server: \(appModel.gatewayServerName ?? "—")")
        lines.append("address: \(appModel.gatewayRemoteAddress ?? "—")")
        return lines
    }
}
