import AppKit
import SwiftUI

final class WidgetsWindowManager {
    static let shared = WidgetsWindowManager()

    private var windowController: NSWindowController?
    private let defaultWindowSize = CGSize(width: 1120, height: 760)

    private init() {}

    func open(apps: [LauncherApp], selectedAppID: UUID?) {
        if let controller = windowController, controller.window != nil {
            if let hosting = controller.contentViewController as? NSHostingController<WidgetsWindowRootView> {
                hosting.rootView = WidgetsWindowRootView(apps: apps, selectedAppID: selectedAppID)
            }
            if let window = controller.window {
                applyScreenBoundedMaxSize(to: window)
                resizeAndCenter(window: window)
            }
            controller.showWindow(nil)
            controller.window?.makeKeyAndOrderFront(nil)
            NSApp.activate(ignoringOtherApps: true)
            return
        }

        let root = WidgetsWindowRootView(apps: apps, selectedAppID: selectedAppID)
        let hosting = NSHostingController(rootView: root)

        let window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: defaultWindowSize.width, height: defaultWindowSize.height),
            styleMask: [.titled, .closable, .miniaturizable],
            backing: .buffered,
            defer: false
        )
        window.title = "Widgets"
        window.contentViewController = hosting
        window.isReleasedWhenClosed = false
        window.minSize = NSSize(width: 900, height: 620)
        applyScreenBoundedMaxSize(to: window)
        window.standardWindowButton(.zoomButton)?.isHidden = true
        resizeAndCenter(window: window)

        let controller = NSWindowController(window: window)
        self.windowController = controller

        controller.showWindow(nil)
        window.makeKeyAndOrderFront(nil)
        NSApp.activate(ignoringOtherApps: true)
    }

    private func resizeAndCenter(window: NSWindow) {
        let targetSize = NSSize(width: defaultWindowSize.width, height: defaultWindowSize.height)
        let screenFrame = (window.screen ?? NSScreen.main)?.visibleFrame ?? NSRect(x: 0, y: 0, width: 1440, height: 900)
        let clampedWidth = min(max(targetSize.width, window.minSize.width), screenFrame.width)
        let clampedHeight = min(max(targetSize.height, window.minSize.height), screenFrame.height)

        let x = screenFrame.origin.x + (screenFrame.width - clampedWidth) / 2
        let y = screenFrame.origin.y + (screenFrame.height - clampedHeight) / 2
        let frame = NSRect(x: x, y: y, width: clampedWidth, height: clampedHeight)
        window.setFrame(frame, display: true)
    }

    /// Keeps the Widgets window from growing past the visible screen when hosted SwiftUI reports a wide fitting size.
    private func applyScreenBoundedMaxSize(to window: NSWindow) {
        let screenFrame = (window.screen ?? NSScreen.main)?.visibleFrame ?? NSRect(x: 0, y: 0, width: 1440, height: 900)
        window.maxSize = NSSize(width: max(window.minSize.width, screenFrame.width),
                                height: max(window.minSize.height, screenFrame.height))
    }
}

private struct WidgetsWindowRootView: View {
    let apps: [LauncherApp]
    let selectedAppID: UUID?

    @AppStorage("widgetAppIDsData") private var widgetAppIDsData = ""
    /// Survives `NSHostingController.rootView` replacement so the sidebar stays selected like macOS widget settings.
    @AppStorage("widgetsLibrarySidebarSelectedAppID") private var persistedSidebarAppID: String = ""

    @State private var selection: UUID?
    @Environment(\.colorScheme) private var colorScheme

    var body: some View {
        ZStack {
            VisualEffectBlur(
                material: colorScheme == .dark ? .hudWindow : .underWindowBackground,
                blendingMode: .behindWindow
            )
                .ignoresSafeArea()
            (colorScheme == .dark ? Color.black.opacity(0.22) : Color.white.opacity(0.28))
                .ignoresSafeArea()

            WidgetsRootView(
                apps: apps,
                selectedAppID: $selection,
                widgetLibraryIDsData: $widgetAppIDsData
            )
            .padding(.top, 12)
        }
        .onAppear {
            restoreSelectionFromStorage()
        }
        .onChange(of: selectedAppID) { newValue in
            if let newValue {
                selection = newValue
                persistedSidebarAppID = newValue.uuidString
            }
        }
        .onChange(of: selection) { newValue in
            if let newValue {
                persistedSidebarAppID = newValue.uuidString
            }
        }
    }

    private func restoreSelectionFromStorage() {
        if let selectedAppID {
            selection = selectedAppID
            persistedSidebarAppID = selectedAppID.uuidString
            return
        }
        if let stored = UUID(uuidString: persistedSidebarAppID),
           apps.contains(where: { $0.id == stored }) {
            selection = stored
            return
        }
        if let first = apps.first {
            selection = first.id
            persistedSidebarAppID = first.id.uuidString
        }
    }
}

