//
//  ChatroomView.swift
//  damus
//
//  Created by William Casarin on 2022-04-19.
//

import SwiftUI
import SwipeActions
import TipKit

struct ChatroomThreadView: View {
    @Environment(\.dismiss) var dismiss
    @State var once: Bool = false
    let damus: DamusState
    @ObservedObject var thread: ThreadModel
    @State var highlighted_note_id: NoteId? = nil
    @State var user_just_posted_flag: Bool = false
    @State var untrusted_network_expanded: Bool = true
    @Namespace private var animation

    // Add state for sticky header
    @State var showStickyHeader: Bool = false
    @State var untrustedSectionOffset: CGFloat = 0

    // Add state for reading progress (longform articles)
    @State private var readingProgress: CGFloat = 0
    @State private var viewportHeight: CGFloat = 0
    @State private var contentTopY: CGFloat = 0
    @State private var contentBottomY: CGFloat = 0
    @State private var initialTopY: CGFloat? = nil

    // Focus mode: auto-hide chrome (nav bar + tab bar) during longform reading
    @State private var chromeHidden: Bool = false
    @State private var lastScrollY: CGFloat = 0
    /// Minimum scroll distance before triggering chrome hide/show
    private let scrollThreshold: CGFloat = 15

    private static let untrusted_network_section_id = "untrusted-network-section"
    private static let sticky_header_adjusted_anchor = UnitPoint(x: UnitPoint.top.x, y: 0.2)

    /// Returns true if the selected event is a longform article (kind 30023).
    var isLongformEvent: Bool {
        thread.selected_event.kind == 30023
    }

    /// Updates reading progress based on scroll position.
    private func updateReadingProgress() {
        guard thread.selected_event.kind == 30023 else { return }
        guard viewportHeight > 0 else { return }

        // Capture initial position on first update
        if initialTopY == nil {
            initialTopY = contentTopY
        }
        guard let startY = initialTopY else { return }

        // Content height is constant (bottom - top in global coords)
        let contentHeight = contentBottomY - contentTopY
        guard contentHeight > 0 else { return }

        // How much we've scrolled from initial position
        // As we scroll down, contentTopY decreases, so scrolled = startY - currentTopY
        let scrolled = startY - contentTopY
        let maxScroll = max(contentHeight - viewportHeight, 1)

        let progress = scrolled / maxScroll
        readingProgress = min(max(progress, 0), 1)
    }

    /// Updates chrome visibility based on scroll direction (longform only).
    /// Scrolling down hides chrome; tap to restore (scroll up does not restore).
    private func updateChromeVisibility(newY: CGFloat) {
        guard isLongformEvent else { return }

        let delta = newY - lastScrollY

        // Only hide chrome on scroll down, don't restore on scroll up (use tap instead)
        if delta < -scrollThreshold && !chromeHidden {
            withAnimation(.easeInOut(duration: 0.25)) {
                chromeHidden = true
            }
            notify(.display_tabbar(false))
        }

        // Always update lastScrollY to prevent stale delta accumulation
        lastScrollY = newY
    }

    /// Shows chrome (nav bar + tab bar) - called on tap or when leaving view.
    private func showChrome() {
        guard chromeHidden else { return }
        withAnimation(.easeInOut(duration: 0.25)) {
            chromeHidden = false
        }
        notify(.display_tabbar(true))
    }

    func go_to_event(scroller: ScrollViewProxy, note_id: NoteId) {
        let adjustedAnchor: UnitPoint = showStickyHeader ? ChatroomThreadView.sticky_header_adjusted_anchor : .top

        scroll_to_event(scroller: scroller, id: note_id, delay: 0, animate: true, anchor: adjustedAnchor)
        highlighted_note_id = note_id
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5, execute: {
            withAnimation {
                highlighted_note_id = nil
            }
        })
    }

    func set_active_event(scroller: ScrollViewProxy, ev: NdbNote) {
        withAnimation {
            self.thread.select(event: ev)
            self.go_to_event(scroller: scroller, note_id: ev.id)
        }
    }

    func trusted_event_filter(_ event: NostrEvent) -> Bool {
        // Always trust our own replies; otherwise gate by trusted network when the setting is enabled
        if event.pubkey == damus.pubkey {
            return true
        }
        if !damus.settings.show_trusted_replies_first {
            return true
        }
        return damus.contacts.is_in_friendosphere(event.pubkey)
    }

    func ThreadedSwipeViewGroup(scroller: ScrollViewProxy, events: [NostrEvent]) -> some View {
        SwipeViewGroup {
            ForEach(Array(zip(events, events.indices)), id: \.0.id) { (ev, ind) in
                ChatEventView(event: events[ind],
                              selected_event: self.thread.selected_event,
                              prev_ev: ind > 0 ? events[ind-1] : nil,
                              next_ev: ind == events.count-1 ? nil : events[ind+1],
                              damus_state: damus,
                              thread: thread,
                              scroll_to_event: { note_id in
                    self.go_to_event(scroller: scroller, note_id: note_id)
                },
                              focus_event: {
                    self.set_active_event(scroller: scroller, ev: ev)
                },
                              highlight_bubble: highlighted_note_id == ev.id
                )
                .id(ev.id)
                .matchedGeometryEffect(id: ev.id.hex(), in: animation, anchor: .center)
                .padding(.horizontal)
            }
        }
    }

    var OutsideTrustedNetworkLabel: some View {
        HStack {
            Label(
                NSLocalizedString(
                    "Replies outside your trusted network",
                    comment: "Section title in thread for replies from outside of the current user's trusted network, which is their follows and follows of follows."),
                systemImage: "network.slash"
            )
            Spacer()
            Image(systemName: "chevron.right")
                .rotationEffect(.degrees(untrusted_network_expanded ? 90 : 0))
                .animation(.easeInOut(duration: 0.1), value: untrusted_network_expanded)
        }
        .foregroundColor(.secondary)
    }

    var StickyHeaderView: some View {
        OutsideTrustedNetworkLabel
            .padding(.horizontal)
            .padding(.vertical, 12)
            .background(
                Color(UIColor.systemBackground)
                    .shadow(color: .black.opacity(0.15), radius: 3, x: 0, y: 2)
            )
    }

    var body: some View {
        ScrollViewReader { scroller in
            let sorted_child_events = thread.sorted_child_events

            let untrusted_events = sorted_child_events.filter { !trusted_event_filter($0) }
            let trusted_events = sorted_child_events.filter { trusted_event_filter($0) }

            ZStack(alignment: .top) {
                ScrollView(.vertical) {
                    VStack(spacing: 0) {
                    // Top scroll position tracker
                    GeometryReader { geo in
                        Color.clear
                            .onChange(of: geo.frame(in: .global).minY) { newY in
                                contentTopY = newY
                                updateReadingProgress()
                                updateChromeVisibility(newY: newY)
                            }
                            .onAppear {
                                contentTopY = geo.frame(in: .global).minY
                                lastScrollY = geo.frame(in: .global).minY
                            }
                    }
                    .frame(height: 1)

                    LazyVStack(alignment: .leading, spacing: 8) {
                        // MARK: - Parents events view
                        ForEach(thread.parent_events, id: \.id) { parent_event in
                            EventMutingContainerView(damus_state: damus, event: parent_event) {
                                EventView(damus: damus, event: parent_event)
                                    .matchedGeometryEffect(id: parent_event.id.hex(), in: animation, anchor: .center)
                            }
                            .padding(.horizontal)
                            .onTapGesture {
                                self.set_active_event(scroller: scroller, ev: parent_event)
                            }
                            .id(parent_event.id)

                            Divider()
                                .padding(.top, 4)
                                .padding(.leading, 25 * 2)

                        }.background(GeometryReader { geometry in
                            let eventHeight = geometry.frame(in: .global).height

                            Rectangle()
                                .fill(Color.gray.opacity(0.25))
                                .frame(width: 2, height: eventHeight)
                                .offset(x: 40, y: 40)
                        })

                        // MARK: - Actual event view
                        EventMutingContainerView(
                            damus_state: damus,
                            event: self.thread.selected_event,
                            muteBox: { event_shown, muted_reason in
                                AnyView(
                                    EventMutedBoxView(shown: event_shown, reason: muted_reason)
                                        .padding(5)
                                )
                            }
                        ) {
                            SelectedEventView(damus: damus, event: self.thread.selected_event, size: .selected)
                                .matchedGeometryEffect(id: self.thread.selected_event.id.hex(), in: animation, anchor: .center)
                        }
                        .id(self.thread.selected_event.id)

                        // MARK: - Children view - inside trusted network
                        if !trusted_events.isEmpty {
                            ThreadedSwipeViewGroup(scroller: scroller, events: trusted_events)
                        }
                    }
                    // Remove top padding for longform articles with sepia to eliminate gap
                    .padding(.top, isLongformEvent && damus.settings.longform_sepia_mode ? 0 : nil)

                    // MARK: - Children view - outside trusted network
                    if !untrusted_events.isEmpty {
                        if #available(iOS 17, *) {
                            TipView(TrustedNetworkRepliesTip.shared, arrowEdge: .bottom)
                                .padding(.top, 10)
                                .padding(.horizontal)
                        }

                        VStack(alignment: .leading, spacing: 0) {
                            // Track this section's position
                            Color.clear
                                .frame(height: 1)
                                .background(
                                    GeometryReader { proxy in
                                        Color.clear
                                            .onAppear {
                                                untrustedSectionOffset = proxy.frame(in: .global).minY
                                            }
                                            .onChange(of: proxy.frame(in: .global).minY) { newY in
                                                let shouldShow = newY <= 100 // Adjust this threshold as needed
                                                if shouldShow != showStickyHeader {
                                                    withAnimation(.easeInOut(duration: 0.3)) {
                                                        showStickyHeader = shouldShow
                                                    }
                                                }
                                            }
                                    }
                                )

                            Button(action: {
                                withAnimation {
                                    untrusted_network_expanded.toggle()

                                    if #available(iOS 17, *) {
                                        TrustedNetworkRepliesTip.shared.invalidate(reason: .actionPerformed)
                                    }

                                    scroll_to_event(scroller: scroller, id: ChatroomThreadView.untrusted_network_section_id, delay: 0.1, animate: true, anchor: ChatroomThreadView.sticky_header_adjusted_anchor)
                                }
                            }) {
                                OutsideTrustedNetworkLabel
                            }
                            .id(ChatroomThreadView.untrusted_network_section_id)
                            .buttonStyle(PlainButtonStyle())
                            .padding(.horizontal)

                            if untrusted_network_expanded {
                                withAnimation {
                                    LazyVStack(alignment: .leading, spacing: 8) {
                                        ThreadedSwipeViewGroup(scroller: scroller, events: untrusted_events)
                                    }
                                    .padding(.top, 10)
                                }
                            }
                        }
                    }

                    // Bottom scroll position tracker - placed before EndBlock so we measure article content, not padding
                    GeometryReader { geo in
                        Color.clear
                            .onChange(of: geo.frame(in: .global).minY) { newY in
                                contentBottomY = newY
                                updateReadingProgress()
                            }
                            .onAppear {
                                contentBottomY = geo.frame(in: .global).minY
                            }
                    }
                    .frame(height: 1)

                    EndBlock()

                    HStack {}
                        .frame(height: tabHeight + getSafeAreaBottom())
                    } // End VStack wrapper
                }
                .background(
                    GeometryReader { geo in
                        Color.clear
                            .onAppear {
                                viewportHeight = geo.size.height
                            }
                            .onChange(of: geo.size.height) { newHeight in
                                // Reset baseline on significant height change (orientation, text size)
                                if abs(newHeight - viewportHeight) > 50 {
                                    initialTopY = nil
                                }
                                viewportHeight = newHeight
                                updateReadingProgress()
                            }
                    }
                )

                if showStickyHeader && !untrusted_events.isEmpty {
                    VStack {
                        StickyHeaderView
                            .onTapGesture {
                                withAnimation {
                                    untrusted_network_expanded.toggle()
                                }
                            }
                        Spacer()
                    }
                    .transition(.move(edge: .top).combined(with: .opacity))
                    .zIndex(1)
                }

                // Reading progress bar - show for longform articles
                if thread.selected_event.kind == 30023 {
                    VStack(spacing: 0) {
                        ReadingProgressBar(progress: readingProgress)
                        Spacer()
                    }
                    .zIndex(100)
                }
            }
            .onReceive(handle_notify(.post), perform: { notify in
                switch notify {
                case .post(_):
                    user_just_posted_flag = true
                case .cancel:
                    return
                }
            })
            .onReceive(thread.objectWillChange) {
                if let last_event = thread.events.last, last_event.pubkey == damus.pubkey, user_just_posted_flag {
                    self.go_to_event(scroller: scroller, note_id: last_event.id)
                    user_just_posted_flag = false
                }
            }
            .onAppear() {
                thread.subscribe()
                // Use .top anchor for longform articles so they open at the title,
                // keep .bottom for regular notes to preserve parent context visibility
                let anchor: UnitPoint = thread.selected_event.known_kind == .longform ? .top : .bottom
                scroll_to_event(scroller: scroller, id: thread.selected_event.id, delay: 0.1, animate: false, anchor: anchor)
                // Ensure chrome is visible when view appears (handles interrupted transitions)
                if isLongformEvent {
                    chromeHidden = false
                    notify(.display_tabbar(true))
                }
            }
            .onChange(of: thread.selected_event.id) { _ in
                // Reset reading progress when switching to a different event
                initialTopY = nil
                readingProgress = 0
                // Restore chrome when switching events (user tapped to select)
                showChrome()
            }
            .onDisappear() {
                thread.unsubscribe()
                showChrome()  // Restore chrome when leaving view
            }
            .navigationBarHidden(chromeHidden && isLongformEvent)
            // Tap anywhere to show chrome when hidden (doesn't block other gestures)
            .simultaneousGesture(
                TapGesture()
                    .onEnded { _ in
                        if isLongformEvent && chromeHidden {
                            showChrome()
                        }
                    }
            )
        }
    }
}

struct ChatroomView_Previews: PreviewProvider {
    static var previews: some View {
        Group {
            ChatroomThreadView(damus: test_damus_state, thread: ThreadModel(event: test_note, damus_state: test_damus_state))
                .previewDisplayName("Test note")
            
            let test_thread = ThreadModel(event: test_thread_note_1, damus_state: test_damus_state)
            ChatroomThreadView(damus: test_damus_state, thread: test_thread)
                .onAppear {
                    test_thread.add_event(test_thread_note_2, keypair: test_keypair)
                    test_thread.add_event(test_thread_note_3, keypair: test_keypair)
                    test_thread.add_event(test_thread_note_4, keypair: test_keypair)
                    test_thread.add_event(test_thread_note_5, keypair: test_keypair)
                    test_thread.add_event(test_thread_note_6, keypair: test_keypair)
                    test_thread.add_event(test_thread_note_7, keypair: test_keypair)
                }
        }
    }
}

