import SwiftUI
import AppKit

struct AppTileView: View {
    let app: LauncherApp
    let onTap: () -> Void
    let onHide: (() -> Void)?

    @ObservedObject private var premiumStore = PremiumStore.shared
    @Environment(\.colorScheme) private var colorScheme
    @State private var isHovering = false
    @AppStorage("pinnedTileIDsData") private var pinnedTileIDsData = ""
    @AppStorage("statusBarAppIDsData") private var statusBarAppIDsData = ""
    @AppStorage("widgetAppIDsData") private var widgetAppIDsData = ""
    private let tileCornerRadius: CGFloat = 16

    var body: some View {
        ZStack(alignment: .topTrailing) {
            Button(action: onTap) {
                tileContent
            }
            .buttonStyle(.plain)

            if isPinned {
                Image(systemName: "pin.fill")
                    .font(.system(size: 12, weight: .semibold))
                    .foregroundStyle(pinForegroundColor)
                    .frame(width: 26, height: 26)
                    .background(
                        Circle()
                            .fill(controlBackgroundColor)
                    )
                    .overlay(
                        Circle()
                            .stroke(controlStrokeColor, lineWidth: 1)
                    )
                    .shadow(color: shadowColor, radius: 10, x: 0, y: 6)
                    .padding(8)
                    .frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
                    .allowsHitTesting(false)
            }

            if !app.isCreateNew {
                Menu {
                    Button(isPinned ? "Unpin" : "Pin") { togglePin() }
                    Divider()
                    if isInStatusBar {
                        Button("Remove from StatusBar") { removeFromStatusBar() }
                    } else {
                        Button("Add to StatusBar") { addToStatusBar() }
                            .disabled(!premiumStore.isPremiumUnlocked)
                    }
                    Button("Add to desktop") { addToDesktop() }
                        .disabled(!premiumStore.isPremiumUnlocked)
                    Button("Add Widget") { addWidget() }
                    Divider()
                    Button("Remove from View") { onHide?() }
                } label: {
                    Image(systemName: "ellipsis")
                        .font(.system(size: 13, weight: .semibold))
                        .rotationEffect(.degrees(90))
                        .foregroundStyle(controlForegroundColor)
                        .frame(width: 26, height: 26)
                        .background(
                            Circle()
                                .fill(controlBackgroundColor)
                        )
                        .overlay(
                            Circle()
                                .stroke(controlStrokeColor, lineWidth: 1)
                        )
                        .shadow(color: shadowColor, radius: 10, x: 0, y: 6)
                }
                .menuStyle(.borderlessButton)
                .modifier(HideMenuIndicatorIfAvailable())
                .padding(8)
                .opacity(isHovering ? 1 : 0.0)
                .animation(.easeOut(duration: 0.12), value: isHovering)
            }
        }
        .contextMenu {
            if !app.isCreateNew {
                Button(isPinned ? "Unpin" : "Pin") { togglePin() }
                Divider()
                if isInStatusBar {
                    Button("Remove from StatusBar") { removeFromStatusBar() }
                } else {
                    Button("Add to StatusBar") { addToStatusBar() }
                        .disabled(!premiumStore.isPremiumUnlocked)
                }
                Button("Add to desktop") { addToDesktop() }
                    .disabled(!premiumStore.isPremiumUnlocked)
                Button("Add Widget") { addWidget() }
                Divider()
                Button("Remove from View") { onHide?() }
            }
        }
        .onHover { hovering in
            isHovering = hovering
        }
    }

    private var tileContent: some View {
        VStack(spacing: 8) {
            AppIconView(
                app: app,
                size: 48,
                showAppBackground: false,
                iconPaddingFactor: 0.07
            )
            // Subtle depth like an app-store tile.
            .shadow(color: .black.opacity(0.14), radius: 8, x: 0, y: 4)

            Text(app.name)
                .font(.system(size: 14, weight: .semibold))
                .foregroundStyle(titleColor)
                .lineLimit(1)
                .truncationMode(.tail)
                .frame(maxWidth: .infinity)
        }
        .padding(.horizontal, 10)
        .padding(.vertical, 12)
        .background(
            RoundedRectangle(cornerRadius: tileCornerRadius, style: .continuous)
                .fill(tileFillColor)
        )
        .overlay(
            RoundedRectangle(cornerRadius: tileCornerRadius, style: .continuous)
                .stroke(tileStrokeColor, lineWidth: 1)
        )
        .shadow(
            color: shadowColor,
            radius: isHovering ? 18 : 10,
            x: 0,
            y: isHovering ? 12 : 6
        )
        .scaleEffect(isHovering ? 1.05 : 1.0)
        // Ensure tiles look consistent across the adaptive grid.
        .frame(maxWidth: .infinity, minHeight: 94)
        .contentShape(RoundedRectangle(cornerRadius: tileCornerRadius, style: .continuous))
        .animation(.spring(response: 0.22, dampingFraction: 0.82), value: isHovering)
    }

    private var isPinned: Bool {
        pinnedIDs.contains(app.id)
    }

    private var isInStatusBar: Bool {
        statusBarIDs.contains(app.id)
    }

    private var pinnedIDs: Set<UUID> {
        decodeUUIDSet(from: pinnedTileIDsData)
    }

    private var statusBarIDs: Set<UUID> {
        decodeUUIDSet(from: statusBarAppIDsData)
    }

    private func togglePin() {
        var updated = pinnedIDs
        if updated.contains(app.id) {
            updated.remove(app.id)
        } else {
            updated.insert(app.id)
        }
        pinnedTileIDsData = encodeUUIDSet(updated)
    }

    private func addToStatusBar() {
        var updated = decodeUUIDSet(from: statusBarAppIDsData)
        guard updated.insert(app.id).inserted else {
            showAlert(title: "Already added", message: "“\(app.name)” is already in the menu bar.")
            return
        }
        statusBarAppIDsData = encodeUUIDSet(updated)
        LauncherApp.notifyStatusBarShortcutIdentifiersChanged()
        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."
        )
    }

    private func removeFromStatusBar() {
        var updated = decodeUUIDSet(from: statusBarAppIDsData)
        guard updated.remove(app.id) != nil else { return }
        statusBarAppIDsData = encodeUUIDSet(updated)
        LauncherApp.notifyStatusBarShortcutIdentifiersChanged()
    }

    private func addWidget() {
        var updated = decodeUUIDSet(from: widgetAppIDsData)
        updated.insert(app.id)
        widgetAppIDsData = encodeUUIDSet(updated)
        NotificationCenter.default.post(
            name: .openWidgetsPage,
            object: nil,
            userInfo: ["appID": app.id]
        )
    }

    private func addToDesktop() {
        guard let webURL = app.webURL else {
            showAlert(title: "Unavailable", message: "Desktop shortcuts need a web address.")
            return
        }
        DesktopWeblocShortcutPresenter.beginSavePanel(forAppNamed: app.name, webURL: webURL)
    }

    private func decodeUUIDSet(from dataString: String) -> Set<UUID> {
        guard !dataString.isEmpty, let data = dataString.data(using: .utf8) else { return [] }
        do {
            let strings = try JSONDecoder().decode([String].self, from: data)
            return Set(strings.compactMap(UUID.init(uuidString:)))
        } catch {
            return []
        }
    }

    private func encodeUUIDSet(_ set: Set<UUID>) -> String {
        do {
            let strings = set.map(\.uuidString).sorted()
            let data = try JSONEncoder().encode(strings)
            return String(decoding: data, as: UTF8.self)
        } catch {
            return ""
        }
    }

    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()
    }

    private var titleColor: Color {
        colorScheme == .dark ? .white.opacity(0.95) : .primary.opacity(0.94)
    }

    private var pinForegroundColor: Color {
        colorScheme == .dark ? .white.opacity(0.92) : .primary.opacity(0.88)
    }

    private var controlForegroundColor: Color {
        colorScheme == .dark
        ? .white.opacity(isHovering ? 0.92 : 0.75)
        : .primary.opacity(isHovering ? 0.90 : 0.68)
    }

    private var controlBackgroundColor: Color {
        colorScheme == .dark
        ? .black.opacity(isHovering ? 0.35 : 0.2)
        : .black.opacity(isHovering ? 0.08 : 0.04)
    }

    private var controlStrokeColor: Color {
        colorScheme == .dark
        ? .white.opacity(isHovering ? 0.18 : 0.12)
        : .black.opacity(isHovering ? 0.12 : 0.07)
    }

    private var tileFillColor: Color {
        colorScheme == .dark
        ? .white.opacity(isHovering ? 0.06 : 0.025)
        : .white.opacity(isHovering ? 0.72 : 0.52)
    }

    private var tileStrokeColor: Color {
        colorScheme == .dark
        ? .white.opacity(isHovering ? 0.18 : 0.08)
        : .black.opacity(isHovering ? 0.10 : 0.05)
    }

    private var shadowColor: Color {
        .black.opacity(colorScheme == .dark ? (isHovering ? 0.20 : 0.10) : (isHovering ? 0.08 : 0.03))
    }
}

extension AppTileView {
    init(app: LauncherApp, onTap: @escaping () -> Void) {
        self.app = app
        self.onTap = onTap
        self.onHide = nil
    }

    init(app: LauncherApp, onTap: @escaping () -> Void, onHide: @escaping () -> Void) {
        self.app = app
        self.onTap = onTap
        self.onHide = onHide
    }
}

private struct HideMenuIndicatorIfAvailable: ViewModifier {
    func body(content: Content) -> some View {
        if #available(macOS 13.0, *) {
            content.menuIndicator(.hidden)
        } else {
            content
        }
    }
}

struct AppDetailView: View {
    let app: LauncherApp
    @Environment(\.dismiss) private var dismiss
    @Environment(\.colorScheme) private var colorScheme

    var body: some View {
        ZStack {
            Group {
                if colorScheme == .dark {
                    Color(red: 0.09, green: 0.09, blue: 0.11)
                } else {
                    Color(nsColor: .windowBackgroundColor)
                }
            }
                .ignoresSafeArea()

            VStack(spacing: 14) {
                AppIconView(app: app, size: 76)
                Text(app.name)
                    .font(.system(size: 22, weight: .semibold))
                    .foregroundStyle(.primary)
                Text(app.description)
                    .font(.system(size: 14))
                    .foregroundStyle(.secondary)
                Button("Close") {
                    dismiss()
                }
                .buttonStyle(.borderedProminent)
            }
            .padding(24)
        }
    }
}
