//
//  ToolbarController.swift
//  ComposeUI
//
//  Created by Shadowfacts on 3/7/23.
//

import SwiftUI
import Pachyderm
import TuskerComponents

class ToolbarController: ViewController {
    static let height: CGFloat = 44
    
    unowned let parent: ComposeController
    
    @Published var minWidth: CGFloat?
    @Published var realWidth: CGFloat?
    
    init(parent: ComposeController) {
        self.parent = parent
    }
    
    var view: some View {
        ToolbarView()
    }
    
    func showEmojiPicker() {
        guard parent.currentInput?.autocompleteState == nil else {
            return
        }
        parent.shouldEmojiAutocompletionBeginExpanded = true
        parent.currentInput?.beginAutocompletingEmoji()
    }
    
    func formatAction(_ format: StatusFormat) -> () -> Void {
        { [weak self] in
            self?.parent.currentInput?.applyFormat(format)
        }
    }
    
    struct ToolbarView: View {
        @EnvironmentObject private var draft: Draft
        @EnvironmentObject private var controller: ToolbarController
        @EnvironmentObject private var composeController: ComposeController
        @ScaledMetric(relativeTo: .body) private var imageSize: CGFloat = 22
        
        @State private var minWidth: CGFloat?
        @State private var realWidth: CGFloat?
        
        var body: some View {
            ScrollView(.horizontal, showsIndicators: false) {
                HStack(spacing: 0) {
                    cwButton
                    
                    MenuPicker(selection: visibilityBinding, options: visibilityOptions, buttonStyle: .iconOnly)
                    // the button has a bunch of extra space by default, but combined with what we add it's too much
                        .padding(.horizontal, -8)
                        .disabled(draft.editedStatusID != nil)
                        .disabled(composeController.mastodonController.instanceFeatures.localOnlyPostsVisibility && draft.localOnly)
                    
                    if composeController.mastodonController.instanceFeatures.localOnlyPosts {
                        localOnlyPicker
                            .padding(.horizontal, -8)
                            .disabled(draft.editedStatusID != nil)
                    }
                    
                    if let currentInput = composeController.currentInput,
                       currentInput.toolbarElements.contains(.emojiPicker) {
                        customEmojiButton
                    }
                    
                    if let currentInput = composeController.currentInput,
                       currentInput.toolbarElements.contains(.formattingButtons),
                       composeController.config.contentType != .plain {
                        
                        Spacer()
                        formatButtons
                    }
                    
                    Spacer()
                    
                    if #available(iOS 16.0, *),
                       composeController.mastodonController.instanceFeatures.createStatusWithLanguage {
                        LanguagePicker(draftLanguage: $draft.language, hasChangedSelection: $composeController.hasChangedLanguageSelection)
                    }
                }
                .padding(.horizontal, 16)
                .frame(minWidth: minWidth)
                .background(GeometryReader { proxy in
                    Color.clear
                        .preference(key: ToolbarWidthPrefKey.self, value: proxy.size.width)
                        .onPreferenceChange(ToolbarWidthPrefKey.self) { width in
                            realWidth = width
                        }
                })
            }
            .scrollDisabledIfAvailable(realWidth ?? 0 <= minWidth ?? 0)
            .frame(height: ToolbarController.height)
            .frame(maxWidth: .infinity)
            .background(.regularMaterial, ignoresSafeAreaEdges: [.bottom, .leading, .trailing])
            .overlay(alignment: .top) {
                Divider()
                    .edgesIgnoringSafeArea([.leading, .trailing])
            }
            .background(GeometryReader { proxy in
                Color.clear
                    .preference(key: ToolbarWidthPrefKey.self, value: proxy.size.width)
                    .onPreferenceChange(ToolbarWidthPrefKey.self) { width in
                        minWidth = width
                    }
            })
        }
        
        private var cwButton: some View {
            Button("CW", action: controller.parent.toggleContentWarning)
                .accessibilityLabel(draft.contentWarningEnabled ? "Remove content warning" : "Add content warning")
                .padding(5)
                .hoverEffect()
        }
        
        private var visibilityBinding: Binding<Pachyderm.Visibility> {
            // On instances that conflate visibliity and local only, we still show two separate controls but don't allow
            // changing the visibility when local-only.
            if draft.localOnly,
               composeController.mastodonController.instanceFeatures.localOnlyPostsVisibility {
                return .constant(.public)
            } else {
                return $draft.visibility
            }
        }
        
        private var visibilityOptions: [MenuPicker<Pachyderm.Visibility>.Option] {
            let visibilities: [Pachyderm.Visibility]
            if !composeController.mastodonController.instanceFeatures.composeDirectStatuses {
                visibilities = [.public, .unlisted, .private]
            } else {
                visibilities = Pachyderm.Visibility.allCases
            }
            return visibilities.map { vis in
                    .init(value: vis, title: vis.displayName, subtitle: vis.subtitle, image: UIImage(systemName: vis.unfilledImageName), accessibilityLabel: "Visibility: \(vis.displayName)")
            }
        }
        
        private var localOnlyPicker: some View {
            let domain = composeController.mastodonController.accountInfo!.instanceURL.host!
            return MenuPicker(selection: $draft.localOnly, options: [
                .init(value: true, title: "Local-only", subtitle: "Only \(domain)", image: UIImage(named: "link.broken")),
                .init(value: false, title: "Federated", image: UIImage(systemName: "link")),
            ], buttonStyle: .iconOnly)
        }
        
        private var customEmojiButton: some View {
            Button(action: controller.showEmojiPicker) {
                Label("Insert custom emoji", systemImage: "face.smiling")
            }
            .labelStyle(.iconOnly)
            .font(.system(size: imageSize))
            .padding(5)
            .hoverEffect()
            .transition(.opacity.animation(.linear(duration: 0.2)))
        }
        
        private var formatButtons: some View {
            ForEach(StatusFormat.allCases, id: \.rawValue) { format in
                Button(action: controller.formatAction(format)) {
                    if let imageName = format.imageName {
                        Image(systemName: imageName)
                            .font(.system(size: imageSize))
                    } else if let (str, attrs) = format.title {
                        let container = try! AttributeContainer(attrs, including: \.uiKit)
                        Text(AttributedString(str, attributes: container))
                    }
                }
                .accessibilityLabel(format.accessibilityLabel)
                .padding(5)
                .hoverEffect()
                .transition(.opacity.animation(.linear(duration: 0.2)))
            }
        }
    }
}

private struct ToolbarWidthPrefKey: PreferenceKey {
    static var defaultValue: CGFloat? = nil
    static func reduce(value: inout CGFloat?, nextValue: () -> CGFloat?) {
        value = nextValue()
    }
}
