import AppKit
import ApplicationServices
import CryptoKit
import Darwin
import Foundation

func validDrag(start: CGPoint, end: CGPoint, displays: [CGRect]) -> Bool {
    let values = [start.x, start.y, end.x, end.y]
    guard values.allSatisfy(\.isFinite), start != end, !displays.isEmpty else { return false }
    return displays.contains(where: { $0.contains(start) }) && displays.contains(where: { $0.contains(end) })
}

struct ReceiverIdentity: Equatable {
    let pid: Int32
    let windowID: UInt32
}

struct WindowSnapshot {
    let identity: ReceiverIdentity
    let layer: Int
    let bounds: CGRect
    let alpha: Double
    let onScreen: Bool
}

struct SourceIdentity: Equatable {
    let byteCount: Int64
    let digest: String
    let device: UInt64
    let inode: UInt64

    init(byteCount: Int64, digest: String, device: UInt64 = 0, inode: UInt64 = 0) {
        self.byteCount = byteCount
        self.digest = digest
        self.device = device
        self.inode = inode
    }
}

func validRect(_ rect: CGRect) -> Bool {
    [rect.origin.x, rect.origin.y, rect.size.width, rect.size.height].allSatisfy(\.isFinite)
        && rect.size.width > 0 && rect.size.height > 0
}

func topReceiver(at point: CGPoint, expectedPID: Int32, windows: [WindowSnapshot]) -> ReceiverIdentity? {
    guard expectedPID > 0, !windows.isEmpty,
          windows.allSatisfy({ snapshot in
              snapshot.identity.pid > 0 && snapshot.identity.windowID > 0
                  && snapshot.alpha.isFinite && snapshot.alpha >= 0 && snapshot.alpha <= 1
                  && validRect(snapshot.bounds)
          }) else { return nil }
    guard let top = windows.first(where: { $0.onScreen && $0.alpha > 0 && $0.bounds.contains(point) }),
          top.layer == 0, top.identity.pid == expectedPID else { return nil }
    return top.identity
}

func receiverStable(preflight: ReceiverIdentity, beforeMouseUp: ReceiverIdentity?) -> Bool {
    preflight == beforeMouseUp
}

func requiredDragFlags() -> CGEventFlags { [.maskAlternate] }

func validQASourceScope(
    rootComponents: [String],
    sourceComponents: [String],
    rootIsDirectory: Bool,
    rootIsSymlink: Bool,
    sourceExists: Bool,
    sourceIsRegular: Bool,
    sourceIsSymlink: Bool,
    sourceSize: Int64,
    maximumSize: Int64
) -> Bool {
    guard !rootComponents.isEmpty, sourceComponents.count > rootComponents.count,
          Array(sourceComponents.prefix(rootComponents.count)) == rootComponents,
          rootIsDirectory, !rootIsSymlink, sourceExists, sourceIsRegular, !sourceIsSymlink,
          sourceSize >= 0, maximumSize > 0, sourceSize <= maximumSize else { return false }
    return true
}

func sourcePreserved(before: SourceIdentity, after: SourceIdentity?) -> Bool { before == after }

enum SourceCleanupResult: Equatable {
    case preserved
    case recovered
    case failed
}

func sourceCleanupResult(
    before: SourceIdentity,
    after: SourceIdentity?,
    restore: () -> Bool
) -> SourceCleanupResult {
    if sourcePreserved(before: before, after: after) { return .preserved }
    return restore() ? .recovered : .failed
}

func makeCopyMouseEvent(source: CGEventSource?, type: CGEventType, point: CGPoint) -> CGEvent? {
    guard let event = CGEvent(
        mouseEventSource: source,
        mouseType: type,
        mouseCursorPosition: point,
        mouseButton: .left
    ) else { return nil }
    event.flags = requiredDragFlags()
    return event
}

func runSafeCancel(
    escapeDown: () -> Bool,
    escapeUp: () -> Bool,
    mouseUp: () -> Bool
) -> Bool {
    let down = escapeDown()
    let up = escapeUp()
    let released = mouseUp()
    return down && up && released
}

struct CleanupState {
    var mouseDownPosted = false
    var mouseUpPosted = false
    var cleanupAttempted = false
    var cleanupSucceeded = false

    mutating func failAfterMouseDown(cleanup: () -> Bool) -> Int32 {
        guard mouseDownPosted && !mouseUpPosted else { return 70 }
        cleanupAttempted = true
        cleanupSucceeded = cleanup()
        if cleanupSucceeded { mouseUpPosted = true }
        return cleanupSucceeded ? 70 : 71
    }
}

func selfTest() -> Int32 {
    let bounds = [CGRect(x: 0, y: 0, width: 1920, height: 1080)]
    let destination = CGPoint(x: 500, y: 500)
    let candidate = ReceiverIdentity(pid: 42, windowID: 7)
    let candidateWindow = WindowSnapshot(identity: candidate, layer: 0, bounds: CGRect(x: 400, y: 400, width: 300, height: 300), alpha: 1, onScreen: true)
    let otherWindow = WindowSnapshot(identity: ReceiverIdentity(pid: 99, windowID: 8), layer: 0, bounds: candidateWindow.bounds, alpha: 1, onScreen: true)
    let nonNormalWindow = WindowSnapshot(identity: ReceiverIdentity(pid: 42, windowID: 9), layer: 1, bounds: candidateWindow.bounds, alpha: 1, onScreen: true)
    let invalidWindow = WindowSnapshot(identity: candidate, layer: 0, bounds: .zero, alpha: 1, onScreen: true)
    let exactReceiver = topReceiver(at: destination, expectedPID: 42, windows: [candidateWindow])
    let rootComponents = ["QA_ROOT"]
    let sourceComponents = ["QA_ROOT", "SOURCE"]
    let validSource = validQASourceScope(
        rootComponents: rootComponents,
        sourceComponents: sourceComponents,
        rootIsDirectory: true,
        rootIsSymlink: false,
        sourceExists: true,
        sourceIsRegular: true,
        sourceIsSymlink: false,
        sourceSize: 1024,
        maximumSize: 16 * 1024 * 1024
    )
    let sourceBefore = SourceIdentity(byteCount: 1024, digest: "FIXTURE_DIGEST")
    let builtCopyEvents = [CGEventType.leftMouseDown, .leftMouseDragged, .leftMouseUp].compactMap {
        makeCopyMouseEvent(source: nil, type: $0, point: CGPoint(x: 1, y: 1))
    }
    var cancelCalls: [String] = []
    let cancelComplete = runSafeCancel(
        escapeDown: { cancelCalls.append("escape-down"); return true },
        escapeUp: { cancelCalls.append("escape-up"); return true },
        mouseUp: { cancelCalls.append("mouse-up"); return true }
    )
    var failedCancelCalls: [String] = []
    let cancelIncomplete = runSafeCancel(
        escapeDown: { failedCancelCalls.append("escape-down"); return false },
        escapeUp: { failedCancelCalls.append("escape-up"); return true },
        mouseUp: { failedCancelCalls.append("mouse-up"); return false }
    )
    guard validDrag(start: CGPoint(x: 1, y: 1), end: CGPoint(x: 2, y: 2), displays: bounds),
          !validDrag(start: CGPoint(x: CGFloat.nan, y: 1), end: CGPoint(x: 2, y: 2), displays: bounds),
          !validDrag(start: CGPoint(x: 1, y: 1), end: CGPoint(x: CGFloat.infinity, y: 2), displays: bounds),
          !validDrag(start: CGPoint(x: 1, y: 1), end: CGPoint(x: 1, y: 1), displays: bounds),
          !validDrag(start: CGPoint(x: -1, y: 1), end: CGPoint(x: 2, y: 2), displays: bounds),
          exactReceiver == candidate,
          topReceiver(at: destination, expectedPID: 42, windows: [otherWindow, candidateWindow]) == nil,
          topReceiver(at: destination, expectedPID: 42, windows: []) == nil,
          topReceiver(at: destination, expectedPID: 42, windows: [invalidWindow]) == nil,
          topReceiver(at: destination, expectedPID: 42, windows: [nonNormalWindow, candidateWindow]) == nil,
          topReceiver(at: destination, expectedPID: 7, windows: [candidateWindow]) == nil,
          receiverStable(preflight: candidate, beforeMouseUp: candidate),
          !receiverStable(preflight: candidate, beforeMouseUp: ReceiverIdentity(pid: 42, windowID: 10)),
          requiredDragFlags().contains(.maskAlternate),
          builtCopyEvents.count == 3,
          builtCopyEvents.allSatisfy({ $0.flags.contains(.maskAlternate) }),
          validSource,
          !validQASourceScope(rootComponents: rootComponents, sourceComponents: ["OUTSIDE", "SOURCE"], rootIsDirectory: true, rootIsSymlink: false, sourceExists: true, sourceIsRegular: true, sourceIsSymlink: false, sourceSize: 1, maximumSize: 16),
          !validQASourceScope(rootComponents: rootComponents, sourceComponents: sourceComponents, rootIsDirectory: true, rootIsSymlink: false, sourceExists: true, sourceIsRegular: true, sourceIsSymlink: true, sourceSize: 1, maximumSize: 16),
          !validQASourceScope(rootComponents: rootComponents, sourceComponents: sourceComponents, rootIsDirectory: true, rootIsSymlink: false, sourceExists: false, sourceIsRegular: true, sourceIsSymlink: false, sourceSize: 1, maximumSize: 16),
          !validQASourceScope(rootComponents: rootComponents, sourceComponents: sourceComponents, rootIsDirectory: true, rootIsSymlink: false, sourceExists: true, sourceIsRegular: false, sourceIsSymlink: false, sourceSize: 1, maximumSize: 16),
          !validQASourceScope(rootComponents: rootComponents, sourceComponents: sourceComponents, rootIsDirectory: true, rootIsSymlink: false, sourceExists: true, sourceIsRegular: true, sourceIsSymlink: false, sourceSize: 17, maximumSize: 16),
          sourcePreserved(before: sourceBefore, after: sourceBefore),
          !sourcePreserved(before: sourceBefore, after: nil),
          !sourcePreserved(before: sourceBefore, after: SourceIdentity(byteCount: 1024, digest: "CHANGED")),
          sourceCleanupResult(before: sourceBefore, after: sourceBefore, restore: { false }) == .preserved,
          sourceCleanupResult(before: sourceBefore, after: nil, restore: { true }) == .recovered,
          sourceCleanupResult(before: sourceBefore, after: nil, restore: { false }) == .failed,
          cancelComplete, cancelCalls == ["escape-down", "escape-up", "mouse-up"],
          !cancelIncomplete, failedCancelCalls == ["escape-down", "escape-up", "mouse-up"] else { return 1 }
    var recovered = CleanupState(mouseDownPosted: true)
    guard recovered.failAfterMouseDown(cleanup: { true }) == 70,
          recovered.cleanupAttempted, recovered.cleanupSucceeded, recovered.mouseUpPosted else { return 2 }
    var failed = CleanupState(mouseDownPosted: true)
    guard failed.failAfterMouseDown(cleanup: { false }) == 71,
          failed.cleanupAttempted, !failed.cleanupSucceeded, !failed.mouseUpPosted else { return 3 }
    print("self_test=PASS coordinate_validation=true mouse_up_cleanup=true")
    return 0
}

if CommandLine.arguments == [CommandLine.arguments[0], "--self-test"] {
    exit(selfTest())
}

guard CommandLine.arguments.count == 8,
      let startX = Double(CommandLine.arguments[1]),
      let startY = Double(CommandLine.arguments[2]),
      let endX = Double(CommandLine.arguments[3]),
      let endY = Double(CommandLine.arguments[4]),
      let candidatePID = Int32(CommandLine.arguments[5]), candidatePID > 0,
      !CommandLine.arguments[6].isEmpty,
      !CommandLine.arguments[7].isEmpty else {
    fputs("usage: NativeDrag startX startY endX endY candidatePID qaRoot qaSource\n", stderr)
    exit(64)
}

let maximumSourceBytes: Int64 = 16 * 1024 * 1024
let rootInputURL = URL(fileURLWithPath: CommandLine.arguments[6]).standardizedFileURL
let sourceInputURL = URL(fileURLWithPath: CommandLine.arguments[7]).standardizedFileURL
let rootURL = rootInputURL.resolvingSymlinksInPath()
let sourceURL = sourceInputURL.resolvingSymlinksInPath()

func fileStatus(_ url: URL) -> stat? {
    var value = stat()
    return lstat(url.path, &value) == 0 ? value : nil
}
func isMode(_ status: stat, _ expected: mode_t) -> Bool {
    status.st_mode & mode_t(S_IFMT) == expected
}
func digest(_ data: Data) -> String {
    SHA256.hash(data: data).map { String(format: "%02x", $0) }.joined()
}
func sourceIdentity(_ url: URL) -> SourceIdentity? {
    guard let status = fileStatus(url), isMode(status, mode_t(S_IFREG)),
          !isMode(status, mode_t(S_IFLNK)), status.st_size >= 0,
          status.st_size <= maximumSourceBytes,
          let data = try? Data(contentsOf: url, options: .mappedIfSafe),
          data.count == Int(status.st_size) else { return nil }
    return SourceIdentity(
        byteCount: status.st_size,
        digest: digest(data),
        device: UInt64(status.st_dev),
        inode: UInt64(status.st_ino)
    )
}
func sourceScopeValid() -> Bool {
    guard let rootStatus = fileStatus(rootInputURL), let sourceStatus = fileStatus(sourceInputURL) else { return false }
    return validQASourceScope(
        rootComponents: rootURL.pathComponents,
        sourceComponents: sourceURL.pathComponents,
        rootIsDirectory: isMode(rootStatus, mode_t(S_IFDIR)),
        rootIsSymlink: isMode(rootStatus, mode_t(S_IFLNK)),
        sourceExists: true,
        sourceIsRegular: isMode(sourceStatus, mode_t(S_IFREG)),
        sourceIsSymlink: isMode(sourceStatus, mode_t(S_IFLNK)),
        sourceSize: sourceStatus.st_size,
        maximumSize: maximumSourceBytes
    )
}
guard sourceScopeValid(), let sourceBefore = sourceIdentity(sourceURL),
      let sourcePreimage = try? Data(contentsOf: sourceURL),
      sourcePreimage.count == Int(sourceBefore.byteCount) else {
    fputs("qa source scope failed closed\n", stderr)
    exit(64)
}

func restoreSourcePreimage() -> Bool {
    let currentURL = sourceInputURL.resolvingSymlinksInPath()
    guard Array(currentURL.pathComponents.prefix(rootURL.pathComponents.count)) == rootURL.pathComponents,
          currentURL.pathComponents.count > rootURL.pathComponents.count else { return false }
    if let currentStatus = fileStatus(sourceInputURL) {
        guard isMode(currentStatus, mode_t(S_IFREG)), !isMode(currentStatus, mode_t(S_IFLNK)) else { return false }
    }
    do {
        try sourcePreimage.write(to: sourceInputURL, options: .atomic)
        guard let restored = sourceIdentity(sourceInputURL) else { return false }
        return restored.byteCount == sourceBefore.byteCount && restored.digest == sourceBefore.digest
    } catch {
        return false
    }
}

func windowSnapshots() -> [WindowSnapshot]? {
    let options: CGWindowListOption = [.optionOnScreenOnly, .excludeDesktopElements]
    guard let raw = CGWindowListCopyWindowInfo(options, kCGNullWindowID) as? [[String: Any]], !raw.isEmpty else { return nil }
    var result: [WindowSnapshot] = []
    for item in raw {
        guard let pid = (item[kCGWindowOwnerPID as String] as? NSNumber)?.int32Value,
              let windowID = (item[kCGWindowNumber as String] as? NSNumber)?.uint32Value,
              let layer = (item[kCGWindowLayer as String] as? NSNumber)?.intValue,
              let alpha = (item[kCGWindowAlpha as String] as? NSNumber)?.doubleValue,
              let onScreen = (item[kCGWindowIsOnscreen as String] as? NSNumber)?.boolValue,
              let boundsValue = item[kCGWindowBounds as String],
              CFGetTypeID(boundsValue as CFTypeRef) == CFDictionaryGetTypeID(),
              let bounds = CGRect(dictionaryRepresentation: boundsValue as! CFDictionary) else { return nil }
        result.append(WindowSnapshot(
            identity: ReceiverIdentity(pid: pid, windowID: windowID),
            layer: layer,
            bounds: bounds,
            alpha: alpha,
            onScreen: onScreen
        ))
    }
    return result
}

var displayCount: UInt32 = 0
guard CGGetActiveDisplayList(0, nil, &displayCount) == .success, displayCount > 0 else { exit(65) }
var displayIDs = [CGDirectDisplayID](repeating: 0, count: Int(displayCount))
guard CGGetActiveDisplayList(displayCount, &displayIDs, &displayCount) == .success else { exit(65) }
let frozenDisplayBounds = displayIDs.prefix(Int(displayCount)).map(CGDisplayBounds)
let start = CGPoint(x: startX, y: startY)
let end = CGPoint(x: endX, y: endY)
guard validDrag(start: start, end: end, displays: frozenDisplayBounds) else {
    fputs("invalid or out-of-bounds drag coordinates\n", stderr)
    exit(64)
}

let finderApplications = NSRunningApplication.runningApplications(withBundleIdentifier: "com.apple.finder")
guard NSRunningApplication(processIdentifier: candidatePID) != nil,
      finderApplications.count == 1, let finder = finderApplications.first,
      let preflightWindows = windowSnapshots(),
      topReceiver(at: start, expectedPID: finder.processIdentifier, windows: preflightWindows) != nil,
      let destinationReceiver = topReceiver(at: end, expectedPID: candidatePID, windows: preflightWindows) else {
    fputs("delivery receiver preflight failed closed\n", stderr)
    exit(65)
}

let trusted = AXIsProcessTrusted()
let postAllowed = CGPreflightPostEventAccess()
print("AXIsProcessTrusted=\(trusted)")
print("CGPreflightPostEventAccess=\(postAllowed)")
guard trusted && postAllowed else { exit(77) }

let source = CGEventSource(stateID: .hidSystemState)
func postMouse(_ type: CGEventType, at point: CGPoint) -> Bool {
    guard let event = makeCopyMouseEvent(source: source, type: type, point: point) else { return false }
    event.post(tap: .cghidEventTap)
    return true
}
func postEscape(_ keyDown: Bool) -> Bool {
    guard let event = CGEvent(keyboardEventSource: source, virtualKey: 53, keyDown: keyDown) else { return false }
    event.post(tap: .cghidEventTap)
    return true
}
func cancelDrag() -> Bool {
    runSafeCancel(
        escapeDown: { postEscape(true) },
        escapeUp: { postEscape(false) },
        mouseUp: { postMouse(.leftMouseUp, at: start) }
    )
}

func terminateFailure(receiverIdentityPassed: Bool, cleanupComplete: Bool) -> Never {
    let sourceResult = sourceCleanupResult(
        before: sourceBefore,
        after: sourceIdentity(sourceInputURL),
        restore: restoreSourcePreimage
    )
    let sourceWasPreserved = sourceResult == .preserved
    let complete = cleanupComplete && sourceResult != .failed
    fputs(
        "receiver_identity=\(receiverIdentityPassed ? "PASS" : "FAIL") copy_semantic=PASS source_preserved=\(sourceWasPreserved ? "PASS" : "FAIL") cleanup=\(complete ? "PASS" : "FAIL")\n",
        stderr
    )
    exit(complete ? 70 : 71)
}

var lastPoint = start
guard postMouse(.mouseMoved, at: start) else { exit(70) }
Thread.sleep(forTimeInterval: 0.35)
guard postMouse(.leftMouseDown, at: start) else { exit(70) }
Thread.sleep(forTimeInterval: 0.45)

let steps = 36
for index in 1...steps {
    let progress = Double(index) / Double(steps)
    let eased = progress * progress * (3.0 - 2.0 * progress)
    lastPoint = CGPoint(x: start.x + (end.x - start.x) * eased, y: start.y + (end.y - start.y) * eased)
    guard postMouse(.leftMouseDragged, at: lastPoint) else {
        terminateFailure(receiverIdentityPassed: false, cleanupComplete: cancelDrag())
    }
    Thread.sleep(forTimeInterval: 0.035)
}

Thread.sleep(forTimeInterval: 0.75)
let receiverBeforeMouseUp = windowSnapshots().flatMap {
    topReceiver(at: end, expectedPID: candidatePID, windows: $0)
}
guard receiverStable(preflight: destinationReceiver, beforeMouseUp: receiverBeforeMouseUp) else {
    terminateFailure(receiverIdentityPassed: false, cleanupComplete: cancelDrag())
}
guard postMouse(.leftMouseUp, at: end) else {
    terminateFailure(receiverIdentityPassed: true, cleanupComplete: cancelDrag())
}
Thread.sleep(forTimeInterval: 0.5)
guard sourcePreserved(before: sourceBefore, after: sourceIdentity(sourceInputURL)) else {
    terminateFailure(receiverIdentityPassed: true, cleanupComplete: true)
}
print("receiver_identity=PASS copy_semantic=PASS source_preserved=PASS cleanup=PASS")
