import AppKit
import SwiftUI
import WebKit

/// Titlebar icon control: white/neutral tint (no accent blue), subtle hover and selected backgrounds.
private final class TitlebarIconButton: NSButton {
    private var isHovering = false
    private var hoverTrackingArea: NSTrackingArea?
    var isToggleActive = false
    /// Looks disabled (gray) but keeps `isEnabled == true` so the control still receives clicks (e.g. paywall).
    var lockedForPaywallAppearance = false {
        didSet { refreshVisuals() }
    }

    override func awakeFromNib() {
        super.awakeFromNib()
        commonInit()
    }

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        commonInit()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        nil
    }

    private func commonInit() {
        wantsLayer = true
        layer?.cornerRadius = 5
        // Do not mask the button’s bounds to the rounded rect: on a ~10×10 pt control,
        // cornerRadius 5 + masksToBounds clips to a *circle* and cuts off corner-heavy
        // bitmap icons (e.g. layout grid). SF Symbols stay fine due to built-in padding.
        layer?.masksToBounds = false
    }

    override func updateTrackingAreas() {
        super.updateTrackingAreas()
        // Do NOT remove all tracking areas here: AppKit uses its own tracking area for tooltips.
        // We only manage our own hover tracking area.
        if let hoverTrackingArea {
            removeTrackingArea(hoverTrackingArea)
        }
        let area = NSTrackingArea(
            rect: bounds,
            options: [.activeInKeyWindow, .mouseEnteredAndExited, .inVisibleRect],
            owner: self,
            userInfo: nil
        )
        hoverTrackingArea = area
        addTrackingArea(area)
    }

    override func mouseEntered(with event: NSEvent) {
        isHovering = true
        refreshVisuals()
    }

    override func mouseExited(with event: NSEvent) {
        isHovering = false
        refreshVisuals()
    }

    override var isEnabled: Bool {
        didSet { refreshVisuals() }
    }

    func setToggleActive(_ on: Bool) {
        isToggleActive = on
        refreshVisuals()
    }

    func refreshVisuals() {
        let white = NSColor.white
        if !isEnabled || lockedForPaywallAppearance {
            contentTintColor = white.withAlphaComponent(0.35)
            layer?.backgroundColor = NSColor.clear.cgColor
            return
        }
        if isToggleActive {
            contentTintColor = white
            let bg = isHovering ? white.withAlphaComponent(0.22) : white.withAlphaComponent(0.16)
            layer?.backgroundColor = bg.cgColor
            return
        }
        if isHovering {
            contentTintColor = white
            layer?.backgroundColor = white.withAlphaComponent(0.12).cgColor
        } else {
            contentTintColor = white.withAlphaComponent(0.88)
            layer?.backgroundColor = NSColor.clear.cgColor
        }
    }
}

/// Scales a bundled template image to a logical point size for the titlebar (SF Symbols here use 12pt; bitmap icons often need ~14pt to read the same optically).
private func makeTitlebarSizedTemplateImage(named name: String, pointSize: CGFloat, layoutMarginFraction: CGFloat = 0) -> NSImage? {
    guard let source = NSImage(named: name) else { return nil }
    let logicalSize = NSSize(width: pointSize, height: pointSize)
    let image = NSImage(size: logicalSize, flipped: false) { rect in
        NSGraphicsContext.current?.imageInterpolation = .high
        let inset = min(rect.width, rect.height) * layoutMarginFraction
        let drawRect = rect.insetBy(dx: inset, dy: inset)
        source.draw(
            in: drawRect,
            from: NSRect(origin: .zero, size: source.size),
            operation: .sourceOver,
            fraction: 1.0
        )
        return true
    }
    image.isTemplate = true
    return image
}

final class InAppBrowserWindowManager {
    static let shared = InAppBrowserWindowManager()

    private var controllers: [InAppBrowserWindowController] = []

    private init() {}

    func open(url: URL, title: String? = nil, injectComposerPrompt: String? = nil) {
        let controller = InAppBrowserWindowController(
            initialURL: url,
            windowTitle: title,
            injectComposerPrompt: injectComposerPrompt
        )
        controllers.append(controller)
        controller.onClose = { [weak self, weak controller] in
            guard let self, let controller else { return }
            self.controllers.removeAll { $0 === controller }
        }
        controller.showWindow(nil)
        controller.window?.makeKeyAndOrderFront(nil)
    }
}

final class InAppBrowserWindowController: NSWindowController, NSWindowDelegate {
    var onClose: (() -> Void)?

    private let webView: WKWebView
    private let initialURL: URL
    /// When opening Gemini from the widget, filled into the page composer after load (Gemini ignores URL query params).
    private let injectComposerPrompt: String?
    private var geminiPromptWasInjected = false
    private var geminiInjectionIsActive = false
    private var didLoadInitialURL = false

    // Titlebar controls
    private var backButton: TitlebarIconButton!
    private var forwardButton: TitlebarIconButton!
    private var reloadButton: TitlebarIconButton!
    private var pinButton: TitlebarIconButton!
    private var homeButton: TitlebarIconButton!
    private var addToStatusBarButton: TitlebarIconButton!
    private var widgetButton: TitlebarIconButton!
    private var addToDesktopButton: TitlebarIconButton!
    private var handButton: TitlebarIconButton!
    private var downloadButton: TitlebarIconButton!
    private var moreButton: TitlebarIconButton!
    private var siteNameLabel: NSTextField!
    private var siteIconView: NSImageView!
    private var currentSiteIconURLKey: String?
    /// Keeps the center title in sync when `document.title` changes after load (e.g. SPAs).
    private var titleObservation: NSKeyValueObservation?
    /// Refreshes site identity when loading starts/stops.
    private var loadingObservation: NSKeyValueObservation?
    private var isPinnedOnTop = false
    private var isGestureModeEnabled = true

    private var statusBarShortcutObserver: NSObjectProtocol?
    private var premiumSheetWindow: NSWindow?

    /// Google endpoints are more likely to trigger human-verification in embedded webviews.
    /// Present a Safari-like UA for Google-family hosts to reduce bot checks.
    private static let safariLikeUserAgent =
    "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.3 Safari/605.1.15"

    private func setToggledStyle(_ button: TitlebarIconButton, isOn: Bool) {
        button.setToggleActive(isOn)
    }

    deinit {
        if let statusBarShortcutObserver {
            NotificationCenter.default.removeObserver(statusBarShortcutObserver)
        }
    }

    init(initialURL: URL, windowTitle: String?, injectComposerPrompt: String? = nil) {
        self.initialURL = initialURL
        self.injectComposerPrompt = injectComposerPrompt

        let config = WKWebViewConfiguration()
        config.defaultWebpagePreferences.allowsContentJavaScript = true
        self.webView = WKWebView(frame: .zero, configuration: config)
        self.webView.customUserAgent = Self.preferredUserAgent(for: initialURL)

        let window = NSWindow(
            contentRect: NSRect(x: 180, y: 140, width: 1200, height: 800),
            styleMask: [.titled, .closable, .miniaturizable, .resizable],
            backing: .buffered,
            defer: false
        )
        window.title = windowTitle ?? "Browser"
        window.isReleasedWhenClosed = false
        window.titleVisibility = .hidden
        window.titlebarAppearsTransparent = true
        window.styleMask.insert(.fullSizeContentView)

        // Build UI: web view fills content; controls live in titlebar.
        let containerView = NSView(frame: .zero)
        containerView.wantsLayer = true
        containerView.layer?.backgroundColor = NSColor.clear.cgColor
        window.contentView = containerView

        func makeIconButton(symbolName: String, accessibility: String, action: Selector, fallbackSymbolName: String? = nil) -> TitlebarIconButton {
            let button = TitlebarIconButton()
            let primary = NSImage(systemSymbolName: symbolName, accessibilityDescription: accessibility)
            let fallback = fallbackSymbolName.flatMap { NSImage(systemSymbolName: $0, accessibilityDescription: accessibility) }
            button.image = primary ?? fallback
            button.image?.isTemplate = true
            button.imagePosition = .imageOnly
            button.imageScaling = .scaleProportionallyDown
            button.isBordered = false
            button.bezelStyle = .regularSquare
            button.controlSize = .small
            button.font = NSFont.systemFont(ofSize: 12, weight: .semibold)
            button.setButtonType(.momentaryPushIn)
            button.action = action
            button.toolTip = accessibility
            if button.image == nil {
                button.title = "⋮"
            } else {
                button.title = ""
            }
            // Consistent symbol sizing.
            let cfg = NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold)
            button.image = button.image?.withSymbolConfiguration(cfg)
            button.refreshVisuals()
            return button
        }

        // Back
        backButton = makeIconButton(
            symbolName: "chevron.left",
            accessibility: "Back",
            action: #selector(goBack)
        )

        // Forward
        forwardButton = makeIconButton(
            symbolName: "chevron.right",
            accessibility: "Forward",
            action: #selector(goForward)
        )

        // Reload
        reloadButton = makeIconButton(
            symbolName: "arrow.clockwise",
            accessibility: "Reload",
            action: #selector(reloadPage)
        )

        // Pin (always on top)
        pinButton = makeIconButton(
            symbolName: "pin",
            accessibility: "Pin (Always on Top)",
            action: #selector(togglePinOnTop)
        )

        // Add to menu bar (matches launcher tile behavior; requires Premium + known app host).
        addToStatusBarButton = makeIconButton(
            symbolName: "arrow.up.square",
            accessibility: "Add to Status Bar",
            action: #selector(toggleCurrentPageStatusBar),
            fallbackSymbolName: "square.and.arrow.up"
        )

        // Home
        homeButton = makeIconButton(
            symbolName: "house",
            accessibility: "Home",
            action: #selector(goHome)
        )

        // Widget (placeholder — hook up when widget feature ships)
        widgetButton = makeIconButton(
            symbolName: "widget.small",
            accessibility: "Widget",
            action: #selector(widgetTapped),
            fallbackSymbolName: "square.on.square"
        )

        // Bundled PNG: 1x = layout-fluid-3, 2x = layout-fluid-4 @ 48px — slightly larger than 12pt symbols so the full glyph reads clearly.
        widgetButton.image = makeTitlebarSizedTemplateImage(named: "widget_custom_icon", pointSize: 14, layoutMarginFraction: 0)

        // Add desktop shortcut (.webloc); matches launcher tile behavior (Premium).
        addToDesktopButton = makeIconButton(
            symbolName: "arrow.up.right.square",
            accessibility: "Add to Desktop",
            action: #selector(addCurrentPageToDesktop),
            fallbackSymbolName: "square.and.arrow.up"
        )

        // Center site identity (logo + app/site name).
        siteIconView = NSImageView()
        siteIconView.translatesAutoresizingMaskIntoConstraints = false
        siteIconView.image = NSImage(systemSymbolName: "globe", accessibilityDescription: "Site")?
            .withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold))
        siteIconView.contentTintColor = NSColor.secondaryLabelColor
        siteIconView.imageScaling = .scaleProportionallyDown
        siteIconView.wantsLayer = true
        siteIconView.layer?.cornerRadius = 3
        siteIconView.layer?.masksToBounds = true

        siteNameLabel = NSTextField(labelWithString: windowTitle ?? "Browser")
        siteNameLabel.font = NSFont.systemFont(ofSize: 13, weight: .semibold)
        siteNameLabel.textColor = NSColor.labelColor
        siteNameLabel.alignment = .left
        siteNameLabel.lineBreakMode = .byTruncatingTail

        let headerStack = NSStackView(views: [siteIconView, siteNameLabel])
        headerStack.orientation = .horizontal
        headerStack.alignment = .centerY
        headerStack.spacing = 8
        headerStack.translatesAutoresizingMaskIntoConstraints = false
        NSLayoutConstraint.activate([
            siteIconView.widthAnchor.constraint(equalToConstant: 14),
            siteIconView.heightAnchor.constraint(equalToConstant: 14),
        ])

        // Hand tool (toggle navigation gestures)
        handButton = makeIconButton(
            symbolName: "hand.raised",
            accessibility: "Gesture Mode",
            action: #selector(toggleGestureMode)
        )

        // Downloads (open Downloads folder)
        downloadButton = makeIconButton(
            symbolName: "arrow.down.circle",
            accessibility: "Downloads",
            action: #selector(openDownloads)
        )

        // More menu
        moreButton = makeIconButton(
            symbolName: "ellipsis.vertical",
            accessibility: "More",
            action: #selector(showMoreMenu),
            fallbackSymbolName: "ellipsis"
        )

        // Web view fills content.
        webView.translatesAutoresizingMaskIntoConstraints = false
        webView.setValue(false, forKey: "drawsBackground")

        containerView.addSubview(webView)

        // Use the window content layout guide so content starts below titlebar.
        let contentGuide = (window.contentLayoutGuide as? NSLayoutGuide) ?? NSLayoutGuide()
        NSLayoutConstraint.activate([
            webView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor),
            webView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor),
            webView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor),
            webView.topAnchor.constraint(equalTo: contentGuide.topAnchor),
        ])

        super.init(window: window)

        window.delegate = self

        // Build titlebar layout after super.init.
        configureTitlebarLayout(centerView: headerStack)

        // Safe to reference `self` only after super.init.
        backButton.target = self
        forwardButton.target = self
        reloadButton.target = self
        pinButton.target = self
        homeButton.target = self
        addToStatusBarButton.target = self
        widgetButton.target = self
        addToDesktopButton.target = self
        handButton.target = self
        downloadButton.target = self
        moreButton.target = self

        statusBarShortcutObserver = NotificationCenter.default.addObserver(
            forName: .statusBarShortcutIDsChanged,
            object: nil,
            queue: .main
        ) { [weak self] _ in
            self?.updateAddToStatusBarButtonAppearance()
        }

        webView.uiDelegate = self
        webView.navigationDelegate = self
        webView.allowsBackForwardNavigationGestures = true
        titleObservation = webView.observe(\.title, options: [.new]) { [weak self] _, _ in
            self?.updateSiteNameFromWebView()
        }
        loadingObservation = webView.observe(\.isLoading, options: [.new]) { [weak self] _, _ in
            self?.updateSiteNameFromWebView()
        }
        isGestureModeEnabled = true

        // Initialize toolbar state.
        setToggledStyle(pinButton, isOn: isPinnedOnTop)
        setToggledStyle(handButton, isOn: isGestureModeEnabled)
        siteNameLabel.stringValue = windowTitle ?? "Browser"
        updateToolbarState()
    }

    private static func preferredUserAgent(for url: URL) -> String? {
        guard let host = url.host?.lowercased() else { return nil }
        if host.contains("google.") || host.contains("youtube.com") {
            return safariLikeUserAgent
        }
        return nil
    }

    private func configureTitlebarLayout(centerView: NSView) {
        guard let window else { return }

        centerView.translatesAutoresizingMaskIntoConstraints = false

        let leftStack = NSStackView(views: [backButton, forwardButton, reloadButton, homeButton])
        leftStack.orientation = .horizontal
        leftStack.alignment = .centerY
        leftStack.spacing = 6

        let rightStack = NSStackView(views: [
            pinButton,
            addToStatusBarButton,
            widgetButton,
            addToDesktopButton,
            handButton,
            downloadButton,
            moreButton,
        ])
        rightStack.orientation = .horizontal
        rightStack.alignment = .centerY
        rightStack.spacing = 6

        func accessory(_ view: NSView, layout: NSLayoutConstraint.Attribute) -> NSTitlebarAccessoryViewController {
            let vc = NSTitlebarAccessoryViewController()
            vc.layoutAttribute = layout
            vc.view = view
            return vc
        }

        // Single top accessory with left / center / right.
        let topBar = NSView(frame: NSRect(x: 0, y: 0, width: 1000, height: 34))
        topBar.translatesAutoresizingMaskIntoConstraints = false

        let leftContainer = NSView()
        leftContainer.translatesAutoresizingMaskIntoConstraints = false
        leftContainer.addSubview(leftStack)
        leftStack.translatesAutoresizingMaskIntoConstraints = false

        let centerContainer = NSView()
        centerContainer.translatesAutoresizingMaskIntoConstraints = false
        centerContainer.addSubview(centerView)

        let rightContainer = NSView()
        rightContainer.translatesAutoresizingMaskIntoConstraints = false
        rightContainer.addSubview(rightStack)
        rightStack.translatesAutoresizingMaskIntoConstraints = false

        let barStack = NSStackView(views: [leftContainer, centerContainer, rightContainer])
        barStack.orientation = .horizontal
        barStack.alignment = .centerY
        barStack.distribution = .fill
        barStack.spacing = 10
        barStack.translatesAutoresizingMaskIntoConstraints = false

        topBar.addSubview(barStack)
        NSLayoutConstraint.activate([
            topBar.heightAnchor.constraint(equalToConstant: 34),

            barStack.leadingAnchor.constraint(equalTo: topBar.leadingAnchor, constant: 12),
            barStack.trailingAnchor.constraint(equalTo: topBar.trailingAnchor, constant: -12),
            barStack.topAnchor.constraint(equalTo: topBar.topAnchor, constant: 2),
            barStack.bottomAnchor.constraint(equalTo: topBar.bottomAnchor, constant: -2),

            leftContainer.widthAnchor.constraint(equalTo: rightContainer.widthAnchor),

            leftStack.leadingAnchor.constraint(equalTo: leftContainer.leadingAnchor),
            leftStack.trailingAnchor.constraint(equalTo: leftContainer.trailingAnchor),
            leftStack.centerYAnchor.constraint(equalTo: leftContainer.centerYAnchor),

            rightStack.leadingAnchor.constraint(equalTo: rightContainer.leadingAnchor),
            rightStack.trailingAnchor.constraint(equalTo: rightContainer.trailingAnchor),
            rightStack.centerYAnchor.constraint(equalTo: rightContainer.centerYAnchor),

            centerView.centerXAnchor.constraint(equalTo: centerContainer.centerXAnchor),
            centerView.centerYAnchor.constraint(equalTo: centerContainer.centerYAnchor),
            centerView.leadingAnchor.constraint(greaterThanOrEqualTo: centerContainer.leadingAnchor, constant: 6),
            centerView.trailingAnchor.constraint(lessThanOrEqualTo: centerContainer.trailingAnchor, constant: -6),
        ])

        window.addTitlebarAccessoryViewController(accessory(topBar, layout: .top))
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        nil
    }

    override func showWindow(_ sender: Any?) {
        super.showWindow(sender)
        guard !didLoadInitialURL else { return }
        didLoadInitialURL = true
        webView.load(URLRequest(url: initialURL))
    }

    func windowWillClose(_ notification: Notification) {
        guard let w = notification.object as? NSWindow else { return }
        if w === premiumSheetWindow {
            premiumSheetWindow = nil
            updateToolbarState()
            return
        }
        if w === window {
            onClose?()
        }
    }

    @objc private func goBack() {
        webView.goBack()
    }

    @objc private func goForward() {
        webView.goForward()
    }

    @objc private func reloadPage() {
        webView.reload()
    }

    @objc private func goHome() {
        webView.load(URLRequest(url: initialURL))
    }

    @objc private func widgetTapped() {
        let candidateTitle = siteNameLabel?.stringValue.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
        let matched = LauncherApp.sampleApps.first { $0.name.caseInsensitiveCompare(candidateTitle) == .orderedSame }
        NotificationCenter.default.post(
            name: .openWidgetsPage,
            object: nil,
            userInfo: matched.map { ["appID": $0.id] }
        )
    }

    @objc private func toggleCurrentPageStatusBar() {
        guard PremiumStore.shared.isPremiumUnlocked else {
            presentPremiumPaywall()
            return
        }
        guard let app = resolvedLauncherAppForCurrentPage() else {
            showAlert(
                title: "Can’t add to menu bar",
                message: "This site isn’t in your launcher list. Open it from the home screen first, then use this button."
            )
            return
        }
        var ids = LauncherApp.statusBarShortcutIDSet()
        if ids.contains(app.id) {
            ids.remove(app.id)
            LauncherApp.persistStatusBarShortcutIDs(ids)
            showAlert(
                title: "Removed from Status Bar",
                message: "“\(app.name)” was removed from the menu bar."
            )
        } else {
            ids.insert(app.id)
            LauncherApp.persistStatusBarShortcutIDs(ids)
            showAlert(
                title: "Added to Status Bar",
                message: "“\(app.name)” now has its own icon in the menu bar. Click it to open, or right‑click to remove."
            )
        }
        updateAddToStatusBarButtonAppearance()
    }

    @objc private func addCurrentPageToDesktop() {
        guard PremiumStore.shared.isPremiumUnlocked else {
            presentPremiumPaywall()
            return
        }
        let url = currentPageURL()
        guard let scheme = url.scheme?.lowercased(), scheme == "http" || scheme == "https" else {
            showAlert(title: "Unavailable", message: "Desktop shortcuts need a web address (http or https).")
            return
        }
        let name = displayNameForDesktopShortcut()
        DesktopWeblocShortcutPresenter.beginSavePanel(forAppNamed: name, webURL: url)
    }

    private func currentPageURL() -> URL {
        webView.url ?? initialURL
    }

    private func normalizedHost(_ host: String) -> String {
        let h = host.lowercased()
        return h.hasPrefix("www.") ? String(h.dropFirst(4)) : h
    }

    private func resolvedLauncherAppForCurrentPage() -> LauncherApp? {
        let url = currentPageURL()
        guard let host = url.host else { return nil }
        let nh = normalizedHost(host)
        let apps = LauncherApp.allLaunchableAppsFromUserDefaults()
        return apps.first { app in
            guard let appURL = app.webURL, let appHost = appURL.host else { return false }
            return normalizedHost(appHost) == nh
        }
    }

    private func displayNameForDesktopShortcut() -> String {
        if let app = resolvedLauncherAppForCurrentPage() {
            return app.name
        }
        let t = siteNameLabel.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
        if !t.isEmpty, !isLikelyURLString(t) {
            return t
        }
        if let host = currentPageURL().host {
            return host
        }
        return "Shortcut"
    }

    private func updateAddToStatusBarButtonAppearance() {
        let cfg = NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold)
        guard PremiumStore.shared.isPremiumUnlocked else {
            addToStatusBarButton.isEnabled = true
            addToStatusBarButton.toolTip = "Add to Status Bar — Premium"
            let img = NSImage(systemSymbolName: "arrow.up.square", accessibilityDescription: "Add to Status Bar")?
                .withSymbolConfiguration(cfg)
            addToStatusBarButton.image = img
            addToStatusBarButton.image?.isTemplate = true
            setToggledStyle(addToStatusBarButton, isOn: false)
            addToStatusBarButton.refreshVisuals()
            return
        }
        guard let app = resolvedLauncherAppForCurrentPage() else {
            addToStatusBarButton.isEnabled = false
            addToStatusBarButton.toolTip = "Add to Status Bar — open this site from the launcher first"
            let img = NSImage(systemSymbolName: "arrow.up.square", accessibilityDescription: "Add to Status Bar")?
                .withSymbolConfiguration(cfg)
            addToStatusBarButton.image = img
            addToStatusBarButton.image?.isTemplate = true
            setToggledStyle(addToStatusBarButton, isOn: false)
            return
        }
        addToStatusBarButton.isEnabled = true
        let inBar = LauncherApp.statusBarShortcutIDSet().contains(app.id)
        if inBar {
            let img = NSImage(systemSymbolName: "arrow.down.square", accessibilityDescription: "Remove from Status Bar")?
                .withSymbolConfiguration(cfg)
                ?? NSImage(systemSymbolName: "minus.square", accessibilityDescription: "Remove from Status Bar")?
                .withSymbolConfiguration(cfg)
            addToStatusBarButton.image = img
            addToStatusBarButton.image?.isTemplate = true
            addToStatusBarButton.toolTip = "Remove from Status Bar"
            setToggledStyle(addToStatusBarButton, isOn: true)
        } else {
            let img = NSImage(systemSymbolName: "arrow.up.square", accessibilityDescription: "Add to Status Bar")?
                .withSymbolConfiguration(cfg)
            addToStatusBarButton.image = img
            addToStatusBarButton.image?.isTemplate = true
            addToStatusBarButton.toolTip = "Add to Status Bar"
            setToggledStyle(addToStatusBarButton, isOn: false)
        }
        addToStatusBarButton.refreshVisuals()
    }

    @objc private func togglePinOnTop() {
        isPinnedOnTop.toggle()
        window?.level = isPinnedOnTop ? .floating : .normal
        if let button = pinButton {
            let symbol = isPinnedOnTop ? "pin.fill" : "pin"
            button.image = NSImage(systemSymbolName: symbol, accessibilityDescription: "Pin (Always on Top)")
            button.image?.isTemplate = true
            let cfg = NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold)
            button.image = button.image?.withSymbolConfiguration(cfg)
            setToggledStyle(button, isOn: isPinnedOnTop)
        }
    }

    @objc private func toggleGestureMode() {
        guard PremiumStore.shared.isPremiumUnlocked else {
            presentPremiumPaywall()
            return
        }
        isGestureModeEnabled.toggle()
        webView.allowsBackForwardNavigationGestures = isGestureModeEnabled
        if let button = handButton {
            let symbol = isGestureModeEnabled ? "hand.raised.fill" : "hand.raised"
            button.image = NSImage(systemSymbolName: symbol, accessibilityDescription: "Gesture Mode")
            button.image?.isTemplate = true
            let cfg = NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold)
            button.image = button.image?.withSymbolConfiguration(cfg)
            setToggledStyle(button, isOn: isGestureModeEnabled)
        }
    }

    /// Borderless `NSWindow` is square; SwiftUI `.sheet` gets rounded chrome automatically — match that look.
    private func configurePaywallWindowRoundedCorners(_ win: NSWindow) {
        guard let cv = win.contentView else { return }
        cv.wantsLayer = true
        let r: CGFloat = 16
        cv.layer?.cornerRadius = r
        if #available(macOS 11.0, *) {
            cv.layer?.cornerCurve = .continuous
        }
        cv.layer?.masksToBounds = true
    }

    private func presentPremiumPaywall() {
        guard let parentWindow = window else { return }
        if let existing = premiumSheetWindow, existing.isVisible {
            existing.makeKeyAndOrderFront(nil)
            NSApp.activate(ignoringOtherApps: true)
            return
        }

        let paywallSize = NSSize(width: 540, height: 720)
        let paywallWindow = NSWindow(
            contentRect: NSRect(origin: .zero, size: paywallSize),
            styleMask: [.borderless, .resizable],
            backing: .buffered,
            defer: false
        )
        paywallWindow.isReleasedWhenClosed = false
        paywallWindow.delegate = self
        paywallWindow.isMovableByWindowBackground = true
        paywallWindow.hasShadow = true
        paywallWindow.isOpaque = false
        paywallWindow.backgroundColor = .clear

        let hosting = NSHostingController(
            rootView: PremiumFeaturesView(onDismissFromAppKitHost: { [weak self] in
                self?.closePremiumPaywallWindow()
            })
            .environmentObject(PremiumStore.shared)
        )
        hosting.view.frame = NSRect(origin: .zero, size: paywallSize)
        paywallWindow.contentViewController = hosting
        configurePaywallWindowRoundedCorners(paywallWindow)

        let pf = parentWindow.frame
        paywallWindow.setFrame(
            NSRect(
                x: pf.midX - paywallSize.width / 2,
                y: pf.midY - paywallSize.height / 2,
                width: paywallSize.width,
                height: paywallSize.height
            ),
            display: false
        )

        premiumSheetWindow = paywallWindow
        parentWindow.addChildWindow(paywallWindow, ordered: .above)
        paywallWindow.makeKeyAndOrderFront(nil)
        NSApp.activate(ignoringOtherApps: true)
    }

    private func closePremiumPaywallWindow() {
        guard let w = premiumSheetWindow else { return }
        w.parent?.removeChildWindow(w)
        w.close()
        // `premiumSheetWindow` and toolbar refresh happen in `windowWillClose`.
    }

    /// Non‑Premium: only gesture, status bar, and desktop look locked; they stay enabled so taps open the paywall.
    private func applyPremiumLockedTitlebarAppearance() {
        let locked = !PremiumStore.shared.isPremiumUnlocked
        handButton.lockedForPaywallAppearance = locked
        addToDesktopButton.lockedForPaywallAppearance = locked
        addToStatusBarButton.lockedForPaywallAppearance = locked
        handButton.alphaValue = 1
        addToDesktopButton.alphaValue = 1
        addToStatusBarButton.alphaValue = 1
        if locked {
            handButton.isEnabled = true
            addToDesktopButton.isEnabled = true
            addToStatusBarButton.isEnabled = true
        }
        handButton.refreshVisuals()
        addToDesktopButton.refreshVisuals()
        addToStatusBarButton.refreshVisuals()
    }

    @objc private func openDownloads() {
        guard let downloads = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first else {
            showAlert(title: "Unavailable", message: "Couldn’t find your Downloads folder.")
            return
        }
        NSWorkspace.shared.open(downloads)
    }

    @objc private func showMoreMenu() {
        let menu = NSMenu()

        let reload = NSMenuItem(title: "Reload", action: #selector(reloadPage), keyEquivalent: "r")
        reload.keyEquivalentModifierMask = [.command]
        menu.addItem(reload)

        let home = NSMenuItem(title: "Home", action: #selector(goHome), keyEquivalent: "h")
        home.keyEquivalentModifierMask = [.command]
        menu.addItem(home)

        let downloads = NSMenuItem(title: "Open Downloads", action: #selector(openDownloads), keyEquivalent: "d")
        downloads.keyEquivalentModifierMask = [.command]
        menu.addItem(downloads)

        menu.addItem(NSMenuItem.separator())

        let close = NSMenuItem(title: "Close Window", action: #selector(closeWindowFromMenu), keyEquivalent: "w")
        close.keyEquivalentModifierMask = [.command]
        menu.addItem(close)

        menu.items.forEach { $0.target = self }

        let location = NSPoint(x: moreButton.bounds.midX, y: moreButton.bounds.minY - 4)
        menu.popUp(positioning: nil, at: location, in: moreButton)
    }

    @objc private func closeWindowFromMenu() {
        window?.performClose(nil)
    }

    /// WKWebView often reports the requested URL as `title` until the real document title is available.
    private func isLikelyURLString(_ raw: String) -> Bool {
        let t = raw.trimmingCharacters(in: .whitespacesAndNewlines)
        if t.isEmpty { return false }
        if t.hasPrefix("http://") || t.hasPrefix("https://") { return true }
        if t.contains("://") { return true }
        if let u = URL(string: t), u.scheme != nil, u.host != nil { return true }
        return false
    }

    private func updateSiteNameFromWebView() {
        let loading = webView.isLoading
        let host = webView.url?.host?.trimmingCharacters(in: .whitespacesAndNewlines)
        let rawTitle = webView.title?.trimmingCharacters(in: .whitespacesAndNewlines)
        let pageTitle: String? = {
            guard let rawTitle, !rawTitle.isEmpty else { return nil }
            return isLikelyURLString(rawTitle) ? nil : rawTitle
        }()

        // Prefer document title; never show hostname or URL-like strings while a load is in progress.
        let preferred: String
        if let pageTitle {
            preferred = pageTitle
        } else if loading {
            preferred = "Loading…"
        } else if let host, !host.isEmpty {
            preferred = host
        } else {
            preferred = "Browser"
        }

        siteNameLabel.stringValue = preferred
        window?.title = preferred

        guard let url = webView.url else {
            siteIconView.image = NSImage(systemSymbolName: "globe", accessibilityDescription: "Site")?
                .withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold))
            return
        }

        let key = (url.host ?? url.absoluteString).lowercased()
        guard currentSiteIconURLKey != key else { return }
        currentSiteIconURLKey = key

        Task { [weak self] in
            guard let self else { return }
            let fetched = await FaviconImageCache.shared.image(for: url)
            await MainActor.run {
                guard self.currentSiteIconURLKey == key else { return }
                if let fetched {
                    self.siteIconView.image = fetched
                } else {
                    self.siteIconView.image = NSImage(systemSymbolName: "globe", accessibilityDescription: "Site")?
                        .withSymbolConfiguration(NSImage.SymbolConfiguration(pointSize: 12, weight: .semibold))
                    self.siteIconView.contentTintColor = NSColor.secondaryLabelColor
                }
            }
        }
    }

    private func updateToolbarState() {
        backButton.isEnabled = webView.canGoBack
        forwardButton.isEnabled = webView.canGoForward

        // Keep reload enabled; it matches typical browser behavior.
        reloadButton.isEnabled = true

        // Sync center site name.
        updateSiteNameFromWebView()
        updateAddToStatusBarButtonAppearance()
        applyPremiumLockedTitlebarAppearance()
    }

    private func showAlert(title: String, message: String) {
        let alert = NSAlert()
        alert.messageText = title
        alert.informativeText = message
        alert.addButton(withTitle: "OK")
        alert.alertStyle = .informational
        alert.runModal()
    }

    /// Gemini’s SPA does not honor `?prompt=` on `/app`; after navigation we fill the visible composer (retries for async UI / sign-in).
    private func injectGeminiComposerPromptIfNeeded(webView: WKWebView) {
        guard let prompt = injectComposerPrompt, !geminiPromptWasInjected, !geminiInjectionIsActive else { return }
        guard let host = webView.url?.host?.lowercased(), host.contains("gemini.google.com") else { return }
        guard let jsonData = try? JSONSerialization.data(withJSONObject: [prompt]),
              let jsonArrayLiteral = String(data: jsonData, encoding: .utf8) else { return }

        geminiInjectionIsActive = true
        let script = """
        (function() {
          return new Promise(function(resolve) {
            var text = \(jsonArrayLiteral)[0];
            var n = 0;
            var timer = setInterval(function() {
              n++;
              function fillTextarea(el) {
                if (!el || !el.offsetParent) return false;
                var r = el.getBoundingClientRect();
                if (r.width < 8 || r.height < 8) return false;
                el.focus();
                el.value = text;
                el.dispatchEvent(new Event('input', { bubbles: true }));
                el.dispatchEvent(new Event('change', { bubbles: true }));
                return true;
              }
              function fillContentEditable(ce) {
                if (!ce || !ce.isContentEditable || !ce.offsetParent) return false;
                ce.focus();
                ce.innerText = text;
                ce.dispatchEvent(new InputEvent('input', { bubbles: true, data: text }));
                return true;
              }
              function tryFillInRoot(root) {
                var areas = root.querySelectorAll('textarea');
                var i, el;
                for (i = 0; i < areas.length; i++) {
                  if (fillTextarea(areas[i])) return true;
                }
                var editables = root.querySelectorAll('[contenteditable="true"]');
                for (i = 0; i < editables.length; i++) {
                  if (fillContentEditable(editables[i])) return true;
                }
                var nodes = root.querySelectorAll('*');
                for (i = 0; i < nodes.length; i++) {
                  var sh = nodes[i].shadowRoot;
                  if (sh && tryFillInRoot(sh)) return true;
                }
                return false;
              }
              function tryFill() {
                if (tryFillInRoot(document)) return true;
                return false;
              }
              if (tryFill()) { clearInterval(timer); resolve(true); }
              else if (n >= 80) { clearInterval(timer); resolve(false); }
            }, 250);
          });
        })();
        """
        webView.evaluateJavaScript(script) { [weak self] result, _ in
            guard let self else { return }
            self.geminiInjectionIsActive = false
            if (result as? NSNumber)?.boolValue == true {
                self.geminiPromptWasInjected = true
            }
        }
    }
}

extension InAppBrowserWindowController: WKNavigationDelegate {
    func webView(
        _ webView: WKWebView,
        decidePolicyFor navigationAction: WKNavigationAction,
        decisionHandler: @escaping (WKNavigationActionPolicy) -> Void
    ) {
        if navigationAction.targetFrame == nil, let url = navigationAction.request.url {
            InAppBrowserWindowManager.shared.open(url: url)
            decisionHandler(.cancel)
            return
        }

        decisionHandler(.allow)
    }

    func webView(_ webView: WKWebView, didFinish navigation: WKNavigation!) {
        updateToolbarState()
        injectGeminiComposerPromptIfNeeded(webView: webView)
    }

    func webView(_ webView: WKWebView, didStartProvisionalNavigation navigation: WKNavigation!) {
        updateToolbarState()
    }

    func webView(_ webView: WKWebView, didCommit navigation: WKNavigation!) {
        updateToolbarState()
    }

    func webView(
        _ webView: WKWebView,
        didFailProvisionalNavigation navigation: WKNavigation!,
        withError error: Error
    ) {
        updateToolbarState()
        showLoadError(error)
    }

    func webView(_ webView: WKWebView, didFail navigation: WKNavigation!, withError error: Error) {
        updateToolbarState()
        showLoadError(error)
    }

    private func showLoadError(_ error: Error) {
        let message = """
        <html>
        <body style="background:#111;color:#EEE;font-family:-apple-system, sans-serif;padding:24px;">
        <h2>Page failed to load</h2>
        <p>Could not open <b>\(initialURL.absoluteString)</b></p>
        <p>\(error.localizedDescription)</p>
        </body>
        </html>
        """
        webView.loadHTMLString(message, baseURL: nil)
        window?.title = "Load Error"
    }
}

extension InAppBrowserWindowController: WKUIDelegate {
    func webView(
        _ webView: WKWebView,
        createWebViewWith configuration: WKWebViewConfiguration,
        for navigationAction: WKNavigationAction,
        windowFeatures: WKWindowFeatures
    ) -> WKWebView? {
        guard let url = navigationAction.request.url else { return nil }
        InAppBrowserWindowManager.shared.open(url: url)
        return nil
    }
}
