import ComponentLibrary
import ComposableArchitecture
import SwiftUI

/*
 Controls presenting the expanded and collapsed EditClipCoordinator.
 By default, this will also show the ClipEditsScreen when the sheet is collapsed.
 EditClipPlayer is surfaced in the collapsed sheet as well.
 */
public struct EditClipPresentation: ViewModifier {
    var store: StoreOf<EditClipCoordinator>?
    @State private var detectedDetent: PresentationDetent = .large
    @State private var showClipEditsScreen: Bool = true
    @State private var showEditor: Bool = false
    @State private var editorCornerRadius: Double = 40.0
    @State private var didAppearForFirstTime: Bool = false

    let collapsedSheetDetent: PresentationDetent = .height(150)
    // We use an intermediate `collapsedSheetThresholdDetent` to avoid
    // a common case where the sheet can expand to the full height, without
    // triggering the sheet content to expand. This is because `onChange` is
    // only triggered when the sheet detent changes to .large, leaving us
    // in an intermediate state of a full sheet but collapsed content.
    let collapsedSheetThresholdDetent: PresentationDetent = .height(160)
    public init(store: StoreOf<EditClipCoordinator>?) {
        self.store = store
    }

    public func body(content: Content) -> some View {
        content
            .overlay {
                if let store, showClipEditsScreen {
                    ClipEditsScreen(store: store.scope(state: \.clipEdits, action: \.clipEdits))
                        .modifier(if: store.forceDarkMode) {
                            $0.preferredColorScheme(.dark) // Locking to dark mode for consistency with hooks
                        }
                        .onAppear {
                            DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                                withAnimation(.easeInOut(duration: 0.5)) {
                                    showEditor = true
                                    didAppearForFirstTime = true
                                }
                            }
                        }
                        .sheet(isPresented: $showEditor) {
                            EditClipCoordinatorScreen(store: store)
                                .presentationDetents([collapsedSheetDetent, collapsedSheetThresholdDetent, .large], selection: $detectedDetent)
                                .presentationBackgroundInteraction(store.isCollapsed ? .enabled(upThrough: collapsedSheetDetent) : .enabled)
                                .presentationDragIndicator(.hidden)
                                .presentationCornerRadius(editorCornerRadius)
                                .interactiveDismissDisabled()
                                .overlay(alignment: .top) {
                                    // Drag area for convenience around the drag indicator
                                    // Small swipes here will quickly expand the sheet
                                    // if the sheet is collapsed
                                    dragOverlay
                                }
                                .presentationBackground {
                                    Color.SemanticV1.backgroundSecondary.ignoresSafeArea(.all, edges: .bottom)
                                }
                        }
                        .transition(.move(edge: .bottom).combined(with: .opacity))
                        .animation(.easeInOut(duration: 0.3), value: editorCornerRadius)
                }
            }
            .onChange(of: store?.isCollapsed ?? false) { _, isCollapsed in
                if isCollapsed, detectedDetent != collapsedSheetDetent {
                    detectedDetent = collapsedSheetDetent
                } else if !isCollapsed, detectedDetent != .large {
                    detectedDetent = .large
                }
            }
            .onChange(of: detectedDetent) { oldValue, newValue in
                if newValue != oldValue, oldValue == .large {
                    store?.send(.setCollapsed(true))
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                        editorCornerRadius = 20.0
                    }
                } else if newValue != oldValue, oldValue == collapsedSheetDetent {
                    store?.send(.setCollapsed(false))
                    DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                        editorCornerRadius = 40.0
                    }
                }
            }
            .onChange(of: store?.showClipEdits ?? true) { _, showClipEdits in
                withAnimation(.spring(response: 0.5, dampingFraction: 0.8, blendDuration: 0)) {
                    showClipEditsScreen = showClipEdits
                }

                // If we're dismissing Clip Edits, dismiss the Editor too
                // This has to be done slightly faster
                guard !showClipEdits else { return }
                withAnimation(.easeInOut(duration: 0.1)) {
                    showEditor = false
                }

                // Wait for Clip Edits and the Editor to dismiss
                // before dismissing the entire Coordinator
                DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
                    store?.send(.dismiss)
                }
            }
            .onChange(of: store?.scope(state: \.extend, action: \.extend)) { oldValue, newValue in
                // Check if the old or new Extend state is nil to determine if we're dismissing or appearing
                let shouldDismiss = newValue != oldValue && newValue == nil && oldValue != nil
                let shouldAppear = newValue != oldValue && newValue != nil && oldValue == nil

                if shouldDismiss {
                    withAnimation(.easeInOut(duration: 0.5)) {
                        showEditor = false
                    }
                } else if shouldAppear, didAppearForFirstTime {
                    // Only trigger the Editor from here if we've already appeared for the first time
                    withAnimation(.easeInOut(duration: 0.5)) {
                        showEditor = true
                    }
                }
            }
    }

    @ViewBuilder
    private var dragOverlay: some View {
        if let store {
            VStack {
                RoundedRectangle(cornerRadius: 10)
                    .fill(Color.clear)
                    .frame(width: 142, height: 32)
                    .contentShape(.rect)
                    .gesture(
                        DragGesture(minimumDistance: 0)
                            .onChanged { value in
                                // If we're dragging vertically, and we're collapsed, expand the editor
                                guard abs(value.translation.height) > 10, store.isCollapsed else { return }
                                detectedDetent = .large
                                DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
                                    store.send(.setCollapsed(false))
                                }
                            }
                    )
            }
        }
    }
}
