import AppKit
import CoreGraphics
import Foundation

struct Bounds: Encodable {
  let x: Int
  let y: Int
  let width: Int
  let height: Int
}

struct WindowInfo: Encodable {
  let id: Int
  let owner: String
  let name: String
  let layer: Int
  let bounds: Bounds
  let area: Int
}

struct Response: Encodable {
  let count: Int
  let selected: WindowInfo?
  let windows: [WindowInfo]?
}

func value(for flag: String) -> String? {
  guard let idx = CommandLine.arguments.firstIndex(of: flag) else {
    return nil
  }
  let next = CommandLine.arguments.index(after: idx)
  guard next < CommandLine.arguments.endIndex else {
    return nil
  }
  return CommandLine.arguments[next]
}

let frontmostFlag = CommandLine.arguments.contains("--frontmost")
let explicitApp = value(for: "--app")
let frontmostName = frontmostFlag ? NSWorkspace.shared.frontmostApplication?.localizedName : nil
if frontmostFlag && frontmostName == nil {
  fputs("{\"count\":0}\n", stderr)
  exit(1)
}
let appFilter = (explicitApp ?? frontmostName)?.lowercased()
let nameFilter = value(for: "--window-name")?.lowercased()
let includeList = CommandLine.arguments.contains("--list")

let options: CGWindowListOption = [.optionOnScreenOnly, .excludeDesktopElements]
guard let raw = CGWindowListCopyWindowInfo(options, kCGNullWindowID) as? [[String: Any]] else {
  fputs("{\"count\":0}\n", stderr)
  exit(1)
}

var exactMatches: [WindowInfo] = []
var partialMatches: [WindowInfo] = []
exactMatches.reserveCapacity(raw.count)
partialMatches.reserveCapacity(raw.count)

for entry in raw {
  guard let owner = entry[kCGWindowOwnerName as String] as? String else { continue }
  let ownerLower = owner.lowercased()
  if let appFilter, !ownerLower.contains(appFilter) { continue }

  let name = (entry[kCGWindowName as String] as? String) ?? ""
  if let nameFilter, !name.lowercased().contains(nameFilter) { continue }

  guard let number = entry[kCGWindowNumber as String] as? Int else { continue }
  let layer = (entry[kCGWindowLayer as String] as? Int) ?? 0

  guard let boundsDict = entry[kCGWindowBounds as String] as? [String: Any] else { continue }
  let x = Int((boundsDict["X"] as? Double) ?? 0)
  let y = Int((boundsDict["Y"] as? Double) ?? 0)
  let width = Int((boundsDict["Width"] as? Double) ?? 0)
  let height = Int((boundsDict["Height"] as? Double) ?? 0)
  if width <= 0 || height <= 0 { continue }

  let bounds = Bounds(x: x, y: y, width: width, height: height)
  let area = width * height
  let info = WindowInfo(id: number, owner: owner, name: name, layer: layer, bounds: bounds, area: area)
  if let appFilter, ownerLower == appFilter {
    exactMatches.append(info)
  } else {
    partialMatches.append(info)
  }
}

let windows: [WindowInfo]
if appFilter != nil && !exactMatches.isEmpty {
  windows = exactMatches
} else {
  windows = partialMatches
}

func rank(_ window: WindowInfo) -> (Int, Int) {
  // Prefer normal-layer windows, then larger area.
  let layerScore = window.layer == 0 ? 0 : 1
  return (layerScore, -window.area)
}

let ordered: [WindowInfo]
if frontmostFlag {
  ordered = windows
} else {
  ordered = windows.sorted { rank($0) < rank($1) }
}
let selected = ordered.first

let list: [WindowInfo]?
if includeList {
  list = ordered
} else {
  list = nil
}

let response = Response(count: windows.count, selected: selected, windows: list)
let encoder = JSONEncoder()
encoder.outputFormatting = [.sortedKeys]

if let data = try? encoder.encode(response),
   let json = String(data: data, encoding: .utf8) {
  print(json)
} else {
  fputs("{\"count\":\(windows.count)}\n", stderr)
  exit(1)
}
