import SwiftUI

struct MainView: View {
    @ObservedObject var model: MainViewModel
    @State private var showingOpenSourceLicenses = false

    var body: some View {
        VStack(spacing: 0) {
            header
            ScrollView {
                status
                    .padding(.horizontal, 20)
                    .padding(.vertical, 28)
                    .frame(maxWidth: .infinity)
            }
            .background(Color(uiColor: .systemBackground))
        }
        .background(Color(uiColor: .systemBackground))
        .onReceive(model.tunnel.$status) { _ in model.refreshFromTunnelStatus() }
        .onReceive(model.tunnel.$lastDisconnectError) { model.refreshFromTunnelError($0) }
        .sheet(isPresented: $showingOpenSourceLicenses) {
            OpenSourceLicensesView(sourceURL: gplSourceURL)
        }
        .fullScreenCover(isPresented: $model.isPortalPresented) {
            SafariView(
                url: model.portalReturnURL ?? MainViewModel.companionURL,
                isPresented: $model.isPortalPresented
            )
                .ignoresSafeArea()
        }
    }

    private var header: some View {
        HStack(spacing: 12) {
            Image("BrandMark")
                .resizable()
                .scaledToFit()
                .frame(width: 40, height: 40)
            VStack(alignment: .leading, spacing: 2) {
                Text("HT Portal VPN").font(.headline)
                Text(L10n.text("app.subtitle", fallback: "Secure mobile connection"))
                    .font(.caption)
                    .foregroundStyle(.secondary)
            }
            Spacer()
        }
        .padding(.horizontal, 20)
        .padding(.vertical, 14)
        .background(Color(uiColor: .systemBackground))
        .overlay(alignment: .bottom) { Divider() }
    }

    private var status: some View {
        VStack(spacing: 0) {
            ConnectionSignal(state: model.state)
                .padding(.bottom, 20)

            Text(L10n.text("handoff.protected", fallback: "Protected handoff"))
                .font(.caption2.weight(.bold))
                .textCase(.uppercase)
                .foregroundStyle(Color.accentColor)
                .padding(.bottom, 8)

            Text(model.state.title)
                .font(.title2.weight(.semibold))
                .multilineTextAlignment(.center)

            if !model.state.deviceName.isEmpty {
                Text(model.state.deviceName)
                    .font(.headline)
                    .foregroundStyle(Color.accentColor)
                    .padding(.top, 7)
            }

            stateDescription
                .padding(.top, 11)

            if model.state.phase == "privacy-consent" {
                privacyDisclosure
                    .padding(.top, 18)
            }

            if model.state.phase == "connected", let diagnostics = model.diagnostics {
                transportStatus(diagnostics.transport)
                    .padding(.top, 14)
            }

            actions
                .padding(.top, 18)

            if let diagnostics = model.diagnostics {
                Divider().padding(.vertical, 20)
                diagnosticsView(diagnostics)
            }

            Text(L10n.text(
                "app.version",
                fallback: "Version %@ (build %@) · n3n 3.4.6",
                appVersion,
                buildNumber
            ))
                .font(.caption2)
                .foregroundStyle(.tertiary)
                .padding(.top, 20)

            Button(L10n.text("license.open_source", fallback: "Open-source licenses")) {
                showingOpenSourceLicenses = true
            }
            .font(.caption)
            .padding(.top, 8)
        }
        .frame(maxWidth: 500)
        .padding(.horizontal, 6)
        .padding(.vertical, 4)
    }

    @ViewBuilder
    private func transportStatus(_ transport: String) -> some View {
        VStack(alignment: .leading, spacing: 8) {
            Text(L10n.text(
                "diagnostics.transport_format",
                fallback: "Transport: %@",
                localizedTransport(transport)
            ))
                .font(.caption.weight(.medium))
                .foregroundStyle(.secondary)

            if transport == "udp-relay" || transport == "tcp-relay" {
                HStack(alignment: .top, spacing: 9) {
                    Image(systemName: "exclamationmark.triangle")
                        .foregroundStyle(.orange)
                    Text(transport == "udp-relay"
                        ? L10n.text(
                            "diagnostics.transport.udp-relay_warning",
                            fallback: "Peer-to-peer is unavailable. Traffic is relayed and may be slower. Allow bidirectional UDP to the configured HT Portal VPN servers; symmetric NAT or carrier-grade NAT may require a network or ISP change."
                        )
                        : L10n.text(
                            "diagnostics.transport.tcp-relay_warning",
                            fallback: "The UDP path is unavailable. Traffic uses a TCP relay and may be slower. Check outbound UDP access to the configured HT Portal VPN servers."
                        ))
                        .font(.footnote)
                }
                .padding(.horizontal, 12)
                .padding(.vertical, 10)
                .frame(maxWidth: .infinity, alignment: .leading)
                .background(Color.orange.opacity(0.10), in: RoundedRectangle(cornerRadius: 6))
            }
        }
        .frame(maxWidth: .infinity, alignment: .leading)
    }

    @ViewBuilder
    private var stateDescription: some View {
        if let error = model.state.error {
            VStack(spacing: 8) {
                Text(error)
                    .font(.callout.monospaced())
                    .foregroundStyle(.red)
                    .multilineTextAlignment(.center)
                    .textSelection(.enabled)
                Text(model.state.detail)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
                    .multilineTextAlignment(.center)
            }
        } else {
            Text(model.state.detail)
                .font(.body)
                .foregroundStyle(.secondary)
                .multilineTextAlignment(.center)
        }
    }

    @ViewBuilder
    private var actions: some View {
        VStack(spacing: 10) {
            if model.state.phase == "privacy-consent" {
                Button { model.confirmVpnDisclosure() } label: {
                    Label(
                        L10n.text("action.continue_vpn", fallback: "Continue and authorize VPN"),
                        systemImage: "checkmark.shield"
                    )
                        .frame(maxWidth: .infinity)
                }
                .buttonStyle(.borderedProminent)
                .controlSize(.large)

                Button(role: .cancel) { model.cancelVpnDisclosure() } label: {
                    Text(L10n.text("action.cancel", fallback: "Cancel"))
                        .frame(maxWidth: .infinity)
                }
                .buttonStyle(.bordered)
                .controlSize(.large)
            } else if ["idle", "stopped", "failed", "cancelled"].contains(model.state.phase) {
                Button { model.showPortal() } label: {
                    Label(
                        L10n.text(
                            "action.sign_in_select_device",
                            fallback: "Sign in and select a device"
                        ),
                        systemImage: "safari"
                    )
                        .frame(maxWidth: .infinity)
                }
                .buttonStyle(.borderedProminent)
                .controlSize(.large)
            }
            if ["connected", "connecting", "awaiting-permission"].contains(model.state.phase) {
                Button(role: .destructive) { model.disconnect() } label: {
                    Label(
                        L10n.text("action.disconnect", fallback: "Disconnect"),
                        systemImage: "stop.circle"
                    )
                        .frame(maxWidth: .infinity)
                }
                .buttonStyle(.bordered)
                .controlSize(.large)
            }
            if model.state.phase == "stop-failed" {
                Button { model.disconnect() } label: {
                    Label(
                        L10n.text("action.retry_disconnect", fallback: "Retry disconnection"),
                        systemImage: "arrow.clockwise"
                    )
                        .frame(maxWidth: .infinity)
                }
                .buttonStyle(.borderedProminent)
                .controlSize(.large)
            }
        }
    }

    private var privacyDisclosure: some View {
        VStack(alignment: .leading, spacing: 14) {
            Label(
                L10n.text("privacy.data_title", fallback: "VPN data and privacy"),
                systemImage: "lock.shield"
            )
                .font(.headline)

            privacyRow(
                icon: "network",
                title: L10n.text("privacy.traffic.title", fallback: "VPN traffic"),
                detail: L10n.text(
                    "privacy.traffic.detail",
                    fallback: "Only HT Portal remote networks are routed. Traffic contents, browsing history and DNS queries are not inspected, logged or stored."
                )
            )
            privacyRow(
                icon: "wrench.and.screwdriver",
                title: L10n.text("privacy.operational.title", fallback: "Operational data"),
                detail: L10n.text(
                    "privacy.operational.detail",
                    fallback: "HT Portal processes the account and target-device association, a random installation ID, app version, session status, transport, quality, latency and errors. Security audit records also include the source IP and action."
                )
            )
            privacyRow(
                icon: "hand.raised",
                title: L10n.text("privacy.use.title", fallback: "Purpose and sharing"),
                detail: L10n.text(
                    "privacy.use.detail",
                    fallback: "This data is used only to establish, secure and troubleshoot the requested connection. It is not sold or shared with third parties and is not used for advertising, analytics or tracking."
                )
            )
            privacyRow(
                icon: "clock",
                title: L10n.text("privacy.retention.title", fallback: "Retention"),
                detail: L10n.text(
                    "privacy.retention.detail",
                    fallback: "Session diagnostics are retained for up to 30 days after disconnection. Security audit events are retained for up to 180 days."
                )
            )

            Text(L10n.text(
                "privacy.consent",
                fallback: "By continuing, you authorize the processing described above, which is necessary to provide the requested VPN session."
            ))
                .font(.footnote.weight(.medium))

            Link(
                L10n.text("privacy.policy", fallback: "Read the privacy policy"),
                destination: URL(string: "https://portal.hivetechnology.net/privacy-policy")!
            )
                .font(.footnote.weight(.semibold))
        }
        .padding(16)
        .frame(maxWidth: .infinity, alignment: .leading)
        .background(Color(uiColor: .secondarySystemBackground), in: RoundedRectangle(cornerRadius: 8))
    }

    private func privacyRow(icon: String, title: String, detail: String) -> some View {
        HStack(alignment: .top, spacing: 11) {
            Image(systemName: icon)
                .frame(width: 20)
                .foregroundStyle(Color.accentColor)
            VStack(alignment: .leading, spacing: 3) {
                Text(title).font(.subheadline.weight(.semibold))
                Text(detail)
                    .font(.footnote)
                    .foregroundStyle(.secondary)
            }
        }
    }

    private func diagnosticsView(_ diagnostics: TunnelDiagnostics) -> some View {
        VStack(alignment: .leading, spacing: 10) {
            Text(L10n.text("diagnostics.title", fallback: "VPN details"))
                .font(.caption.weight(.semibold))
                .foregroundStyle(.secondary)
                .textCase(.uppercase)
            diagnosticRow(
                L10n.text("diagnostics.address", fallback: "VPN address"),
                diagnostics.assignedAddress ?? "-"
            )
            diagnosticRow(
                L10n.text("diagnostics.runtime", fallback: "Runtime"),
                diagnostics.runtimeAddress ?? "-"
            )
            diagnosticRow(
                L10n.text("diagnostics.transport", fallback: "Transport"),
                localizedTransport(diagnostics.transport)
            )
            diagnosticRow(
                L10n.text("diagnostics.phase", fallback: "Phase"),
                localizedPhase(diagnostics.phase)
            )
            if !diagnostics.includedRoutes.isEmpty {
                diagnosticRow(
                    L10n.text("diagnostics.routes", fallback: "Routes"),
                    diagnostics.includedRoutes.joined(separator: ", ")
                )
            }
            if !diagnostics.skippedRoutes.isEmpty {
                diagnosticRow(
                    L10n.text("diagnostics.skipped_routes", fallback: "Excluded routes"),
                    diagnostics.skippedRoutes.joined(separator: ", ")
                )
            }
            DisclosureGroup(L10n.text("diagnostics.packets", fallback: "Packet diagnostics")) {
                Text(diagnostics.packetSummary)
                    .font(.caption2.monospaced())
                    .foregroundStyle(.secondary)
                    .textSelection(.enabled)
                    .padding(.top, 6)
            }
            .font(.caption)
        }
        .frame(maxWidth: .infinity, alignment: .leading)
    }

    private func diagnosticRow(_ label: String, _ value: String) -> some View {
        HStack(alignment: .firstTextBaseline, spacing: 8) {
            Text(label).foregroundStyle(.secondary)
            Spacer(minLength: 12)
            Text(value).font(.caption.monospaced()).multilineTextAlignment(.trailing)
        }
        .font(.caption)
    }

    private func localizedPhase(_ phase: String) -> String {
        let fallbacks = [
            "idle": "Idle",
            "starting": "Starting",
            "waiting-runtime": "Waiting for runtime",
            "connecting-n3n": "Connecting n3n",
            "checking-runtime": "Checking runtime",
            "connected": "Connected",
            "reconnecting": "Reconnecting",
            "failed": "Failed",
        ]
        guard let fallback = fallbacks[phase] else { return phase }
        return L10n.text("diagnostics.phase.\(phase)", fallback: fallback)
    }

    private func localizedTransport(_ transport: String) -> String {
        let fallbacks = [
            "unknown": "Unknown",
            "udp-adaptive": "UDP automatico",
            "udp-p2p": "UDP peer-to-peer",
            "udp-relay": "UDP relay",
            "tcp-relay": "TCP relay",
        ]
        guard let fallback = fallbacks[transport] else { return transport }
        return L10n.text("diagnostics.transport.\(transport)", fallback: fallback)
    }

    private var appVersion: String {
        Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String ?? ""
    }

    private var buildNumber: String {
        Bundle.main.object(forInfoDictionaryKey: "CFBundleVersion") as? String ?? ""
    }

    private var gplSourceURL: URL {
        if let configured = Bundle.main.object(forInfoDictionaryKey: "HTPGPLSourceURL") as? String,
           let url = URL(string: configured) {
            return url
        }
        return URL(string: "https://portal.hivetechnology.net/open-source/ht-portal-vpn/")!
    }
}

private struct ConnectionSignal: View {
    let state: MainViewModel.State

    var body: some View {
        ZStack {
            OrbitRing(diameter: 136, opacity: 0.55, duration: 4.8)
            OrbitRing(diameter: 100, opacity: 0.45, duration: 3.6, clockwise: false)
            RoundedRectangle(cornerRadius: 8)
                .fill(Color(uiColor: .secondarySystemBackground))
                .frame(width: 76, height: 76)
            Image("BrandMark").resizable().scaledToFit().frame(width: 54, height: 54)
            statusBadge.offset(x: 51, y: 51)
        }
        .frame(width: 144, height: 144)
    }

    @ViewBuilder
    private var statusBadge: some View {
        if state.isLoading {
            ProgressView()
                .controlSize(.small)
                .tint(Color.accentColor)
                .frame(width: 28, height: 28)
                .background(Color(uiColor: .systemBackground), in: Circle())
                .overlay(Circle().stroke(Color(uiColor: .separator).opacity(0.45), lineWidth: 1))
        } else if state.phase == "connected" {
            Image(systemName: "checkmark.circle.fill")
                .font(.system(size: 24))
                .foregroundStyle(.green)
                .frame(width: 28, height: 28)
                .background(Color(uiColor: .systemBackground), in: Circle())
        } else if state.error != nil || state.phase == "failed" {
            Image(systemName: "exclamationmark.circle.fill")
                .font(.system(size: 24))
                .foregroundStyle(.red)
                .frame(width: 28, height: 28)
                .background(Color(uiColor: .systemBackground), in: Circle())
        }
    }
}

private struct OrbitRing: View {
    let diameter: CGFloat
    let opacity: Double
    let duration: Double
    var clockwise = true

    var body: some View {
        TimelineView(.animation(minimumInterval: 1.0 / 30.0, paused: false)) { context in
            let elapsed = context.date.timeIntervalSinceReferenceDate
            let progress = elapsed.truncatingRemainder(dividingBy: duration) / duration
            let direction = clockwise ? 1.0 : -1.0

            ZStack {
                Circle().stroke(Color(uiColor: .separator).opacity(opacity), lineWidth: 1)
                Circle()
                    .fill(Color(red: 247.0 / 255.0, green: 152.0 / 255.0, blue: 29.0 / 255.0))
                    .frame(width: 9, height: 9)
                    .shadow(color: Color.orange.opacity(0.45), radius: 5)
                    .offset(x: -diameter / 2)
            }
            .rotationEffect(.degrees(progress * 360.0 * direction))
        }
        .frame(width: diameter, height: diameter)
    }
}
