//
//  WireGuardView.swift
//  Passepartout
//
//  Created by Davide De Rosa on 7/31/24.
//  Copyright (c) 2024 Davide De Rosa. All rights reserved.
//
//  https://github.com/passepartoutvpn
//
//  This file is part of Passepartout.
//
//  Passepartout is free software: you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation, either version 3 of the License, or
//  (at your option) any later version.
//
//  Passepartout is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with Passepartout.  If not, see <http://www.gnu.org/licenses/>.
//

import CommonLibrary
import CommonUtils
import PassepartoutKit
import SwiftUI

struct WireGuardView: View, ModuleDraftEditing {

    @Environment(\.navigationPath)
    private var path

    let module: WireGuardModule.Builder

    @ObservedObject
    var editor: ProfileEditor

    let impl: WireGuardModule.Implementation?

    @State
    private var paywallReason: PaywallReason?

    @State
    private var errorHandler: ErrorHandler = .default()

    init(module: WireGuardModule.Builder, parameters: ModuleViewParameters) {
        self.module = module
        editor = parameters.editor
        impl = parameters.impl as? WireGuardModule.Implementation
    }

    var body: some View {
        contentView
            .moduleView(editor: editor, draft: draft.wrappedValue)
            .modifier(PaywallModifier(reason: $paywallReason))
            .navigationDestination(for: Subroute.self, destination: destination)
            .themeAnimation(on: draft.wrappedValue.providerId, category: .modules)
            .withErrorHandler(errorHandler)
    }
}

// MARK: - Content

private extension WireGuardView {

    @ViewBuilder
    var contentView: some View {
        if let configuration = draft.wrappedValue.configurationBuilder {
            ConfigurationView(configuration: configuration)
        } else {
            EmptyView()
                .modifier(providerModifier)
        }
    }

    var providerModifier: some ViewModifier {
        VPNProviderContentModifier(
            providerId: providerId,
            providerPreferences: nil,
            selectedEntity: providerEntity,
            paywallReason: $paywallReason,
            entityDestination: Subroute.providerServer,
            providerRows: {
                moduleGroup(for: providerKeyRows)
            }
        )
    }

    var providerKeyRows: [ModuleRow]? {
        [.push(caption: Strings.Modules.Wireguard.providerKey, route: HashableRoute(Subroute.providerKey))]
    }
}

private extension WireGuardView {
    func onSelectServer(server: VPNServer, preset: VPNPreset<WireGuard.Configuration>) {
        draft.wrappedValue.providerEntity = VPNEntity(server: server, preset: preset)
        path.wrappedValue.removeLast()
    }

    func importConfiguration(from url: URL) {
        // TODO: #657, import draft from external URL
    }
}

// MARK: - Destinations

private extension WireGuardView {
    enum Subroute: Hashable {
        case providerServer

        case providerKey
    }

    @ViewBuilder
    func destination(for route: Subroute) -> some View {
        switch route {
        case .providerServer:
            draft.wrappedValue.providerSelection.map {
                VPNProviderServerView(
                    moduleId: module.id,
                    providerId: $0.id,
                    selectedEntity: $0.entity,
                    filtersWithSelection: true,
                    onSelect: onSelectServer
                )
            }

        case .providerKey:
            // TODO: #339, WireGuard upload public key to provider
            EmptyView()
        }
    }
}

// MARK: - Previews

// swiftlint: disable force_try
#Preview {
    let gen = MockGenerator()

    var builder = WireGuard.Configuration.Builder(keyGenerator: gen)
    builder.interface.addresses = ["1.1.1.1", "2.2.2.2"]
    builder.interface.mtu = 1200
    builder.interface.dns.protocolType = .cleartext
    builder.interface.dns.servers = ["8.8.8.8", "4.4.4.4"]
    builder.interface.dns.domainName = "domain.com"
    builder.interface.dns.searchDomains = ["search1.com", "search2.net"]

    builder.peers = (0..<3).map { _ in
        var peer = WireGuard.RemoteInterface.Builder(publicKey: try! gen.publicKey(for: gen.newPrivateKey()))
        peer.preSharedKey = gen.newPrivateKey()
        peer.allowedIPs = ["1.1.1.1/8", "2.2.2.2/12"]
        peer.endpoint = "8.8.8.8:12345"
        peer.keepAlive = 30
        return peer
    }

    let module = WireGuardModule.Builder(configurationBuilder: builder)
    return module.preview()
}
// swiftlint: enable force_try

private final class MockGenerator: WireGuardKeyGenerator {
    func newPrivateKey() -> String {
        "private-key"
    }

    func privateKey(from string: String) throws -> String {
        "private-key"
    }

    func publicKey(from string: String) throws -> String {
        "public-key"
    }

    func publicKey(for privateKey: String) throws -> String {
        "public-key"
    }
}