import AppKit

@MainActor
final class QuickQuestionPanelView: NSVisualEffectView, NSTextViewDelegate {
    weak var delegate: (any SybilQuickQuestionPanelDelegate)?
    var onDismiss: (() -> Void)?

    let promptView = QuickQuestionPromptView()
    private let stack = NSStackView()
    private let answerView = NSTextView()
    private let answerScroll = NSScrollView()
    private let statusLabel = NSTextField(wrappingLabelWithString: "")
    private let providerPopup = NSPopUpButton()
    private let modelPopup = NSPopUpButton()
    private let convertButton = NSButton(title: "Open in Chat", target: nil, action: nil)
    private let openAppButton = NSButton(title: "Open Sybil", target: nil, action: nil)
    private let sendButton = NSButton()
    private let shortcutLabel = NSTextField(labelWithString: "⌥ Space")
    private var panelState = SybilQuickQuestionPanelState()

    var preferredHeight: CGFloat { stack.fittingSize.height + 40 }

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        material = .hudWindow
        blendingMode = .behindWindow
        state = .active
        wantsLayer = true
        layer?.cornerRadius = 18
        layer?.masksToBounds = true
        layer?.borderWidth = 1
        layer?.borderColor = NSColor.white.withAlphaComponent(0.14).cgColor

        stack.orientation = .vertical
        stack.alignment = .leading
        stack.spacing = 12
        stack.detachesHiddenViews = true
        stack.translatesAutoresizingMaskIntoConstraints = false
        addSubview(stack)
        NSLayoutConstraint.activate([
            stack.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
            stack.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
            stack.topAnchor.constraint(equalTo: topAnchor, constant: 20),
            stack.bottomAnchor.constraint(equalTo: bottomAnchor, constant: -20)
        ])

        addRow(makeHeader())
        configureTextView(promptView, fontSize: 22)
        promptView.delegate = self
        promptView.isRichText = false
        promptView.allowsUndo = true
        promptView.setAccessibilityLabel("Quick question prompt")
        promptView.onSubmit = { [weak self] in self?.submit() }
        promptView.onToggle = { [weak self] in self?.delegate?.quickQuestionToggleRequested() }
        let promptScroll = makeScrollView(documentView: promptView)
        promptScroll.heightAnchor.constraint(equalToConstant: 64).isActive = true
        addRow(promptScroll)

        configureTextView(answerView, fontSize: 15)
        answerView.isEditable = false
        answerView.isSelectable = true
        answerView.setAccessibilityLabel("Quick question answer")
        answerView.linkTextAttributes = [.foregroundColor: NSColor.systemPurple, .underlineStyle: NSUnderlineStyle.single.rawValue]
        answerScroll.drawsBackground = false
        answerScroll.hasVerticalScroller = true
        answerScroll.autohidesScrollers = true
        answerScroll.documentView = answerView
        answerScroll.heightAnchor.constraint(equalToConstant: 280).isActive = true
        addRow(answerScroll)

        statusLabel.font = .systemFont(ofSize: 12)
        statusLabel.textColor = .secondaryLabelColor
        statusLabel.maximumNumberOfLines = 3
        addRow(statusLabel)
        addRow(makeControls())
        let hint = NSTextField(labelWithString: "Return to ask   ·   Shift+Return for a new line   ·   Esc to close")
        hint.font = .systemFont(ofSize: 11)
        hint.textColor = .secondaryLabelColor
        addRow(hint)
        update(SybilQuickQuestionPanelState(), shortcutAvailable: true)
    }

    required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") }

    func update(_ next: SybilQuickQuestionPanelState, shortcutAvailable: Bool) {
        let previous = panelState
        panelState = next
        if promptView.string != next.prompt, !promptView.hasMarkedText() {
            promptView.string = next.prompt
            promptView.setSelectedRange(NSRange(location: (next.prompt as NSString).length, length: 0))
        }
        promptView.isEditable = !next.isBusy
        promptView.needsDisplay = true
        if previous.answer != next.answer {
            let wasAtBottom = answerScroll.contentView.bounds.maxY >= answerView.bounds.maxY - 36
            answerView.textStorage?.setAttributedString(Self.renderAnswer(next.answer))
            if wasAtBottom { answerView.scrollToEndOfDocument(nil) }
        }
        answerScroll.isHidden = next.answer.isEmpty

        if providerPopup.numberOfItems == 0 || previous.providers != next.providers {
            providerPopup.removeAllItems()
            for provider in next.providers {
                providerPopup.addItem(withTitle: provider.title)
                providerPopup.lastItem?.representedObject = provider.id
            }
        }
        if let index = next.providers.firstIndex(where: { $0.id == next.provider }) {
            providerPopup.selectItem(at: index)
        }
        if modelPopup.numberOfItems == 0 || previous.models != next.models || previous.model != next.model {
            modelPopup.removeAllItems()
            var models = next.models
            if !next.model.isEmpty, !models.contains(next.model) { models.insert(next.model, at: 0) }
            modelPopup.addItems(withTitles: models)
            modelPopup.selectItem(withTitle: next.model)
        }
        providerPopup.isEnabled = next.isAuthenticated && !next.isBusy
        modelPopup.isEnabled = next.isAuthenticated && !next.isBusy
        convertButton.isEnabled = next.canConvert
        sendButton.isEnabled = next.canSend
        openAppButton.isHidden = next.isAuthenticated
        convertButton.isHidden = !next.isAuthenticated
        shortcutLabel.stringValue = shortcutAvailable ? "⌥ Space" : "Shortcut unavailable"
        shortcutLabel.toolTip = shortcutAvailable ? "Option+Space toggles Quick Question" : "Another app may be using Option+Space. Open Quick Question from Sybil’s File menu."

        let status: String
        if let error = next.error {
            status = error
            statusLabel.textColor = .systemRed
        } else {
            statusLabel.textColor = .secondaryLabelColor
            if next.isCheckingSession {
                status = "Connecting to Sybil…"
            } else if !next.isAuthenticated {
                status = "Connect in Sybil before asking a question."
            } else if next.isConverting {
                status = "Opening in chat…"
            } else if next.isSending {
                status = next.toolSummaries.last ?? "Thinking…"
            } else {
                status = ""
            }
        }
        statusLabel.stringValue = status
        statusLabel.isHidden = status.isEmpty
    }

    func preparePromptForPresentation() {
        // Select only when invoked, never on streamed updates or normal edits.
        promptView.selectAll(nil)
    }

    func textDidChange(_ notification: Notification) {
        guard !panelState.isBusy else { return }
        promptView.needsDisplay = true
        delegate?.quickQuestionPromptChanged(promptView.string)
    }

    private func addRow(_ view: NSView) {
        stack.addArrangedSubview(view)
        view.widthAnchor.constraint(equalTo: stack.widthAnchor).isActive = true
    }

    private func makeHeader() -> NSView {
        let icon = NSImageView(image: NSImage(systemSymbolName: "sparkles", accessibilityDescription: nil)!)
        icon.contentTintColor = .systemPurple
        icon.widthAnchor.constraint(equalToConstant: 18).isActive = true
        let title = NSTextField(labelWithString: "Quick Question")
        title.font = .systemFont(ofSize: 14, weight: .semibold)
        shortcutLabel.font = .monospacedSystemFont(ofSize: 11, weight: .medium)
        shortcutLabel.textColor = .secondaryLabelColor
        let close = NSButton(image: NSImage(systemSymbolName: "xmark", accessibilityDescription: "Close Quick Question")!, target: self, action: #selector(closePanel))
        close.isBordered = false
        close.toolTip = "Close (Esc)"
        close.setAccessibilityLabel("Close Quick Question")
        close.widthAnchor.constraint(equalToConstant: 24).isActive = true
        let row = NSStackView(views: [icon, title, NSView(), shortcutLabel, close])
        row.spacing = 8
        row.alignment = .centerY
        row.heightAnchor.constraint(equalToConstant: 24).isActive = true
        return row
    }

    private func makeControls() -> NSView {
        providerPopup.target = self
        providerPopup.action = #selector(changeProvider)
        providerPopup.setAccessibilityLabel("Quick question provider")
        providerPopup.widthAnchor.constraint(equalToConstant: 120).isActive = true
        modelPopup.target = self
        modelPopup.action = #selector(changeModel)
        modelPopup.setAccessibilityLabel("Quick question model")
        modelPopup.widthAnchor.constraint(lessThanOrEqualToConstant: 230).isActive = true
        modelPopup.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
        convertButton.target = self
        convertButton.action = #selector(convertToChat)
        convertButton.bezelStyle = .rounded
        openAppButton.target = self
        openAppButton.action = #selector(openApp)
        openAppButton.bezelStyle = .rounded
        sendButton.image = NSImage(systemSymbolName: "arrow.up", accessibilityDescription: "Ask Quick Question")
        sendButton.imagePosition = .imageOnly
        sendButton.bezelStyle = .circular
        sendButton.contentTintColor = .systemPurple
        sendButton.target = self
        sendButton.action = #selector(submit)
        sendButton.setAccessibilityLabel("Ask Quick Question")
        sendButton.toolTip = "Ask (Return)"
        sendButton.widthAnchor.constraint(equalToConstant: 32).isActive = true
        let row = NSStackView(views: [providerPopup, modelPopup, NSView(), openAppButton, convertButton, sendButton])
        row.spacing = 8
        row.alignment = .centerY
        row.detachesHiddenViews = true
        row.heightAnchor.constraint(equalToConstant: 32).isActive = true
        return row
    }

    private func configureTextView(_ view: NSTextView, fontSize: CGFloat) {
        view.drawsBackground = false
        view.textColor = .labelColor
        view.insertionPointColor = .labelColor
        view.font = .systemFont(ofSize: fontSize)
        view.textContainerInset = NSSize(width: 0, height: 4)
        view.isHorizontallyResizable = false
        view.isVerticallyResizable = true
        view.autoresizingMask = [.width]
        view.textContainer?.widthTracksTextView = true
        view.textContainer?.lineFragmentPadding = 0
    }

    private func makeScrollView(documentView: NSView) -> NSScrollView {
        let scroll = NSScrollView()
        scroll.drawsBackground = false
        scroll.hasVerticalScroller = true
        scroll.autohidesScrollers = true
        scroll.documentView = documentView
        return scroll
    }

    private static func renderAnswer(_ text: String) -> NSAttributedString {
        let parsed = (try? AttributedString(markdown: text, options: .init(interpretedSyntax: .inlineOnlyPreservingWhitespace))) ?? AttributedString(text)
        let rendered = NSMutableAttributedString(attributedString: NSAttributedString(parsed))
        let paragraph = NSMutableParagraphStyle()
        paragraph.lineSpacing = 4
        rendered.addAttributes([.foregroundColor: NSColor.labelColor, .paragraphStyle: paragraph], range: NSRange(location: 0, length: rendered.length))
        for run in parsed.runs {
            let intent = run.inlinePresentationIntent ?? []
            var font = intent.contains(.code) ? NSFont.monospacedSystemFont(ofSize: 14, weight: .regular) : NSFont.systemFont(ofSize: 15)
            if intent.contains(.stronglyEmphasized) { font = NSFontManager.shared.convert(font, toHaveTrait: .boldFontMask) }
            if intent.contains(.emphasized) { font = NSFontManager.shared.convert(font, toHaveTrait: .italicFontMask) }
            rendered.addAttribute(.font, value: font, range: NSRange(run.range, in: parsed))
        }
        return rendered
    }

    @objc private func closePanel() { onDismiss?() }
    @objc private func changeProvider() {
        guard let provider = providerPopup.selectedItem?.representedObject as? String else { return }
        delegate?.quickQuestionProviderChanged(provider)
    }
    @objc private func changeModel() {
        guard let model = modelPopup.titleOfSelectedItem else { return }
        delegate?.quickQuestionModelChanged(model)
    }
    @objc private func convertToChat() { delegate?.quickQuestionConvertRequested() }
    @objc private func openApp() { delegate?.quickQuestionOpenAppRequested() }
    @objc private func submit() {
        guard panelState.canSend, !promptView.hasMarkedText() else { return }
        delegate?.quickQuestionSubmitRequested()
    }
}

@MainActor
final class QuickQuestionPromptView: NSTextView {
    var onSubmit: (() -> Void)?
    var onToggle: (() -> Void)?

    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        guard string.isEmpty else { return }
        ("Ask anything…" as NSString).draw(
            at: NSPoint(x: textContainerInset.width, y: textContainerInset.height),
            withAttributes: [.font: font ?? NSFont.systemFont(ofSize: 22), .foregroundColor: NSColor.placeholderTextColor]
        )
    }

    override func keyDown(with event: NSEvent) {
        let modifiers = event.modifierFlags.intersection([.command, .control, .option, .shift])
        if event.keyCode == 49, modifiers == .option {
            if !event.isARepeat { onToggle?() }
            return
        }
        if (event.keyCode == 36 || event.keyCode == 76), modifiers.isEmpty, !hasMarkedText() {
            if !event.isARepeat { onSubmit?() }
            return
        }
        // Let the text input system handle Shift+Return and IME confirmation.
        super.keyDown(with: event)
    }
}
