//go:build windows && !bindings

// NB the !bindings tag: Wails generates the TypeScript bindings by BUILDING AND
// RUNNING this binary. With the guard active, a normal OpsLog already running on
// the dev machine holds the mutex, the generator's process exits instantly, and
// no bindings are produced. Excluding the guard from that build keeps generation
// working while shipping builds still get it.

package main

import (
	"errors"
	"unsafe"

	"golang.org/x/sys/windows"
)

// singleInstanceName is a per-session named mutex. The Windows kernel releases
// it automatically when the owning process dies (even on a crash), so a
// lingering/zombie OpsLog can't permanently block future launches — killing it
// frees the name at once. Session-local (no "Global\\") = one instance per
// logged-in desktop, which is what we want.
const singleInstanceName = "OpsLog-SingleInstance-Mutex"

// acquireSingleInstance creates the named mutex. Returns ok=false when another
// OpsLog already holds it (this instance should exit); on the way out it brings
// the existing window to the front so a double-click just refocuses OpsLog
// instead of spawning a duplicate that fights over the CAT / antenna.
//
// The mutex handle is deliberately never closed — it must live for the whole
// process lifetime; the OS reclaims it on exit.
func acquireSingleInstance() (ok bool) {
	namePtr, err := windows.UTF16PtrFromString(singleInstanceName)
	if err != nil {
		return true // never block launch on an unexpected error
	}
	kernel32 := windows.NewLazySystemDLL("kernel32.dll")
	createMutex := kernel32.NewProc("CreateMutexW")
	// CreateMutexW(lpSecurityAttributes=NULL, bInitialOwner=FALSE, lpName)
	h, _, callErr := createMutex.Call(0, 0, uintptr(unsafe.Pointer(namePtr)))
	if h == 0 {
		return true // couldn't create the mutex → don't block the app
	}
	if errors.Is(callErr, windows.ERROR_ALREADY_EXISTS) {
		focusExistingWindow()
		return false
	}
	return true
}

// focusExistingWindow finds the running OpsLog window by its title and restores
// + foregrounds it. Best-effort; failures are silently ignored.
func focusExistingWindow() {
	user32 := windows.NewLazySystemDLL("user32.dll")
	findWindow := user32.NewProc("FindWindowW")
	setForeground := user32.NewProc("SetForegroundWindow")
	showWindow := user32.NewProc("ShowWindow")

	title, err := windows.UTF16PtrFromString("OpsLog")
	if err != nil {
		return
	}
	hwnd, _, _ := findWindow.Call(0, uintptr(unsafe.Pointer(title)))
	if hwnd == 0 {
		return
	}
	const swRestore = 9 // SW_RESTORE — un-minimise if needed
	showWindow.Call(hwnd, swRestore)
	setForeground.Call(hwnd)
}
