//
//  WidescreenNavigationPrefsView.swift
//  Tusker
//
//  Created by Shadowfacts on 9/2/23.
//  Copyright © 2023 Shadowfacts. All rights reserved.
//

import SwiftUI
import Combine

struct WidescreenNavigationPrefsView: View {
    @ObservedObject private var preferences = Preferences.shared
    @State private var startAnimation = PassthroughSubject<Void, Never>()
    
    var body: some View {
        HStack {
            Spacer()
            
            OptionView<StackNavigationPreview>(
                value: .stack,
                selection: $preferences.widescreenNavigationMode,
                startAnimation: startAnimation
            ) {
                Text("Stack")
            }
            
            Spacer(minLength: 32)
            
            OptionView<SplitNavigationPreview>(
                value: .splitScreen,
                selection: $preferences.widescreenNavigationMode,
                startAnimation: startAnimation
            ) {
                Text("Split Screen")
            }
            
            if preferences.hasFeatureFlag(.iPadMultiColumn) {
                Spacer(minLength: 32)
                
                OptionView<MultiColumnNavigationPreview>(
                    value: .multiColumn,
                    selection: $preferences.widescreenNavigationMode,
                    startAnimation: startAnimation
                ) {
                    Text("Multi-Column")
                }
            }
            
            Spacer()
        }
        .frame(height: 100)
        .onAppear {
            DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
                startAnimation.send()
            }
        }
    }
}

private struct OptionView<Content: NavigationModePreview>: View {
    let value: Preferences.WidescreenNavigationMode
    @Binding var selection: Preferences.WidescreenNavigationMode
    let startAnimation: PassthroughSubject<Void, Never>
    @ViewBuilder let label: Text
    @Environment(\.colorScheme) private var colorScheme
    
    private var selected: Bool {
        selection == value
    }
    
    var body: some View {
        Button(action: self.selectValue) {
            VStack {
                preview
                
                label
                    .foregroundStyle(selected ? Color.white : .primary)
                    .background(selected ? AnyShapeStyle(.tint) : AnyShapeStyle(.clear), in: WideCapsule())
            }
        }
        .buttonStyle(.plain)
    }
    
    private var preview: some View {
        NavigationModeRepresentable<Content>(startAnimation: startAnimation)
            .clipShape(RoundedRectangle(cornerRadius: 12.5, style: .continuous))
            .overlay {
                RoundedRectangle(cornerRadius: 12.5, style: .continuous)
                    .stroke(.gray, lineWidth: 3)
            }
            .aspectRatio(4/3, contentMode: .fit)
    }
    
    private func selectValue() {
        selection = value
    }
}

private struct WideCapsule: Shape {
    func path(in rect: CGRect) -> Path {
        Capsule().path(in: rect.insetBy(dx: -6, dy: -2))
    }
}

private protocol NavigationModePreview: UIView {
    init(startAnimation: PassthroughSubject<Void, Never>)
}

private struct NavigationModeRepresentable<UIViewType: NavigationModePreview>: UIViewRepresentable {
    let startAnimation: PassthroughSubject<Void, Never>
    
    func makeUIView(context: Context) -> UIViewType {
        UIViewType(startAnimation: startAnimation)
    }
    
    func updateUIView(_ uiView: UIViewType, context: Context) {
    }
}

private let timingParams = UISpringTimingParameters(mass: 1, stiffness: 70, damping: 16, initialVelocity: .zero)

private final class StackNavigationPreview: UIView, NavigationModePreview {
    private let cellStack = UIStackView(arrangedSubviews: [CellView(), CellView(), CellView()])
    private let destinationView = UIView()
    private var cancellable: AnyCancellable?
    
    init(startAnimation: PassthroughSubject<Void, Never>) {
        super.init(frame: .zero)
        
        backgroundColor = .appBackground
        layer.masksToBounds = true
        
        cellStack.axis = .vertical
        cellStack.spacing = 0
        cellStack.distribution = .fillEqually
        cellStack.translatesAutoresizingMaskIntoConstraints = false
        addSubview(cellStack)
        cellStack.arrangedSubviews[1].backgroundColor = .clear
        
        destinationView.translatesAutoresizingMaskIntoConstraints = false
        destinationView.backgroundColor = .tintColor
        destinationView.isHidden = true
        addSubview(destinationView)
        
        NSLayoutConstraint.activate([
            cellStack.leadingAnchor.constraint(equalTo: leadingAnchor),
            cellStack.trailingAnchor.constraint(equalTo: trailingAnchor),
            cellStack.topAnchor.constraint(equalTo: topAnchor, constant: 4),
            cellStack.bottomAnchor.constraint(equalTo: bottomAnchor),
            
            destinationView.leadingAnchor.constraint(equalTo: leadingAnchor),
            destinationView.trailingAnchor.constraint(equalTo: trailingAnchor),
            destinationView.topAnchor.constraint(equalTo: topAnchor),
            destinationView.bottomAnchor.constraint(equalTo: bottomAnchor),
        ])
        
        cancellable = startAnimation.sink { [unowned self] _ in
            self.startAnimation()
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func startAnimation() {
        destinationView.transform = CGAffineTransform(translationX: destinationView.bounds.width, y: 0)
        destinationView.isHidden = false
        
        let animator = UIViewPropertyAnimator(duration: 1, timingParameters: timingParams)
        animator.addAnimations {
            self.cellStack.arrangedSubviews[1].backgroundColor = .tertiaryLabel
            self.cellStack.transform = CGAffineTransform(translationX: -0.3 * self.cellStack.bounds.width, y: 0)
            self.destinationView.transform = .identity
        }
        animator.addCompletion { [weak self] _ in
            self?.reverseAnimation()
        }
        animator.startAnimation(afterDelay: 0.6)
    }
    
    private func reverseAnimation() {
        let animator = UIViewPropertyAnimator(duration: 1, timingParameters: timingParams)
        animator.addAnimations {
            self.cellStack.arrangedSubviews[1].backgroundColor = .clear
            self.cellStack.transform = .identity
            self.destinationView.transform = CGAffineTransform(translationX: self.destinationView.bounds.width, y: 0)
        }
        animator.addCompletion { [weak self] _ in
            self?.startAnimation()
        }
        animator.startAnimation(afterDelay: 0.5)
    }
}

private final class SplitNavigationPreview: UIView, NavigationModePreview {
    private let cellStack = UIStackView(arrangedSubviews: [CellView(), CellView(), CellView()])
    private let destinationView = UIView()
    private var cellStackTrailingConstraint: NSLayoutConstraint!
    private var cancellable: AnyCancellable?
    
    init(startAnimation: PassthroughSubject<Void, Never>) {
        super.init(frame: .zero)
        
        backgroundColor = .appBackground
        layer.masksToBounds = true
        
        cellStack.axis = .vertical
        cellStack.spacing = 0
        cellStack.distribution = .fillEqually
        cellStack.translatesAutoresizingMaskIntoConstraints = false
        addSubview(cellStack)
        cellStack.arrangedSubviews[1].backgroundColor = .clear
        
        destinationView.translatesAutoresizingMaskIntoConstraints = false
        destinationView.backgroundColor = .tintColor
        destinationView.isHidden = true
        addSubview(destinationView)
        
        cellStackTrailingConstraint = cellStack.trailingAnchor.constraint(equalTo: trailingAnchor)
        NSLayoutConstraint.activate([
            cellStack.leadingAnchor.constraint(equalTo: leadingAnchor),
            cellStackTrailingConstraint,
            cellStack.topAnchor.constraint(equalTo: topAnchor, constant: 4),
            cellStack.bottomAnchor.constraint(equalTo: bottomAnchor),
            
            destinationView.leadingAnchor.constraint(equalTo: centerXAnchor),
            destinationView.trailingAnchor.constraint(equalTo: trailingAnchor),
            destinationView.topAnchor.constraint(equalTo: topAnchor),
            destinationView.bottomAnchor.constraint(equalTo: bottomAnchor),
        ])
        
        cancellable = startAnimation.sink { [unowned self] _ in
            self.startAnimation()
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    private func startAnimation() {
        destinationView.transform = CGAffineTransform(translationX: destinationView.bounds.width, y: 0)
        destinationView.isHidden = false
        
        cellStackTrailingConstraint.isActive = false
        cellStackTrailingConstraint = cellStack.trailingAnchor.constraint(equalTo: centerXAnchor)
        cellStackTrailingConstraint.isActive = true
        
        let animator = UIViewPropertyAnimator(duration: 1, timingParameters: timingParams)
        animator.addAnimations {
            self.layoutIfNeeded()
            self.destinationView.transform = .identity
        }
        animator.addCompletion { [weak self] _ in
            self?.reverseAnimation()
        }
        animator.startAnimation(afterDelay: 0.6)
        
        UIView.animate(withDuration: 0.1, delay: 0.5, options: .curveEaseIn) {
            self.cellStack.arrangedSubviews[1].backgroundColor = .tertiaryLabel
        } completion: { _ in
            UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut) {
                self.cellStack.arrangedSubviews[1].backgroundColor = .clear
            }
        }
    }
    
    private func reverseAnimation() {
        cellStackTrailingConstraint.isActive = false
        cellStackTrailingConstraint = cellStack.trailingAnchor.constraint(equalTo: trailingAnchor)
        cellStackTrailingConstraint.isActive = true
        
        let animator = UIViewPropertyAnimator(duration: 1, timingParameters: timingParams)
        animator.addAnimations {
            self.layoutIfNeeded()
            self.destinationView.transform = CGAffineTransform(translationX: self.destinationView.bounds.width, y: 0)
        }
        animator.addCompletion { [weak self] _ in
            self?.startAnimation()
        }
        animator.startAnimation(afterDelay: 0.5)
    }
}

private final class MultiColumnNavigationPreview: UIView, NavigationModePreview {
    private static let columnSpacing: CGFloat = 5
    
    private let cellStack1 = UIStackView(arrangedSubviews: [CellView(), CellView(), CellView()])
    private let cellStack2 = UIStackView(arrangedSubviews: [CellView(), CellView(), CellView()])
    private let destinationView = UIView()
    private var cancellable: AnyCancellable?
    
    private var startedAnimation = false

    init(startAnimation: PassthroughSubject<Void, Never>) {
        super.init(frame: .zero)
        
        backgroundColor = .appSecondaryBackground
        layer.masksToBounds = true
        
        cellStack1.axis = .vertical
        cellStack1.spacing = 0
        cellStack1.distribution = .fillEqually
        cellStack1.backgroundColor = .appBackground
        cellStack1.layer.cornerRadius = 6
        cellStack1.translatesAutoresizingMaskIntoConstraints = false
        addSubview(cellStack1)
        
        cellStack2.axis = .vertical
        cellStack2.spacing = 0
        cellStack2.distribution = .fillEqually
        cellStack2.backgroundColor = .appBackground
        cellStack2.layer.cornerRadius = 6
        cellStack2.translatesAutoresizingMaskIntoConstraints = false
        addSubview(cellStack2)
        cellStack2.arrangedSubviews[1].backgroundColor = .clear
        
        destinationView.translatesAutoresizingMaskIntoConstraints = false
        destinationView.backgroundColor = .tintColor
        destinationView.layer.cornerRadius = 6
        destinationView.layer.opacity = 0
        addSubview(destinationView)
        
        NSLayoutConstraint.activate([
            cellStack1.leadingAnchor.constraint(equalTo: leadingAnchor, constant: Self.columnSpacing),
            cellStack1.widthAnchor.constraint(equalToConstant: 50),
            cellStack1.topAnchor.constraint(equalTo: topAnchor, constant: Self.columnSpacing),
            cellStack1.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Self.columnSpacing),
            
            cellStack2.leadingAnchor.constraint(equalTo: cellStack1.trailingAnchor, constant: Self.columnSpacing),
            cellStack2.widthAnchor.constraint(equalToConstant: 50),
            cellStack2.topAnchor.constraint(equalTo: topAnchor, constant: Self.columnSpacing),
            cellStack2.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Self.columnSpacing),
            
            destinationView.leadingAnchor.constraint(equalTo: cellStack2.trailingAnchor, constant: Self.columnSpacing),
            destinationView.widthAnchor.constraint(equalToConstant: 50),
            destinationView.topAnchor.constraint(equalTo: topAnchor, constant: Self.columnSpacing),
            destinationView.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -Self.columnSpacing),
        ])
        
        cancellable = startAnimation.sink { [unowned self] _ in
            self.startedAnimation = true
            self.startAnimation()
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    override func layoutSubviews() {
        super.layoutSubviews()
        if !startedAnimation {
            setUnscrolledTransform()
        }
    }
    
    private func startAnimation() {
        let totalWidth = 50 * 3 + Self.columnSpacing * 4
        let offset = bounds.width - totalWidth
        let transform = CGAffineTransform(translationX: offset, y: 0)
        
        let animator = UIViewPropertyAnimator(duration: 1, timingParameters: timingParams)
        animator.addAnimations {
            self.cellStack1.transform = transform
            self.cellStack2.transform = transform
            self.destinationView.transform = transform
            self.destinationView.layer.opacity = 1
        }
        animator.addCompletion { [weak self] _ in
            self?.reverseAnimation()
        }
        animator.startAnimation(afterDelay: 0.6)
        
        UIView.animate(withDuration: 0.1, delay: 0.5, options: .curveEaseIn) {
            self.cellStack2.arrangedSubviews[1].backgroundColor = .tertiaryLabel
        } completion: { _ in
            UIView.animate(withDuration: 0.3, delay: 0, options: .curveEaseOut) {
                self.cellStack2.arrangedSubviews[1].backgroundColor = .clear
            }
        }
    }
    
    private func reverseAnimation() {
        let animator = UIViewPropertyAnimator(duration: 1, timingParameters: timingParams)
        animator.addAnimations {
            self.setUnscrolledTransform()
            self.destinationView.layer.opacity = 0
        }
        animator.addCompletion { [weak self] _ in
            self?.startAnimation()
        }
        animator.startAnimation(afterDelay: 0.5)
    }
    
    private func setUnscrolledTransform() {
        let totalWidth = 50 * 2 + Self.columnSpacing * 3
        let offset = bounds.width - totalWidth
        let transform = CGAffineTransform(translationX: offset, y: 0)
        
        cellStack1.transform = transform
        cellStack2.transform = transform
        destinationView.transform = transform
    }
}

private class CellView: UIView {
    init() {
        super.init(frame: .zero)
        
        let avatarView = UIView()
        avatarView.backgroundColor = .tertiaryLabel
        avatarView.layer.cornerRadius = 1
        avatarView.translatesAutoresizingMaskIntoConstraints = false
        addSubview(avatarView)
        
        let line1 = UIView()
        line1.backgroundColor = .tertiaryLabel
        line1.translatesAutoresizingMaskIntoConstraints = false
        addSubview(line1)
        
        let line2 = UIView()
        line2.backgroundColor = .tertiaryLabel
        line2.translatesAutoresizingMaskIntoConstraints = false
        addSubview(line2)
        
        let line3 = UIView()
        line3.backgroundColor = .tertiaryLabel
        line3.translatesAutoresizingMaskIntoConstraints = false
        addSubview(line3)
        
        NSLayoutConstraint.activate([
            avatarView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 8),
            avatarView.topAnchor.constraint(equalTo: topAnchor, constant: 4),
            avatarView.widthAnchor.constraint(equalToConstant: 10),
            avatarView.heightAnchor.constraint(equalToConstant: 10),
            
            line1.topAnchor.constraint(equalTo: avatarView.topAnchor),
            line1.leadingAnchor.constraint(equalTo: avatarView.trailingAnchor, constant: 4),
            line1.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -8),
            line1.heightAnchor.constraint(equalToConstant: 4),
            
            line2.topAnchor.constraint(equalTo: line1.bottomAnchor, constant: 2),
            line2.leadingAnchor.constraint(equalTo: line1.leadingAnchor),
            line2.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -24),
            line2.heightAnchor.constraint(equalToConstant: 4),
            
            line3.topAnchor.constraint(equalTo: line2.bottomAnchor, constant: 2),
            line3.leadingAnchor.constraint(equalTo: line1.leadingAnchor),
            line3.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -16),
            line3.heightAnchor.constraint(equalToConstant: 4),
        ])
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

#Preview {
    WidescreenNavigationPrefsView()
}
