#!/bin/bash

# --- CONFIGURATION ---
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
if [ -f "$SCRIPT_DIR/version.json" ]; then
    APP_VERSION=$(python3 -c "import json; print(json.load(open('$SCRIPT_DIR/version.json'))['version'])" 2>/dev/null || echo "1.0.8")
else
    APP_VERSION="1.0.8"
fi
APP_NAME="Snap Back"

# --- CYBERPUNK THEME COLORS ---
BOLD='\033[1m'
# Neon Palette (256-color)
NEON_CYAN='\033[38;5;51m'
NEON_BLUE='\033[38;5;33m'
NEON_GREEN='\033[38;5;82m'
NEON_PURPLE='\033[38;5;129m'
ALERT_RED='\033[38;5;196m'
WARN_YELLOW='\033[38;5;226m'
DIM_GREY='\033[38;5;238m'
TXT_GREY='\033[38;5;250m'
NC='\033[0m' # No Color

# --- UI UTILITIES ---

# Tactical Separator
print_line() {
    printf "${NEON_BLUE}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
}

# Modular Step Function
show_step() {
    local step_num="$1"
    local desc="$2"
    # Pads number with zero if less than 10
    local fmt_num=$(printf "%02d" $step_num)
    printf "${NEON_PURPLE}❯❯ [${fmt_num}] ${NEON_CYAN}${desc}${NC}\n"
}

# Spinner with rotating slash
show_spinner() {
    local pid=$1
    local delay=0.1
    local spinstr='|/-\'
    printf "  "
    while ps -p $pid > /dev/null 2>&1; do
        local temp=${spinstr#?}
        printf "${NEON_GREEN}%c${NC}" "$spinstr"
        local spinstr=$temp${spinstr%"$temp"}
        sleep $delay
        printf "\b"
    done
    printf " \b\b"
}

# Cyber Header
show_banner() {
    clear
    printf "${NEON_CYAN}"
    cat << "EOF"
 █▀ █▄ █ ▄▀█ █▀█  █▄▄ ▄▀█ █▀▀ █▄▀
 ▄█ █ ▀█ █▀█ █▀▀  █▄█ █▀█ █▄▄ █ █
EOF
    printf "${NC}\n"
    print_line
    printf " ${BOLD}BUILDER & INSTALLER PROTOCOL${NC}\n"
    printf " ${TXT_GREY}Author: Arun Thomas${NC}\n"
    print_line
    printf "\n"
}

# High-Impact Outcome: Success
show_success_art() {
    printf "\n${NEON_GREEN}"
    cat << "EOF"
 █▀ █ █ █▀▀ █▀▀ █▀▀ █▀ █▀
 ▄█ █▄█ █▄▄ █▄▄ ██▄ ▄█ ▄█
EOF
    printf "${NC}\n"
}

# High-Impact Outcome: Failure
show_failure_art() {
    printf "\n${ALERT_RED}"
    cat << "EOF"
 █▀▀ ▄▀█ █ █   █▀▀ █▀▄
 █▀  █▀█ █ █▄▄ ██▄ █▄▀
EOF
    printf "${NC}\n"
}

# --- WINDOW RESIZE ---
resize_window() {
    osascript -e 'tell application "Terminal" to set number of rows of front window to 42' -e 'tell application "Terminal" to set number of columns of front window to 85' &>/dev/null || \
    osascript -e 'tell application "iTerm" to set columns of current session of current window to 85' -e 'tell application "iTerm" to set rows of current session of current window to 42' &>/dev/null || true
}

# --- EXECUTION START ---

resize_window
show_banner

# --- STEP 0: SYSTEM CHECK ---
show_step 0 "Scanning system for compilation tools..."

if ! xcode-select -p &>/dev/null; then
    printf "   ${WARN_YELLOW}⚠ Tools missing. Initiating download sequence...${NC}\n"
    xcode-select --install
    print_line
    printf "   ${WARN_YELLOW}WAITING FOR USER INTERACTION...${NC}\n"
    printf "   Please complete the installer prompt.\n"
    printf "   Press [ENTER] when installation is confirmed..."
    read -r

    if ! xcode-select -p &>/dev/null; then
        show_failure_art
        printf "\n"
        printf "   ${ALERT_RED}Installation not confirmed. Xcode Command Line Tools are required.${NC}\n"
        printf "   ${TXT_GREY}Please rerun the script and complete the installation.${NC}\n"
        exit 1
    fi
else
    printf "   ${NEON_GREEN}✔ Xcode Command Line Tools detected.${NC}\n"
fi
printf "\n"

# --- STEP 1: ENVIRONMENT ---
# Create a secure temporary directory
BUILD_DIR=$(mktemp -d)

show_step 1 "Initializing clean build workspace..."
printf "   Target Path: ${TXT_GREY}$BUILD_DIR${NC}\n"

# Move into the temp directory
pushd "$BUILD_DIR" > /dev/null

# --- STEP 2: ASSET GENERATION ---
show_step 2 "Synthesizing high-resolution assets..."

(
cat > GenIcon.swift <<'EOF'
import Cocoa

let size = CGSize(width: 1024, height: 1024)
let image = NSImage(size: size)
image.lockFocus()

let context = NSGraphicsContext.current!.cgContext

// 1. Background (Dark Rounded Rect)
let bgPath = NSBezierPath(roundedRect: NSRect(origin: .zero, size: size), xRadius: 224, yRadius: 224)
NSColor(red: 0.12, green: 0.12, blue: 0.14, alpha: 1.0).setFill() // Dark Grey
bgPath.fill()

context.setShadow(offset: CGSize(width: 0, height: -10), blur: 25, color: NSColor.black.withAlphaComponent(0.6).cgColor)

// 2. Corners (Restored & Adjusted)
let strokePath = NSBezierPath()
strokePath.lineWidth = 70
strokePath.lineCapStyle = .round
strokePath.lineJoinStyle = .round
let padding: CGFloat = 160
let legLength: CGFloat = 160

func drawCorner(start: CGPoint, corner: CGPoint, end: CGPoint) {
    strokePath.move(to: start)
    strokePath.line(to: corner)
    strokePath.line(to: end)
}

// Top Left
drawCorner(start: CGPoint(x: padding, y: 1024 - padding - legLength), corner: CGPoint(x: padding, y: 1024 - padding), end: CGPoint(x: padding + legLength, y: 1024 - padding))
// Top Right
drawCorner(start: CGPoint(x: 1024 - padding - legLength, y: 1024 - padding), corner: CGPoint(x: 1024 - padding, y: 1024 - padding), end: CGPoint(x: 1024 - padding, y: 1024 - padding - legLength))
// Bottom Left
drawCorner(start: CGPoint(x: padding, y: padding + legLength), corner: CGPoint(x: padding, y: padding), end: CGPoint(x: padding + legLength, y: padding))
// Bottom Right
drawCorner(start: CGPoint(x: 1024 - padding - legLength, y: padding), corner: CGPoint(x: 1024 - padding, y: padding), end: CGPoint(x: 1024 - padding, y: padding + legLength))

let cyan = NSColor(red: 0.0, green: 1.0, blue: 1.0, alpha: 1.0)
cyan.setStroke()
strokePath.stroke()

// 3. Central Arrow (Reduced Size & Centered)
let arrowPath = NSBezierPath()
let center = CGPoint(x: 512, y: 512)
let radius: CGFloat = 200 // Reduced radius to fit inside corners
let startAngle: CGFloat = -30
let endAngle: CGFloat = 220

arrowPath.appendArc(withCenter: center, radius: radius, startAngle: startAngle, endAngle: endAngle, clockwise: false)
arrowPath.lineWidth = 70
arrowPath.lineCapStyle = .round
arrowPath.stroke()

// 4. Arrow Head (Classic & Aligned)
let endRad = endAngle * .pi / 180.0
let tip = CGPoint(
    x: center.x + radius * cos(endRad),
    y: center.y + radius * sin(endRad)
)
let tangent = endAngle + 90

let headPath = NSBezierPath()
// Classic Triangular Arrowhead
headPath.move(to: CGPoint(x: 0, y: 0))      // Tip
headPath.line(to: CGPoint(x: -120, y: 100)) // Top Back
headPath.line(to: CGPoint(x: -80, y: 0))    // Center Back (Slight indent)
headPath.line(to: CGPoint(x: -120, y: -100))// Bottom Back
headPath.close()

var transform = AffineTransform()
// Move to the end of the arc
transform.translate(x: tip.x, y: tip.y)
// Rotate to tangent
transform.rotate(byDegrees: tangent)
// Push FORWARD slightly (55px) along the tangent to extend past the round cap.
// 35px is flush with cap (70/2), 55px pushes it out to the "corner".
transform.translate(x: 55, y: 0)

headPath.transform(using: transform)
cyan.setFill()
headPath.fill()

image.unlockFocus()

// Export
if let tiff = image.tiffRepresentation, let bitmap = NSBitmapImageRep(data: tiff), let png = bitmap.representation(using: .png, properties: [:]) {
    try? png.write(to: URL(fileURLWithPath: "AppIcon.png"))
}
EOF

swiftc GenIcon.swift -o GenIcon
./GenIcon

# Generate Iconset
mkdir AppIcon.iconset
sips -z 16 16     AppIcon.png --out AppIcon.iconset/icon_16x16.png > /dev/null
sips -z 32 32     AppIcon.png --out AppIcon.iconset/icon_16x16@2x.png > /dev/null
sips -z 32 32     AppIcon.png --out AppIcon.iconset/icon_32x32.png > /dev/null
sips -z 64 64     AppIcon.png --out AppIcon.iconset/icon_32x32@2x.png > /dev/null
sips -z 128 128   AppIcon.png --out AppIcon.iconset/icon_128x128.png > /dev/null
sips -z 256 256   AppIcon.png --out AppIcon.iconset/icon_128x128@2x.png > /dev/null
sips -z 256 256   AppIcon.png --out AppIcon.iconset/icon_256x256.png > /dev/null
sips -z 512 512   AppIcon.png --out AppIcon.iconset/icon_256x256@2x.png > /dev/null
sips -z 512 512   AppIcon.png --out AppIcon.iconset/icon_512x512.png > /dev/null
sips -z 1024 1024 AppIcon.png --out AppIcon.iconset/icon_512x512@2x.png > /dev/null
iconutil -c icns AppIcon.iconset
) &
show_spinner $!
printf "   ${NEON_GREEN}✔ Assets Compiled (New Logo).${NC}\n\n"

# --- STEP 3: BUNDLE STRUCTURE ---
show_step 3 "Constructing Application Bundle..."
mkdir -p "$APP_NAME.app/Contents/MacOS"
mkdir -p "$APP_NAME.app/Contents/Resources"
mv AppIcon.icns "$APP_NAME.app/Contents/Resources/"

cat > "$APP_NAME.app/Contents/Info.plist" <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleExecutable</key>
    <string>SnapBack</string>
    <key>CFBundleIdentifier</key>
    <string>com.aoh.snapback</string>
    <key>CFBundleName</key>
    <string>__APP_NAME__</string>
    <key>CFBundleIconFile</key>
    <string>AppIcon</string>
    <key>CFBundleShortVersionString</key>
    <string>__APP_VERSION__</string>
    <key>CFBundleVersion</key>
    <string>1</string>
    <key>CFBundlePackageType</key>
    <string>APPL</string>
    <key>LSMinimumSystemVersion</key>
    <string>10.15</string>
    <key>NSHighResolutionCapable</key>
    <true/>
    <key>NSAppleEventsUsageDescription</key>
    <string>__APP_NAME__ needs to control other applications to save and restore window layouts.</string>
</dict>
</plist>
EOF

# Inject Version and Name into Info.plist
sed -i '' "s/__APP_VERSION__/$APP_VERSION/g" "$APP_NAME.app/Contents/Info.plist"
sed -i '' "s/__APP_NAME__/$APP_NAME/g" "$APP_NAME.app/Contents/Info.plist"

printf "   ${NEON_GREEN}✔ Info.plist injected.${NC}\n\n"

# --- STEP 4: SOURCE INJECTION ---
show_step 4 "Injecting Source Code..."
cat > SnapBack.swift <<'EOF'
import Cocoa
import SwiftUI

struct UpdateChangelogView: View {
    let changelog: String

    var body: some View {
        ScrollView(.vertical, showsIndicators: true) {
            VStack(alignment: .leading, spacing: 6) {
                Text(changelog)
                    .font(.system(size: 12, weight: .regular))
                    .foregroundColor(Color(NSColor.labelColor))
                    .lineSpacing(3)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .fixedSize(horizontal: false, vertical: true)
            }
            .padding(12)
        }
        .frame(width: 360, height: 150)
        .background(Color(NSColor.controlBackgroundColor))
        .clipShape(RoundedRectangle(cornerRadius: 8, style: .continuous))
        .overlay(
            RoundedRectangle(cornerRadius: 8, style: .continuous)
                .stroke(Color(NSColor.separatorColor).opacity(0.4), lineWidth: 1)
        )
    }
}

func createChangelogView(changelog: String) -> NSView {
    let hostingView = NSHostingView(rootView: UpdateChangelogView(changelog: changelog))
    hostingView.frame = NSRect(x: 0, y: 0, width: 360, height: 150)
    return hostingView
}

// --- CONFIGURATION ---
let appTitle = "__APP_NAME__"
let fileManager = FileManager.default
let home = fileManager.homeDirectoryForCurrentUser
let storageFolder = home.appendingPathComponent("Pictures/Snap Back Profiles")
try? fileManager.createDirectory(at: storageFolder, withIntermediateDirectories: true)

// --- HELPERS ---
func runShell(_ command: String) -> Int32 {
    let task = Process()
    task.launchPath = "/bin/zsh"
    task.arguments = ["-c", command]
    task.launch()
    task.waitUntilExit()
    return task.terminationStatus
}

func runAppleScript(_ source: String) -> (Bool, String?) {
    if let script = NSAppleScript(source: source) {
        var error: NSDictionary?
        let result = script.executeAndReturnError(&error)
        if let err = error {
            let msg = err["NSAppleScriptErrorMessage"] as? String ?? "Unknown AppleScript error"
            return (false, msg)
        }
        let resString = result.stringValue
        return (true, resString)
    }
    return (false, "Failed to initialize NSAppleScript")
}

func getSavedProfiles() -> [String] {
    guard let items = try? fileManager.contentsOfDirectory(atPath: storageFolder.path) else { return [] }
    return items.filter { item in
        let path = storageFolder.appendingPathComponent(item)
        var isDir: ObjCBool = false
        return fileManager.fileExists(atPath: path.path, isDirectory: &isDir) && isDir.boolValue && !item.hasPrefix(".")
    }
}

func showSuccess(message: String) {
    NSSound(named: "Glass")?.play()
    let script = "display notification \"\(message)\" with title \"__APP_NAME__\""
    let _ = runAppleScript(script)
}

func showError(message: String) {
    let alert = NSAlert()
    alert.messageText = "Error"
    alert.informativeText = message
    alert.addButton(withTitle: "OK")
    alert.runModal()
}

func showScriptError(details: String?) {
    let alert = NSAlert()
    alert.messageText = "Operation Failed"
    var info = "__APP_NAME__ encountered an error."
    if let d = details { info += "\n\nError: \(d)" }
    
    // Check for permissions
    let err = details?.lowercased() ?? ""
    let isPermissionIssue = err.contains("not allowed") || err.contains("not authorized") || err.contains("(-1743)")
    
    alert.informativeText = info
    
    if isPermissionIssue {
        alert.addButton(withTitle: "Open Settings")
        alert.addButton(withTitle: "Cancel")
    } else {
        alert.addButton(withTitle: "OK")
    }
    
    let response = alert.runModal()
    
    if isPermissionIssue && response == .alertFirstButtonReturn {
        let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation")!
        NSWorkspace.shared.open(url)
    }
}

var aboutUpdateBtn: NSButton?

func setUpdateLoading(_ loading: Bool) {
    DispatchQueue.main.async {
        guard let btn = aboutUpdateBtn else { return }
        btn.isEnabled = !loading
        btn.attributedTitle = NSAttributedString(string: loading ? "Checking..." : "Updates", attributes: [
            .foregroundColor: NSColor.white,
            .font: NSFont.systemFont(ofSize: 12.5, weight: .medium)
        ])
    }
}

func checkForUpdates(isManual: Bool = false) {
    let now = Date()
    if !isManual {
        if let lastCheck = UserDefaults.standard.object(forKey: "lastUpdateCheckDate") as? Date,
           now.timeIntervalSince(lastCheck) < 86400 {
            return // Only check once per 24 hours on automatic launch
        }
    }
    UserDefaults.standard.set(now, forKey: "lastUpdateCheckDate")
    setUpdateLoading(true)

    URLCache.shared.removeAllCachedResponses()
    let ts = Int(now.timeIntervalSince1970)
    let versionURLStr = "https://raw.githubusercontent.com/arunofhyd/SnapBack/main/version.json?t=\(ts)"
    guard let url = URL(string: versionURLStr) else {
        setUpdateLoading(false)
        if isManual {
            DispatchQueue.main.async {
                let alert = NSAlert()
                NSApp.activate(ignoringOtherApps: true)
                alert.messageText = "Check for Updates Failed"
                alert.informativeText = "Unable to connect to update server."
                alert.addButton(withTitle: "OK")
                alert.runModal()
            }
        }
        return
    }
    var request = URLRequest(url: url)
    request.cachePolicy = .reloadIgnoringLocalAndRemoteCacheData
    request.addValue("no-cache", forHTTPHeaderField: "Cache-Control")
    request.addValue("no-cache", forHTTPHeaderField: "Pragma")

    let task = URLSession.shared.dataTask(with: request) { data, response, error in
        setUpdateLoading(false)
        guard let data = data,
              let json = try? JSONSerialization.jsonObject(with: data, options: []) as? [String: Any],
              let remoteVer = json["version"] as? String,
              let currentVer = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else {
            if isManual {
                DispatchQueue.main.async {
                    let alert = NSAlert()
                    NSApp.activate(ignoringOtherApps: true)
                    alert.messageText = "Check for Updates Failed"
                    alert.informativeText = "Could not check for updates. Please try again later."
                    alert.addButton(withTitle: "OK")
                    alert.runModal()
                }
            }
            return
        }

        let rComponents = remoteVer.split(separator: ".").compactMap { Int($0) }
        let cComponents = currentVer.split(separator: ".").compactMap { Int($0) }

        var updateAvailable = false
        for i in 0..<max(rComponents.count, cComponents.count) {
            let r = i < rComponents.count ? rComponents[i] : 0
            let c = i < cComponents.count ? cComponents[i] : 0
            if r > c { updateAvailable = true; break }
            if r < c { break }
        }

        if updateAvailable {
            var fullChangelog = ""
            if let changes = json["changelog"] as? [[String: Any]] {
                let unreadEntries = changes.filter { entry in
                    guard let v = (entry["version"] as? String)?.replacingOccurrences(of: "v", with: "") else { return false }
                    let rComp = v.split(separator: ".").compactMap { Int($0) }
                    let cComp = currentVer.split(separator: ".").compactMap { Int($0) }
                    for i in 0..<max(rComp.count, cComp.count) {
                        let r = i < rComp.count ? rComp[i] : 0
                        let c = i < cComp.count ? cComp[i] : 0
                        if r > c { return true }
                        if r < c { return false }
                    }
                    return false
                }
                fullChangelog = unreadEntries.compactMap { entry -> String? in
                    guard let v = entry["version"] as? String,
                          let notes = entry["changes"] as? [String] else { return nil }
                    let list = notes.map { "• \($0)" }.joined(separator: "\n")
                    return "Version \(v):\n\(list)"
                }.joined(separator: "\n\n")
            }
            
            DispatchQueue.main.async {
                let alert = NSAlert()
                NSApp.activate(ignoringOtherApps: true)
                alert.icon = NSImage(named: "AppIcon") ?? NSApp.applicationIconImage
                alert.messageText = "SnapBack \(remoteVer) is available"
                let cur = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? currentVer
                alert.informativeText = "You have v\(cur). Here's what's new:"
                
                if !fullChangelog.isEmpty {
                    alert.accessoryView = createChangelogView(changelog: fullChangelog)
                }
                
                alert.addButton(withTitle: "Update Now")
                alert.addButton(withTitle: "Later")
                
                let response = alert.runModal()
                if response == .alertFirstButtonReturn {
                    downloadAndInstallUpdate()
                }
            }
        } else if isManual {
            DispatchQueue.main.async {
                let alert = NSAlert()
                NSApp.activate(ignoringOtherApps: true)
                alert.icon = NSImage(named: "AppIcon") ?? NSApp.applicationIconImage
                alert.messageText = "You're up to date"
                let cur = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? currentVer
                alert.informativeText = "__APP_NAME__ v\(cur) is the latest version."
                alert.addButton(withTitle: "OK")
                alert.runModal()
            }
        }
    }
    task.resume()
}

func downloadAndInstallUpdate() {
    let commandURL = "https://raw.githubusercontent.com/arunofhyd/SnapBack/main/install-snapback.command"
    guard let url = URL(string: commandURL) else { return }
    
    let task = URLSession.shared.downloadTask(with: url) { tempURL, response, error in
        DispatchQueue.main.async {
            let statusCode = (response as? HTTPURLResponse)?.statusCode ?? 0
            if error != nil || statusCode != 200 {
                let err = NSAlert()
                err.alertStyle = .warning
                err.messageText = "Download Failed"
                let reason = error?.localizedDescription ?? "Server returned HTTP \(statusCode)"
                err.informativeText = "Could not download the update:\n\(reason)\n\nPlease check your internet connection and try again."
                err.addButton(withTitle: "OK")
                err.runModal()
                return
            }
            
            guard let tempURL = tempURL else { return }
            
            let downloadsDir = FileManager.default.urls(for: .downloadsDirectory, in: .userDomainMask).first!
            let destURL = downloadsDir.appendingPathComponent("install-snapback.command")
            
            try? FileManager.default.removeItem(at: destURL)
            do {
                try FileManager.default.copyItem(at: tempURL, to: destURL)
                try FileManager.default.setAttributes(
                    [.posixPermissions: NSNumber(value: 0o755)],
                    ofItemAtPath: destURL.path
                )
                NSWorkspace.shared.open(destURL)
            } catch {
                let err = NSAlert()
                err.alertStyle = .warning
                err.messageText = "Could Not Save Installer"
                err.informativeText = "The installer was downloaded but couldn't be saved:\n\(error.localizedDescription)"
                err.addButton(withTitle: "OK")
                err.runModal()
            }
        }
    }
    task.resume()
}

var permissionsWindow: NSWindow?
var permissionButtons: [NSButton] = []
var permissionsTimer: Timer?

class PermissionsActionHandler: NSObject, NSWindowDelegate {
    static let shared = PermissionsActionHandler()

    @objc func timerFired() {
        checkPermissionsStatus()
    }

    @objc func openScreenRecording() {
        if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_ScreenCapture") {
            NSWorkspace.shared.open(url)
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            checkPermissionsStatus()
        }
    }

    @objc func openAccessibility() {
        let options = [kAXTrustedCheckOptionPrompt.takeUnretainedValue() as String: true] as CFDictionary
        _ = AXIsProcessTrustedWithOptions(options)
        if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Accessibility") {
            NSWorkspace.shared.open(url)
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            checkPermissionsStatus()
        }
    }

    @objc func openAutomation() {
        if let url = URL(string: "x-apple.systempreferences:com.apple.preference.security?Privacy_Automation") {
            NSWorkspace.shared.open(url)
        }
        DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
            checkPermissionsStatus()
        }
    }

    @objc func closePermissions() {
        permissionsTimer?.invalidate()
        permissionsTimer = nil
        permissionButtons = []
        UserDefaults.standard.set(true, forKey: "HasShownFirstRunPermissions")
        if let win = permissionsWindow {
            NSApp.stopModal()
            win.close()
            permissionsWindow = nil
        }
    }

    func windowWillClose(_ notification: Notification) {
        permissionsTimer?.invalidate()
        permissionsTimer = nil
        permissionButtons = []
        NSApp.stopModal()
        permissionsWindow = nil
    }
}

func checkPermissionsStatus() {
    guard permissionsWindow != nil, permissionButtons.count >= 3 else { return }

    // Row 0: Screen Recording
    var screenOk = false
    if #available(macOS 10.15, *) {
        screenOk = CGPreflightScreenCaptureAccess()
    } else {
        screenOk = true
    }
    updatePermButton(permissionButtons[0], isGranted: screenOk)

    // Row 1: Accessibility
    let axOk = AXIsProcessTrusted()
    updatePermButton(permissionButtons[1], isGranted: axOk)
}

func updatePermButton(_ btn: NSButton, isGranted: Bool) {
    if isGranted {
        if btn.title != "✓" {
            btn.title = "✓"
            btn.font = NSFont.systemFont(ofSize: 13, weight: .bold)
            btn.contentTintColor = .systemGreen
            btn.toolTip = "Granted (Click to open settings)"
        }
    } else {
        if btn.title != "Open" {
            btn.title = "Open"
            btn.font = NSFont.systemFont(ofSize: 12, weight: .medium)
            btn.contentTintColor = nil
            btn.toolTip = "Click to open settings"
        }
    }
}

func showPermissionsGuide(isFirstLaunch: Bool = false) {
    if permissionsWindow != nil {
        permissionsWindow?.makeKeyAndOrderFront(nil)
        NSApp.activate(ignoringOtherApps: true)
        return
    }

    struct PermItem {
        let symbol: String
        let color: NSColor
        let title: String
        let desc: String
        let action: Selector
    }

    let width: CGFloat = 460
    let height: CGFloat = 340

    let win = NSWindow(
        contentRect: NSRect(x: 0, y: 0, width: width, height: height),
        styleMask: [.titled, .closable, .fullSizeContentView],
        backing: .buffered, defer: false
    )
    win.titleVisibility = .hidden
    win.titlebarAppearsTransparent = true
    win.isMovableByWindowBackground = true
    win.standardWindowButton(.miniaturizeButton)?.isHidden = true
    win.standardWindowButton(.zoomButton)?.isHidden = true
    win.center()
    win.isReleasedWhenClosed = false
    win.level = .floating

    let bg = NSVisualEffectView(frame: NSRect(x: 0, y: 0, width: width, height: height))
    bg.material = .popover
    bg.blendingMode = .behindWindow
    bg.state = .active

    // App Icon
    let icon = NSImageView(frame: NSRect(x: (width - 56)/2, y: height - 72, width: 56, height: 56))
    icon.image = NSImage(named: "AppIcon") ?? NSApp.applicationIconImage
    icon.imageScaling = .scaleProportionallyUpOrDown
    bg.addSubview(icon)

    // Title & Subtitle
    let title = NSTextField(labelWithString: "Permissions & Setup")
    title.font = NSFont.systemFont(ofSize: 20, weight: .bold)
    title.alignment = .center
    title.frame = NSRect(x: 0, y: height - 100, width: width, height: 24)
    bg.addSubview(title)

    let sub = NSTextField(labelWithString: "Configure SnapBack for full system control")
    sub.font = NSFont.systemFont(ofSize: 12, weight: .medium)
    sub.textColor = .secondaryLabelColor
    sub.alignment = .center
    sub.frame = NSRect(x: 16, y: height - 120, width: width - 32, height: 16)
    bg.addSubview(sub)

    let items: [PermItem] = [
        PermItem(
            symbol: "record.circle.fill",
            color: .systemRed,
            title: "Screen Recording (Required)",
            desc: "Allows capturing window layout previews and coordinates.",
            action: #selector(PermissionsActionHandler.openScreenRecording)
        ),
        PermItem(
            symbol: "hand.point.up.left.fill",
            color: .systemPurple,
            title: "Accessibility (Required)",
            desc: "Allows moving, resizing, and restoring open windows.",
            action: #selector(PermissionsActionHandler.openAccessibility)
        ),
        PermItem(
            symbol: "safari.fill",
            color: .systemBlue,
            title: "Automation / Browser Tabs",
            desc: "Allows capturing and restoring open browser tabs.",
            action: #selector(PermissionsActionHandler.openAutomation)
        )
    ]

    let rowHeight: CGFloat = 48
    let rowYPositions: [CGFloat] = [156, 102, 48]

    permissionButtons = []

    for (idx, item) in items.enumerated() {
        let rowY = rowYPositions[idx]

        // Icon
        let symView = NSImageView(frame: NSRect(x: 32, y: rowY + (rowHeight - 26)/2, width: 26, height: 26))
        let symCfg = NSImage.SymbolConfiguration(pointSize: 20, weight: .semibold)
        symView.image = NSImage(systemSymbolName: item.symbol, accessibilityDescription: nil)?.withSymbolConfiguration(symCfg)
        symView.contentTintColor = item.color
        bg.addSubview(symView)

        // Text
        let textWidth = width - 68 - 115
        let hLabel = NSTextField(labelWithString: item.title)
        hLabel.font = NSFont.systemFont(ofSize: 13, weight: .semibold)
        hLabel.frame = NSRect(x: 68, y: rowY + 24, width: textWidth, height: 18)
        bg.addSubview(hLabel)

        let dLabel = NSTextField(labelWithString: item.desc)
        dLabel.font = NSFont.systemFont(ofSize: 11.5, weight: .regular)
        dLabel.textColor = .secondaryLabelColor
        dLabel.lineBreakMode = .byTruncatingTail
        dLabel.frame = NSRect(x: 68, y: rowY + 4, width: textWidth, height: 18)
        bg.addSubview(dLabel)

        // Action Button
        let btn = NSButton(title: "Open", target: PermissionsActionHandler.shared, action: item.action)
        btn.frame = NSRect(x: width - 32 - 70, y: rowY + (rowHeight - 28)/2, width: 70, height: 28)
        btn.bezelStyle = .rounded
        btn.font = NSFont.systemFont(ofSize: 12, weight: .medium)
        bg.addSubview(btn)
        permissionButtons.append(btn)
    }

    // Done Button (Crisp contrast pill)
    let doneBtn = NSButton(title: "Done", target: PermissionsActionHandler.shared, action: #selector(PermissionsActionHandler.closePermissions))
    doneBtn.frame = NSRect(x: (width - 150)/2, y: 10, width: 150, height: 32)
    doneBtn.isBordered = false
    doneBtn.wantsLayer = true
    doneBtn.layer?.backgroundColor = NSColor.white.cgColor
    doneBtn.layer?.cornerRadius = 16
    doneBtn.layer?.masksToBounds = true
    doneBtn.attributedTitle = NSAttributedString(string: "Done", attributes: [
        .foregroundColor: NSColor.black,
        .font: NSFont.systemFont(ofSize: 13, weight: .semibold)
    ])
    bg.addSubview(doneBtn)

    win.delegate = PermissionsActionHandler.shared
    win.contentView = bg
    win.makeKeyAndOrderFront(nil)
    NSApp.activate(ignoringOtherApps: true)
    permissionsWindow = win

    // Initial check and auto-polling timer for live updates
    checkPermissionsStatus()
    permissionsTimer?.invalidate()
    let t = Timer(timeInterval: 0.8, target: PermissionsActionHandler.shared, selector: #selector(PermissionsActionHandler.timerFired), userInfo: nil, repeats: true)
    RunLoop.current.add(t, forMode: .common)
    permissionsTimer = t

    NSApp.runModal(for: win)
}

func checkFirstRun() {
    let key = "HasShownFirstRunPermissions"
    let defaults = UserDefaults.standard
    let axTrusted = AXIsProcessTrusted()
    var screenTrusted = false
    if #available(macOS 10.15, *) {
        screenTrusted = CGPreflightScreenCaptureAccess()
    } else {
        screenTrusted = true
    }
    
    if !axTrusted || !screenTrusted || !defaults.bool(forKey: key) {
        showPermissionsGuide(isFirstLaunch: true)
    }
}

var aboutWindow: NSWindow?

class AboutActionHandler: NSObject, NSWindowDelegate {
    static let shared = AboutActionHandler()
    
    @objc func contactDev() {
        let subject = "SnapBack feedback"
        let encoded = subject.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? subject
        if let url = URL(string: "mailto:arunthomas04042001@gmail.com?subject=\(encoded)") {
            NSWorkspace.shared.open(url)
        }
    }
    
    @objc func openGit() {
        if let url = URL(string: "https://github.com/arunofhyd/SnapBack") {
            NSWorkspace.shared.open(url)
        }
    }

    @objc func checkUpdates() {
        checkForUpdates(isManual: true)
    }
    
    @objc func closeAboutWin() {
        if let win = aboutWindow {
            NSApp.stopModal()
            win.close()
            aboutWindow = nil
        }
    }

    func windowWillClose(_ notification: Notification) {
        NSApp.stopModal()
        aboutUpdateBtn = nil
        aboutWindow = nil
    }
}

func showAbout() {
    if aboutWindow != nil {
        aboutWindow?.makeKeyAndOrderFront(nil)
        NSApp.activate(ignoringOtherApps: true)
        return
    }

    struct AboutFeature {
        let symbol: String
        let color: NSColor
        let title: String
        let desc: String
    }

    let features: [AboutFeature] = [
        AboutFeature(symbol: "camera.viewfinder", color: .systemCyan, title: "Instant Layout Snapshots", desc: "Saves multi-monitor window geometry and active browser tabs with zero latency."),
        AboutFeature(symbol: "arrow.counterclockwise.circle.fill", color: .systemGreen, title: "1-Click Layout Restore", desc: "Instantly brings all open application windows and browser tabs back to their exact recorded positions."),
        AboutFeature(symbol: "lock.shield.fill", color: .systemPurple, title: "100% On-Device & Private", desc: "All window coordinates, screenshots, and workspace profiles stay strictly local on your Mac."),
        AboutFeature(symbol: "slider.horizontal.3", color: .systemBlue, title: "Multi-Profile Management", desc: "Seamlessly switch between Work, Coding, Reading, and Meeting workspace arrangements."),
        AboutFeature(symbol: "chevron.left.forwardslash.chevron.right", color: .systemPink, title: "Free & Open Source", desc: "SnapBack is free and open source. Check out the project repository on GitHub.")
    ]

    let width: CGFloat = 460
    let textWidth: CGFloat = width - 115
    let para = NSMutableParagraphStyle()
    para.lineSpacing = 2
    let textFont = NSFont.systemFont(ofSize: 11.5, weight: .regular)
    let titleFont = NSFont.systemFont(ofSize: 13, weight: .semibold)

    var featureHeights: [CGFloat] = []
    var totalFeaturesHeight: CGFloat = 0
    for f in features {
        let attr = NSAttributedString(string: f.desc, attributes: [
            .font: textFont,
            .paragraphStyle: para
        ])
        let measured = attr.boundingRect(
            with: NSSize(width: textWidth, height: .greatestFiniteMagnitude),
            options: [.usesLineFragmentOrigin, .usesFontLeading])
        let h = ceil(measured.height) + 24
        featureHeights.append(h)
        totalFeaturesHeight += h + 16
    }
    totalFeaturesHeight -= 16

    let headerHeight: CGFloat = 204
    let bottomSpaceNeeded: CGFloat = 124
    let finalHeight = headerHeight + totalFeaturesHeight + bottomSpaceNeeded

    let win = NSWindow(
        contentRect: NSRect(x: 0, y: 0, width: width, height: finalHeight),
        styleMask: [.titled, .closable, .fullSizeContentView],
        backing: .buffered, defer: false
    )
    win.titleVisibility = .hidden
    win.titlebarAppearsTransparent = true
    win.isMovableByWindowBackground = true
    win.standardWindowButton(.miniaturizeButton)?.isHidden = true
    win.standardWindowButton(.zoomButton)?.isHidden = true
    win.center()
    win.isReleasedWhenClosed = false
    win.level = .floating

    let bg = NSVisualEffectView(frame: NSRect(x: 0, y: 0, width: width, height: finalHeight))
    bg.material = .popover
    bg.blendingMode = .behindWindow
    bg.state = .active

    let icon = NSImageView(frame: NSRect(x: (width - 64)/2, y: finalHeight - 88, width: 64, height: 64))
    icon.image = NSImage(named: "AppIcon") ?? NSApp.applicationIconImage
    icon.imageScaling = .scaleProportionallyUpOrDown
    bg.addSubview(icon)

    let title = NSTextField(labelWithString: "__APP_NAME__")
    title.font = NSFont.systemFont(ofSize: 24, weight: .bold)
    title.alignment = .center
    title.frame = NSRect(x: 0, y: finalHeight - 124, width: width, height: 28)
    bg.addSubview(title)

    let currentVer = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "__APP_VERSION__"
    let ver = NSTextField(labelWithString: "Version \(currentVer)")
    ver.font = NSFont.systemFont(ofSize: 11.5, weight: .medium)
    ver.textColor = .tertiaryLabelColor
    ver.alignment = .center
    ver.frame = NSRect(x: 0, y: finalHeight - 144, width: width, height: 15)
    bg.addSubview(ver)

    let sub = NSTextField(labelWithString: "Instant Window & Tab Layout Snapshots for macOS")
    sub.font = NSFont.systemFont(ofSize: 12, weight: .medium)
    sub.textColor = .secondaryLabelColor
    sub.alignment = .center
    sub.frame = NSRect(x: 16, y: finalHeight - 168, width: width - 32, height: 16)
    bg.addSubview(sub)

    var currentY = finalHeight - headerHeight
    for (i, f) in features.enumerated() {
        let itemH = featureHeights[i]
        let itemY = currentY - itemH

        let symSize: CGFloat = 24
        let symView = NSImageView(frame: NSRect(x: 36, y: itemY + (itemH - symSize)/2 + 2, width: symSize, height: symSize))
        let symCfg = NSImage.SymbolConfiguration(pointSize: 18, weight: .semibold)
        symView.image = NSImage(systemSymbolName: f.symbol, accessibilityDescription: nil)?
            .withSymbolConfiguration(symCfg)
        symView.contentTintColor = f.color
        bg.addSubview(symView)

        let titleLabel = NSTextField(labelWithString: f.title)
        titleLabel.frame = NSRect(x: 74, y: itemY + itemH - 20, width: textWidth, height: 18)
        titleLabel.font = titleFont
        titleLabel.textColor = .labelColor
        titleLabel.isEditable = false
        titleLabel.drawsBackground = false
        titleLabel.isBordered = false
        bg.addSubview(titleLabel)

        let attr = NSAttributedString(string: f.desc, attributes: [
            .font: textFont,
            .foregroundColor: NSColor.secondaryLabelColor,
            .paragraphStyle: para
        ])
        let descLabel = NSTextField(labelWithAttributedString: attr)
        descLabel.frame = NSRect(x: 74, y: itemY, width: textWidth, height: itemH - 22)
        descLabel.lineBreakMode = .byWordWrapping
        descLabel.maximumNumberOfLines = 0
        descLabel.isEditable = false
        descLabel.drawsBackground = false
        descLabel.isBordered = false
        bg.addSubview(descLabel)

        currentY = itemY - 16
    }

    // Author Note
    let credit = NSTextField(labelWithString: "Built by Arun Thomas")
    credit.frame = NSRect(x: 0, y: 78, width: width, height: 16)
    credit.alignment = .center
    credit.font = NSFont.systemFont(ofSize: 11.5, weight: .semibold)
    credit.textColor = .secondaryLabelColor
    bg.addSubview(credit)

    // Action Buttons
    let buttonsY: CGFloat = 26
    let contactW: CGFloat = 92
    let gitW: CGFloat = 92
    let updateW: CGFloat = 92
    let closeW: CGFloat = 104
    let spacing: CGFloat = 10
    let totalW = contactW + gitW + updateW + closeW + (3 * spacing)
    let startX = (width - totalW) / 2

    let contact = NSButton(title: "Contact", target: nil, action: nil)
    contact.frame = NSRect(x: startX, y: buttonsY, width: contactW, height: 34)
    contact.isBordered = false
    contact.wantsLayer = true
    contact.layer?.backgroundColor = NSColor.white.cgColor
    contact.layer?.cornerRadius = 17
    contact.layer?.masksToBounds = true
    contact.attributedTitle = NSAttributedString(string: "Contact", attributes: [
        .foregroundColor: NSColor.black,
        .font: NSFont.systemFont(ofSize: 12.5, weight: .medium)
    ])
    contact.target = AboutActionHandler.shared
    contact.action = #selector(AboutActionHandler.contactDev)
    bg.addSubview(contact)

    let github = NSButton(title: "GitHub", target: nil, action: nil)
    github.frame = NSRect(x: startX + contactW + spacing, y: buttonsY, width: gitW, height: 34)
    github.isBordered = false
    github.wantsLayer = true
    github.layer?.backgroundColor = NSColor.black.cgColor
    github.layer?.cornerRadius = 17
    github.layer?.masksToBounds = true
    github.attributedTitle = NSAttributedString(string: "GitHub", attributes: [
        .foregroundColor: NSColor.white,
        .font: NSFont.systemFont(ofSize: 12.5, weight: .medium)
    ])
    github.target = AboutActionHandler.shared
    github.action = #selector(AboutActionHandler.openGit)
    bg.addSubview(github)

    let update = NSButton(title: "Updates", target: nil, action: nil)
    update.frame = NSRect(x: startX + contactW + spacing + gitW + spacing, y: buttonsY, width: updateW, height: 34)
    update.isBordered = false
    update.wantsLayer = true
    update.layer?.backgroundColor = NSColor.secondaryLabelColor.withAlphaComponent(0.3).cgColor
    update.layer?.cornerRadius = 17
    update.layer?.masksToBounds = true
    update.attributedTitle = NSAttributedString(string: "Updates", attributes: [
        .foregroundColor: NSColor.white,
        .font: NSFont.systemFont(ofSize: 12.5, weight: .medium)
    ])
    update.target = AboutActionHandler.shared
    update.action = #selector(AboutActionHandler.checkUpdates)
    aboutUpdateBtn = update
    bg.addSubview(update)

    let close = NSButton(title: "Done", target: nil, action: nil)
    close.frame = NSRect(x: startX + contactW + spacing + gitW + spacing + updateW + spacing, y: buttonsY, width: closeW, height: 34)
    close.isBordered = false
    close.wantsLayer = true
    close.layer?.backgroundColor = NSColor.controlAccentColor.cgColor
    close.layer?.cornerRadius = 17
    close.layer?.masksToBounds = true
    close.keyEquivalent = "\r"
    close.attributedTitle = NSAttributedString(string: "Done", attributes: [
        .foregroundColor: NSColor.white,
        .font: NSFont.systemFont(ofSize: 12.5, weight: .medium)
    ])
    close.target = AboutActionHandler.shared
    close.action = #selector(AboutActionHandler.closeAboutWin)
    bg.addSubview(close)

    win.delegate = AboutActionHandler.shared
    win.contentView = bg
    win.makeKeyAndOrderFront(nil)
    NSApp.activate(ignoringOtherApps: true)
    aboutWindow = win
    NSApp.runModal(for: win)
}

func deleteProfile(name: String) -> Bool {
    let profileDir = storageFolder.appendingPathComponent(name)
    do {
        if fileManager.fileExists(atPath: profileDir.path) {
            try fileManager.removeItem(at: profileDir)
            return true
        }
        return false
    } catch { return false }
}

// --- CORE LOGIC ---
func saveProfile(name: String) -> Bool {
    let profileDir = storageFolder.appendingPathComponent(name)
    try? fileManager.createDirectory(at: profileDir, withIntermediateDirectories: true)
    
    let dataFile = profileDir.appendingPathComponent("data.scpt")
    let imgFile = profileDir.appendingPathComponent("preview.jpg") 
    
    let status = runShell("screencapture -x -t jpg \"\(imgFile.path)\"")
    if status != 0 { showError(message: "Screenshot failed. Check permissions."); return false }

    // UPGRADE: Handlers with strict 'using terms from' to fix identifier errors
    let script = #"""
    script DataCarrier
        property mainData : {}
    end script
    
    -- HANDLERS
    on getSafariUrls(winIdx)
        set uList to {}
        try
            tell application "Safari"
                if (count of windows) >= winIdx then
                    set tabList to every tab of window winIdx
                    repeat with t in tabList
                        set end of uList to (get URL of t)
                    end repeat
                end if
            end tell
        end try
        return uList
    end getSafariUrls

    on getChromeUrls(theApp, winIdx)
        try
            set scriptSrc to "on run {wIdx}" & return
            set scriptSrc to scriptSrc & "tell application \"" & theApp & "\"" & return
            set scriptSrc to scriptSrc & "set uList to {}" & return
            set scriptSrc to scriptSrc & "if (count of windows) >= wIdx then" & return
            set scriptSrc to scriptSrc & "tell window wIdx" & return
            set scriptSrc to scriptSrc & "set tabList to every tab" & return
            set scriptSrc to scriptSrc & "repeat with t in tabList" & return
            set scriptSrc to scriptSrc & "set end of uList to (URL of t)" & return
            set scriptSrc to scriptSrc & "end repeat" & return
            set scriptSrc to scriptSrc & "end tell" & return
            set scriptSrc to scriptSrc & "end if" & return
            set scriptSrc to scriptSrc & "return uList" & return
            set scriptSrc to scriptSrc & "end tell" & return
            set scriptSrc to scriptSrc & "end run"
            return run script scriptSrc with parameters {winIdx}
        on error
            return {}
        end try
    end getChromeUrls
    
    set thePath to "\#(dataFile.path)"
    
    set chromiumVariants to {"Google Chrome", "Brave Browser", "Microsoft Edge", "Arc", "Vivaldi", "Opera"}
    
    tell application "Finder" to set screenBounds to bounds of window of desktop
    set screenW to item 3 of screenBounds
    set screenH to item 4 of screenBounds

    tell application "System Events"
        set processList to (every process where background only is false)
        repeat with proc in processList
            try
                set appName to name of proc
                if appName is not "__APP_NAME__" and appName is not "app_mode_loader" and appName is not "ControlCenter" then
                    tell proc
                        set allWindows to every window
                        repeat with i from 1 to count of allWindows
                            set thisWindow to item i of allWindows
                            
                            set wPos to position of thisWindow
                            set wSize to size of thisWindow
                            set absX to item 1 of wPos
                            set absY to item 2 of wPos
                            set absW to item 1 of wSize
                            set absH to item 2 of wSize
                            
                            set relX to absX / screenW
                            set relY to absY / screenH
                            set relW to absW / screenW
                            set relH to absH / screenH
                            
                            set wUrls to {}
                            
                            if appName is "Safari" then
                                set wUrls to my getSafariUrls(i)
                            else if appName is in chromiumVariants then
                                set wUrls to my getChromeUrls(appName, i)
                            else if appName is "Firefox" then
                                try
                                    set rawUrl to value of attribute "AXValue" of combo box 1 of toolbar "Navigation" of thisWindow
                                    if rawUrl starts with "http" then set end of wUrls to rawUrl
                                end try
                            end if
                            
                            set end of mainData of DataCarrier to {procName:appName, winIndex:i, relPos:{relX, relY}, relSize:{relW, relH}, savedUrls:wUrls}
                        end repeat
                    end tell
                end if
            end try
        end repeat
    end tell
    set theFile to POSIX file thePath
    store script DataCarrier in theFile replacing yes
    """#
    let (success, errorMsg) = runAppleScript(script)
    if success { return true }
    else { showScriptError(details: errorMsg); return false }
}

func restoreProfile(name: String) {
    let profileDir = storageFolder.appendingPathComponent(name)
    let dataFile = profileDir.appendingPathComponent("data.scpt")
    
    let script = #"""
    set theFile to POSIX file "\#(dataFile.path)"
    set chromiumVariants to {"Google Chrome", "Brave Browser", "Microsoft Edge", "Arc", "Vivaldi", "Opera"}
    
    -- HANDLERS FOR RESTORE (Isolates Dictionaries)
    on restoreSafari(urlList)
        try
            tell application "Safari"
                set shouldReuse to false
                if (count of windows) = 0 then
                    make new document
                    set shouldReuse to true
                else if (count of windows) = 1 then
                     try
                        if not (exists URL of document 1) or (URL of document 1 is "") then set shouldReuse to true
                     end try
                end if
                
                if not shouldReuse then make new document
                
                if (count of urlList) > 0 then
                    set URL of document 1 to (item 1 of urlList)
                    if (count of urlList) > 1 then
                        repeat with k from 2 to count of urlList
                            tell window 1 to make new tab with properties {URL:(item k of urlList)}
                        end repeat
                    end if
                end if
            end tell
        end try
    end restoreSafari

    on restoreChrome(theApp, urlList)
        try
            set scriptSrc to "on run {urls}" & return & "tell application \"" & theApp & "\"" & return
            set scriptSrc to scriptSrc & "if (count of urls) > 0 then" & return
            set scriptSrc to scriptSrc & "set reuse to false" & return
            set scriptSrc to scriptSrc & "if (count of windows) = 1 and (count of tabs of window 1) = 1 then" & return
            set scriptSrc to scriptSrc & "set u to URL of active tab of window 1" & return
            set scriptSrc to scriptSrc & "if u is \"\" or u starts with \"chrome://\" then set reuse to true" & return
            set scriptSrc to scriptSrc & "end if" & return
            set scriptSrc to scriptSrc & "if not reuse then make new window" & return
            set scriptSrc to scriptSrc & "set URL of active tab of window 1 to (item 1 of urls)" & return
            set scriptSrc to scriptSrc & "if (count of urls) > 1 then" & return
            set scriptSrc to scriptSrc & "repeat with k from 2 to count of urls" & return
            set scriptSrc to scriptSrc & "make new tab at end of tabs of window 1 with properties {URL:(item k of urls)}" & return
            set scriptSrc to scriptSrc & "end repeat" & return
            set scriptSrc to scriptSrc & "end if" & return
            set scriptSrc to scriptSrc & "end if" & return
            set scriptSrc to scriptSrc & "end tell" & return
            set scriptSrc to scriptSrc & "end run"
            run script scriptSrc with parameters {urlList}
        end try
    end restoreChrome
    
    on restoreFirefox(theApp, urlList)
        try
            tell application theApp
                if (count of urlList) > 0 then
                    repeat with k from 1 to count of urlList
                        open location (item k of urlList)
                        delay 0.2
                    end repeat
                end if
            end tell
        end try
    end restoreFirefox

    try
        set loadedScript to load script theFile
        set windowPositions to mainData of loadedScript
        
        tell application "System Events"
            set visible of (every process where visible is true and name is not "__APP_NAME__") to false
        end tell
        delay 0.5
        
        tell application "Finder" to set screenBounds to bounds of window of desktop
        set screenW to item 3 of screenBounds
        set screenH to item 4 of screenBounds
        
        -- MERGED RESTORATION LOOP
        repeat with winRecord in windowPositions
            set appName to procName of winRecord
            set urlList to savedUrls of winRecord
            set rawIndex to winIndex of winRecord
            
            -- Geometry Calcs
            set relPos to relPos of winRecord
            set relSize to relSize of winRecord
            set relX to item 1 of relPos
            set relY to item 2 of relPos
            set relW to item 1 of relSize
            set relH to item 2 of relSize
            
            set targetX to (relX * screenW)
            set targetY to (relY * screenH)
            set targetW to (relW * screenW)
            set targetH to (relH * screenH)
            
            if targetX < 0 then set targetX to 0
            if targetY < 0 then set targetY to 25
            
            set targetPos to {targetX, targetY}
            set targetSize to {targetW, targetH}
            
            try
                -- 1. Activate & Restore Content
                tell application appName to activate
                delay 0.2
                
                if appName is "Safari" then
                      my restoreSafari(urlList)
                      delay 0.5 -- Allow window creation
                else if appName is in chromiumVariants then
                      my restoreChrome(appName, urlList)
                      delay 0.5
                else if appName is "Firefox" then
                      my restoreFirefox(appName, urlList)
                      delay 0.5
                else
                      -- Generic App: Just wait
                      delay 0.2
                end if
                
                -- 2. Restore Geometry
                tell application "System Events" to tell process appName
                    set targetIndex to rawIndex
                    -- For browsers, target the frontmost window (Window 1) as we just created/touched it
                    if (appName is "Safari" or appName is "Firefox" or appName is in chromiumVariants) then set targetIndex to 1
                    
                    repeat 3 times
                        if (count of windows) >= targetIndex then
                            set position of window targetIndex to targetPos
                            set size of window targetIndex to targetSize
                            exit repeat
                        end if
                        delay 0.2
                    end repeat
                end tell
            end try
        end repeat
    end try
    """#
    let (success, errorMsg) = runAppleScript(script)
    if !success { 
        showScriptError(details: errorMsg) 
    } else {
        showSuccess(message: "Restored!")
    }
}

// --- UI SETUP (PRESERVED) ---
let app = NSApplication.shared
app.setActivationPolicy(.regular)

let alert = NSAlert()
alert.messageText = "\(appTitle)"
alert.informativeText = "Instantly save and restore your workspace, Never lose your layout again."
alert.addButton(withTitle: "Restore")
alert.addButton(withTitle: "Save New...")
alert.addButton(withTitle: "Close")
alert.addButton(withTitle: "About")

if alert.buttons.count > 2 {
    alert.buttons[2].keyEquivalent = "\u{1b}"
}

let viewWidth: CGFloat = 450
let viewHeight: CGFloat = 300
let container = NSView(frame: NSRect(x: 0, y: 0, width: viewWidth, height: viewHeight))

let dropdown = NSPopUpButton(frame: NSRect(x: 0, y: 260, width: 300, height: 25))
let revealBtn = NSButton(frame: NSRect(x: 310, y: 260, width: 40, height: 25))
revealBtn.title = "📂"
revealBtn.bezelStyle = .rounded
revealBtn.toolTip = "Reveal Profiles in Finder"

let deleteBtn = NSButton(frame: NSRect(x: 360, y: 260, width: 80, height: 25))
deleteBtn.title = "Delete"; deleteBtn.bezelStyle = .rounded

// --- ADAPTIVE FRAME LOGIC ---
let maxW: CGFloat = 450
let maxH: CGFloat = 250
let imgContainer = NSView(frame: NSRect(x: 0, y: 0, width: maxW, height: maxH)) 
imgContainer.wantsLayer = true
imgContainer.layer?.borderWidth = 1.0
imgContainer.layer?.borderColor = NSColor.clear.cgColor 
imgContainer.layer?.cornerRadius = 6.0

let gap: CGFloat = 4
let imgView = NSImageView(frame: NSRect(x: gap, y: gap, width: maxW - (gap*2), height: maxH - (gap*2)))
imgView.imageScaling = .scaleAxesIndependently 
imgView.imageFrameStyle = .none
imgView.wantsLayer = true
imgView.layer?.cornerRadius = 4.0
imgView.layer?.masksToBounds = true
imgContainer.addSubview(imgView)

// --- HANDLERS ---
func refreshList() {
    let profiles = getSavedProfiles()
    dropdown.removeAllItems()
    dropdown.addItems(withTitles: profiles)
    if profiles.isEmpty { dropdown.addItem(withTitle: "No Profiles Found"); deleteBtn.isEnabled = false }
    else { deleteBtn.isEnabled = true }
    updateImage()
}

func updateImage() {
    guard let selected = dropdown.titleOfSelectedItem else { 
        imgView.image = nil
        imgContainer.layer?.borderColor = NSColor.clear.cgColor 
        return 
    }
    let profileDir = storageFolder.appendingPathComponent(selected)
    let imgPath = profileDir.appendingPathComponent("preview.jpg")
    
    if fileManager.fileExists(atPath: imgPath.path), let img = NSImage(contentsOf: imgPath) {
        imgView.image = img
        imgContainer.layer?.borderColor = NSColor.tertiaryLabelColor.cgColor
        let imgSize = img.size
        let ratio = imgSize.width / imgSize.height
        var targetW = maxW
        var targetH = maxW / ratio
        if targetH > maxH {
            targetH = maxH
            targetW = maxH * ratio
        }
        imgContainer.frame = NSRect(x: (viewWidth - targetW) / 2, y: (maxH - targetH) / 2, width: targetW, height: targetH)
        imgView.frame = NSRect(x: gap, y: gap, width: targetW - (gap*2), height: targetH - (gap*2))
    } else { 
        imgView.image = nil
        imgContainer.layer?.borderColor = NSColor.clear.cgColor 
    }
}

class UIHandler: NSObject {
    @objc func dropdownChanged(_ sender: Any) { updateImage() }
    @objc func deleteClicked(_ sender: Any) {
        guard let name = dropdown.titleOfSelectedItem, name != "No Profiles Found" else { return }
        let confirm = NSAlert()
        confirm.messageText = "Delete '\(name)'?"; confirm.addButton(withTitle: "Delete"); confirm.addButton(withTitle: "Cancel")
        if confirm.runModal() == .alertFirstButtonReturn { if deleteProfile(name: name) { refreshList() } }
    }
    @objc func revealClicked(_ sender: Any) {
        NSWorkspace.shared.activateFileViewerSelecting([storageFolder])
    }
}
let handler = UIHandler()
dropdown.target = handler; dropdown.action = #selector(UIHandler.dropdownChanged(_:))
deleteBtn.target = handler; deleteBtn.action = #selector(UIHandler.deleteClicked(_:))
revealBtn.target = handler; revealBtn.action = #selector(UIHandler.revealClicked(_:))

refreshList()

container.addSubview(dropdown)
container.addSubview(revealBtn)
container.addSubview(deleteBtn)
container.addSubview(imgContainer)

alert.accessoryView = container

app.finishLaunching()
app.activate(ignoringOtherApps: true)

checkFirstRun()
checkForUpdates()

// --- LOOP ---
while true {
    let response = alert.runModal()
    if response == .alertFirstButtonReturn { // Restore
        if let name = dropdown.titleOfSelectedItem, name != "No Profiles Found" { 
            restoreProfile(name: name)
            Thread.sleep(forTimeInterval: 1.0)
            exit(0)
        }
    } else if response == .alertSecondButtonReturn { // Save
        while true {
            let nameAlert = NSAlert()
            nameAlert.messageText = "Name Profile"; nameAlert.addButton(withTitle: "Save"); nameAlert.addButton(withTitle: "Cancel")
            let input = NSTextField(frame: NSRect(x: 0, y: 0, width: 200, height: 24))
            nameAlert.accessoryView = input
            if nameAlert.runModal() == .alertFirstButtonReturn {
                let newName = input.stringValue.trimmingCharacters(in: .whitespacesAndNewlines)
                if newName.isEmpty { continue }
                let existing = getSavedProfiles().map { $0.lowercased() }
                
                if existing.contains(newName.lowercased()) {
                    let dup = NSAlert()
                    dup.messageText = "Replace '\(newName)'?"
                    dup.informativeText = "This will overwrite the existing profile."
                    dup.addButton(withTitle: "Replace")
                    dup.addButton(withTitle: "Cancel")
                    if dup.runModal() == .alertSecondButtonReturn { continue }
                }
                
                if saveProfile(name: newName) { 
                    showSuccess(message: "Saved!")
                    refreshList() 
                    break
                } else { break }
            } else { break }
        }
    } else if response == .alertThirdButtonReturn { exit(0) }
    else { showAbout() }
}
EOF

# Inject Version and Name into Swift Source
sed -i '' "s/__APP_VERSION__/$APP_VERSION/g" SnapBack.swift
sed -i '' "s/__APP_NAME__/$APP_NAME/g" SnapBack.swift

printf "   ${NEON_GREEN}✔ Core Logic Integrated.${NC}\n\n"

# --- STEP 5: COMPILATION & CLEANUP ---
show_step 5 "Compiling Binary Executable..."

(
swiftc SnapBack.swift -o "$APP_NAME.app/Contents/MacOS/SnapBack"
) &
show_spinner $!

if [ $? -eq 0 ]; then
    printf "   ${NEON_GREEN}✔ Compilation Complete.${NC}\n\n"
    
    show_step 6 "Finalizing and Signing..."
    
    # Ensure binary is executable
    chmod +x "$APP_NAME.app/Contents/MacOS/SnapBack"
    chmod +x "$APP_NAME.app/Contents/Resources"
    
    # --- Ad-Hoc Signing ---
    printf "   ${TXT_GREY}Applying local signature...${NC} "
    codesign --force --deep -s - --requirements '=designated => identifier "com.aoh.snapback"' "$APP_NAME.app" &>/dev/null
    printf "${NEON_GREEN}Secure.${NC}\n"
    
    # --- STEP 6: DIRECT AUTO-INSTALL OR FALLBACK ---
    show_step 6 "Installing Snap Back to /Applications..."
    
    DEST="/Applications/$APP_NAME.app"
    pkill -x "$APP_NAME" >/dev/null 2>&1 || true
    INSTALLED=false

    if [ "$FORCE_MODAL" = "1" ] || [ "$1" = "--modal" ] || [ "$1" = "--gui" ] || [ "$2" = "--modal" ]; then
        INSTALLED=false
    elif [ -w "/Applications" ]; then
        rm -rf "$DEST" 2>/dev/null || true
        if cp -R "$APP_NAME.app" "$DEST" 2>/dev/null; then
            INSTALLED=true
        fi
    fi

    if [ "$INSTALLED" = "true" ]; then
        xattr -dr com.apple.quarantine "$DEST" >/dev/null 2>&1 || true
        printf "   ${NEON_GREEN}✔ Snap Back installed to /Applications.${NC}\n\n"
        print_line
        show_success_art
        print_line
        printf "\n"
        printf "   ${NEON_CYAN}DEPLOYMENT COMPLETE:${NC} $APP_NAME (v$APP_VERSION) is now running in your menu bar.\n"
        open "$DEST"
        afplay /System/Library/Sounds/Glass.aiff 2>/dev/null || true
        popd > /dev/null
        rm -rf "$BUILD_DIR"
        exit 0
    fi

    # Fallback to Drag Window
    cat > Installer.swift <<'EOF'
import Cocoa
import QuartzCore

class DraggableIconView: NSImageView, NSDraggingSource {
    var fileURL: URL?

    func draggingSession(_ session: NSDraggingSession, sourceOperationMaskFor context: NSDraggingContext) -> NSDragOperation {
        return .copy
    }

    override func mouseDown(with event: NSEvent) {
        guard let url = fileURL, let originalImg = self.image else { return }
        let pasteboardItem = NSPasteboardItem()
        pasteboardItem.setString(url.path, forType: .fileURL)
        let draggingItem = NSDraggingItem(pasteboardWriter: pasteboardItem)
        let dragRect = self.bounds
        
        let dragImg = NSImage(size: bounds.size)
        dragImg.lockFocus()
        if let ctx = NSGraphicsContext.current {
            ctx.imageInterpolation = .high
        }
        originalImg.draw(in: bounds)
        dragImg.unlockFocus()

        draggingItem.setDraggingFrame(dragRect, contents: dragImg)
        beginDraggingSession(with: [draggingItem], event: event, source: self)
    }
}

class DropTargetView: NSImageView {
    override func awakeFromNib() { registerForDraggedTypes([.fileURL]) }
    override init(frame frameRect: NSRect) { super.init(frame: frameRect); registerForDraggedTypes([.fileURL]) }
    required init?(coder: NSCoder) { super.init(coder: coder); registerForDraggedTypes([.fileURL]) }
    override func draggingEntered(_ sender: NSDraggingInfo) -> NSDragOperation { .copy }
    override func performDragOperation(_ sender: NSDraggingInfo) -> Bool {
        guard let board = sender.draggingPasteboard.propertyList(forType: .fileURL) as? String else { return false }
        let sourceURL = URL(fileURLWithPath: board)
        let appName = sourceURL.lastPathComponent
        let destURL = URL(fileURLWithPath: "/Applications").appendingPathComponent(appName)
        do {
            if FileManager.default.fileExists(atPath: destURL.path) {
                try FileManager.default.removeItem(at: destURL)
            }
            try FileManager.default.copyItem(at: sourceURL, to: destURL)
            let clean = Process()
            clean.launchPath = "/usr/bin/xattr"
            clean.arguments = ["-dr", "com.apple.quarantine", destURL.path]
            clean.standardOutput = Pipe(); clean.standardError = Pipe()
            try? clean.run(); clean.waitUntilExit()
            
            NSSound(named: "Glass")?.play()
            let alert = NSAlert()
            alert.messageText = "Installation Successful"
            alert.informativeText = "Snap Back has been installed to Applications."
            alert.addButton(withTitle: "Launch App")
            alert.addButton(withTitle: "Quit")
            if alert.runModal() == .alertFirstButtonReturn {
                NSWorkspace.shared.open(destURL)
            }
            NSApplication.shared.terminate(nil)
        } catch {
            let script = "do shell script \"cp -R '\(sourceURL.path)' /Applications/\" with administrator privileges"
            var errorDict: NSDictionary?
            if let appleScript = NSAppleScript(source: script) {
                appleScript.executeAndReturnError(&errorDict)
                if errorDict == nil {
                    NSWorkspace.shared.open(destURL)
                    NSApplication.shared.terminate(nil)
                }
            }
        }
        return true
    }
}

class InstallerWindow: NSWindow {
    override init(contentRect: NSRect, styleMask style: NSWindow.StyleMask, backing backingStoreType: NSWindow.BackingStoreType, defer flag: Bool) {
        super.init(contentRect: contentRect, styleMask: [.titled, .closable, .miniaturizable], backing: backingStoreType, defer: flag)
        self.title = "Install __APP_NAME__"
        self.center()
    }
}

let app = NSApplication.shared
app.setActivationPolicy(.regular)

let winW: CGFloat = 620, winH: CGFloat = 410
let window = InstallerWindow(contentRect: NSRect(x: 0, y: 0, width: winW, height: winH), styleMask: [.titled, .closable], backing: .buffered, defer: false)

let bg = NSVisualEffectView(frame: NSRect(x: 0, y: 0, width: winW, height: winH))
bg.material = .windowBackground; bg.state = .active
window.contentView = bg

let titleLabel = NSTextField(labelWithString: "Install __APP_NAME__")
titleLabel.frame = NSRect(x: 0, y: winH - 65, width: winW, height: 30)
titleLabel.alignment = .center
titleLabel.font = NSFont.systemFont(ofSize: 22, weight: .bold)
bg.addSubview(titleLabel)

let subLabel = NSTextField(labelWithString: "Drag __APP_NAME__ to Applications or click Instant Install below")
subLabel.frame = NSRect(x: 0, y: winH - 90, width: winW, height: 20)
subLabel.alignment = .center
subLabel.font = NSFont.systemFont(ofSize: 13)
subLabel.textColor = .secondaryLabelColor
bg.addSubview(subLabel)

let iconSize: CGFloat = 128
let midY: CGFloat = 145
let appIconView = DraggableIconView(frame: NSRect(x: 90, y: midY, width: iconSize, height: iconSize))
let appPath = Bundle.main.bundlePath.replacingOccurrences(of: "Install __APP_NAME__.app", with: "__APP_NAME__.app")
if let image = NSImage(contentsOfFile: appPath + "/Contents/Resources/AppIcon.icns") {
    appIconView.image = image
}
appIconView.fileURL = URL(fileURLWithPath: appPath)
appIconView.imageScaling = .scaleProportionallyUpOrDown
bg.addSubview(appIconView)

let appLabel = NSTextField(labelWithString: "__APP_NAME__")
appLabel.frame = NSRect(x: 90, y: midY - 26, width: iconSize, height: 18)
appLabel.alignment = .center
appLabel.font = NSFont.systemFont(ofSize: 12, weight: .medium)
bg.addSubview(appLabel)

// Gradient Arrow View (Fading Stem Arrow)
class GradientArrowView: NSView {
    override func draw(_ dirtyRect: NSRect) {
        super.draw(dirtyRect)
        let w = bounds.width
        let h = bounds.height
        let midY = h / 2.0
        
        let stemH: CGFloat = 10
        let headH: CGFloat = 26
        let headW: CGFloat = 20
        let stemR = w - headW
        
        let path = NSBezierPath()
        path.move(to: NSPoint(x: 0, y: midY - stemH/2))
        path.line(to: NSPoint(x: stemR, y: midY - stemH/2))
        path.line(to: NSPoint(x: stemR, y: midY - headH/2))
        path.line(to: NSPoint(x: w, y: midY))
        path.line(to: NSPoint(x: stemR, y: midY + headH/2))
        path.line(to: NSPoint(x: stemR, y: midY + stemH/2))
        path.line(to: NSPoint(x: 0, y: midY + stemH/2))
        path.close()
        
        let startColor = NSColor.labelColor.withAlphaComponent(0.0)
        let endColor = NSColor.labelColor.withAlphaComponent(0.50)
        let gradient = NSGradient(starting: startColor, ending: endColor)
        gradient?.draw(in: path, angle: 0)
    }
}

let arrowView = GradientArrowView(frame: NSRect(x: (winW - 70)/2, y: midY + iconSize/2 - 18, width: 70, height: 36))
bg.addSubview(arrowView)

let folderIconView = DropTargetView(frame: NSRect(x: winW - 90 - iconSize, y: midY, width: iconSize, height: iconSize))
folderIconView.image = NSWorkspace.shared.icon(forFile: "/Applications")
folderIconView.imageScaling = .scaleProportionallyUpOrDown
bg.addSubview(folderIconView)

let appsLabel = NSTextField(labelWithString: "Applications")
appsLabel.frame = NSRect(x: winW - 90 - iconSize, y: midY - 26, width: iconSize, height: 18)
appsLabel.alignment = .center
appsLabel.font = NSFont.systemFont(ofSize: 12, weight: .medium)
bg.addSubview(appsLabel)



class InstallerActionTarget: NSObject {
    @objc static func oneClickInstall() {
        let sourceURL = URL(fileURLWithPath: Bundle.main.bundlePath.replacingOccurrences(of: "Install __APP_NAME__.app", with: "__APP_NAME__.app"))
        let destURL = URL(fileURLWithPath: "/Applications/__APP_NAME__.app")
        do {
            if FileManager.default.fileExists(atPath: destURL.path) {
                try FileManager.default.removeItem(at: destURL)
            }
            try FileManager.default.copyItem(at: sourceURL, to: destURL)
            let clean = Process()
            clean.launchPath = "/usr/bin/xattr"
            clean.arguments = ["-dr", "com.apple.quarantine", destURL.path]
            clean.standardOutput = Pipe(); clean.standardError = Pipe()
            try? clean.run(); clean.waitUntilExit()
            
            NSSound(named: "Glass")?.play()
            let alert = NSAlert()
            alert.messageText = "Installation Successful"
            alert.informativeText = "Snap Back has been installed to Applications."
            alert.addButton(withTitle: "Launch App")
            alert.addButton(withTitle: "Quit")
            if alert.runModal() == .alertFirstButtonReturn {
                NSWorkspace.shared.open(destURL)
            }
            NSApplication.shared.terminate(nil)
        } catch {
            let sanitizedPath = sourceURL.path.replacingOccurrences(of: "'", with: "'\\''")
            let script = "do shell script \"cp -R '\(sanitizedPath)' /Applications/\" with administrator privileges"
            var errorDict: NSDictionary?
            if let appleScript = NSAppleScript(source: script) {
                appleScript.executeAndReturnError(&errorDict)
                if errorDict == nil {
                    NSWorkspace.shared.open(destURL)
                    NSApplication.shared.terminate(nil)
                }
            }
        }
    }
}

class AnimatedInstallButton: NSButton {
    private var trackingArea: NSTrackingArea?
    private var isHovered = false { didSet { animateState() } }
    private var isPressed = false { didSet { animateState() } }

    override init(frame frameRect: NSRect) {
        super.init(frame: frameRect)
        setup()
    }
    required init?(coder: NSCoder) {
        super.init(coder: coder)
        setup()
    }
    private func setup() {
        wantsLayer = true
        isBordered = false
        focusRingType = .none
        layer?.cornerRadius = 10
        layer?.backgroundColor = NSColor(red: 0.18, green: 0.18, blue: 0.20, alpha: 1.0).cgColor
        contentTintColor = .white
    }
    override func updateTrackingAreas() {
        super.updateTrackingAreas()
        if let area = trackingArea { removeTrackingArea(area) }
        let area = NSTrackingArea(rect: bounds, options: [.mouseEnteredAndExited, .activeAlways], owner: self, userInfo: nil)
        addTrackingArea(area)
        trackingArea = area
    }
    override func mouseEntered(with event: NSEvent) {
        isHovered = true
        NSCursor.pointingHand.set()
    }
    override func mouseExited(with event: NSEvent) {
        isHovered = false
        NSCursor.arrow.set()
    }
    override func mouseDown(with event: NSEvent) {
        isPressed = true
        super.mouseDown(with: event)
    }
    override func mouseUp(with event: NSEvent) {
        isPressed = false
        super.mouseUp(with: event)
    }
    private func animateState() {
        NSAnimationContext.runAnimationGroup { ctx in
            ctx.duration = 0.15
            ctx.allowsImplicitAnimation = true
            if isPressed {
                layer?.backgroundColor = NSColor(red: 0.14, green: 0.14, blue: 0.16, alpha: 1.0).cgColor
                layer?.transform = CATransform3DMakeScale(0.95, 0.95, 1.0)
            } else if isHovered {
                layer?.backgroundColor = NSColor(red: 0.28, green: 0.28, blue: 0.30, alpha: 1.0).cgColor
                layer?.transform = CATransform3DMakeScale(1.04, 1.04, 1.0)
                layer?.shadowColor = NSColor.black.cgColor
                layer?.shadowRadius = 8
                layer?.shadowOpacity = 0.35
                layer?.shadowOffset = .zero
            } else {
                layer?.backgroundColor = NSColor(red: 0.18, green: 0.18, blue: 0.20, alpha: 1.0).cgColor
                layer?.transform = CATransform3DIdentity
                layer?.shadowOpacity = 0
            }
        }
    }
}

let oneClickBtn = AnimatedInstallButton(frame: NSRect(x: (winW - 270)/2, y: 20, width: 270, height: 42))
oneClickBtn.title = "⚡ One-Click Install to /Applications"
oneClickBtn.font = NSFont.systemFont(ofSize: 13, weight: .bold)
oneClickBtn.target = InstallerActionTarget.self
oneClickBtn.action = #selector(InstallerActionTarget.oneClickInstall)
window.contentView?.addSubview(oneClickBtn)

window.makeKeyAndOrderFront(nil)
app.activate(ignoringOtherApps: true)
app.run()
EOF

    sed -i '' "s/__APP_NAME__/$APP_NAME/g" Installer.swift
    sed -i '' "s/__APP_VERSION__/$APP_VERSION/g" Installer.swift

    mkdir -p "Install $APP_NAME.app/Contents/MacOS"
    mkdir -p "Install $APP_NAME.app/Contents/Resources"
    cp "$APP_NAME.app/Contents/Resources/AppIcon.icns" "Install $APP_NAME.app/Contents/Resources/"
    swiftc Installer.swift -o "Install $APP_NAME.app/Contents/MacOS/Install $APP_NAME"
    chmod +x "Install $APP_NAME.app/Contents/MacOS/Install $APP_NAME"

    cat > "Install $APP_NAME.app/Contents/Info.plist" <<EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleExecutable</key><string>Install $APP_NAME</string>
    <key>CFBundleIdentifier</key><string>com.aoh.snapback.installer</string>
    <key>CFBundleName</key><string>Install $APP_NAME</string>
    <key>CFBundleIconFile</key><string>AppIcon</string>
    <key>CFBundlePackageType</key><string>APPL</string>
</dict>
</plist>
EOF

    INSTALLER_DIR=$(mktemp -d)
    mv "$APP_NAME.app" "$INSTALLER_DIR/"
    mv "Install $APP_NAME.app" "$INSTALLER_DIR/"
    popd > /dev/null
    rm -rf "$BUILD_DIR"

    open "$INSTALLER_DIR/Install $APP_NAME.app"
    afplay /System/Library/Sounds/Glass.aiff 2>/dev/null || true
else
    show_failure_art
    printf "\n"
    printf "${ALERT_RED}CRITICAL ERROR:${NC} Compilation protocol failed.\n"
    printf "${TXT_GREY}DEBUGGING:${NC} Review output log above for trace data.\n"
    popd > /dev/null
    exit 1
fi
