import 'dart:convert';
import 'dart:ffi';
import 'dart:io';
import 'dart:typed_data';

import 'package:ffi/ffi.dart';

class KRWindowsProcessUtil {
  /// 🔍 调试标志：设为 true 可以追踪所有命令执行（用于排查黑窗问题）
  static const bool _debugCommandExecution = true;  // ← 开启调试

  static Future<ProcessResult> runHidden(String executable, List<String> arguments) async {
    if (!Platform.isWindows) {
      return Process.run(executable, arguments);
    }

    final result = await _runHiddenWindows(executable, arguments);
    return result;
  }

  static Future<int> startHidden(String executable, List<String> arguments) async {
    if (!Platform.isWindows) {
      final process = await Process.start(executable, arguments);
      return process.pid;
    }
    return _startHiddenWindows(executable, arguments);
  }

  static String _buildCommandLine(String executable, List<String> arguments) {
    final parts = <String>[_quoteArgument(executable)];
    for (final arg in arguments) {
      parts.add(_quoteArgument(arg));
    }
    return parts.join(' ');
  }

  static bool _shouldSearchPath(String executable) {
    if (executable.isEmpty) {
      return true;
    }
    return !(executable.contains('\\') || executable.contains('/') || executable.contains(':'));
  }

  static String _quoteArgument(String value) {
    if (value.isEmpty) {
      return '""';
    }
    final needsQuotes = value.contains(' ') || value.contains('\t') || value.contains('"');
    if (!needsQuotes) {
      return value;
    }
    final buffer = StringBuffer('"');
    var backslashes = 0;
    for (var i = 0; i < value.length; i++) {
      final char = value[i];
      if (char == '\\') {
        backslashes++;
        continue;
      }
      if (char == '"') {
        buffer.write('\\' * (backslashes * 2 + 1));
        buffer.write('"');
        backslashes = 0;
        continue;
      }
      if (backslashes > 0) {
        buffer.write('\\' * backslashes);
        backslashes = 0;
      }
      buffer.write(char);
    }
    if (backslashes > 0) {
      buffer.write('\\' * (backslashes * 2));
    }
    buffer.write('"');
    return buffer.toString();
  }

  static Future<ProcessResult> _runHiddenWindows(String executable, List<String> arguments) async {
    final stdoutPipe = _createPipe();
    final stderrPipe = _createPipe();

    final startupInfo = calloc<STARTUPINFO>();
    final processInfo = calloc<PROCESS_INFORMATION>();
    final commandLine = _buildCommandLine(executable, arguments).toNativeUtf16();
    final applicationName = _shouldSearchPath(executable) ? nullptr : executable.toNativeUtf16();
    final stdInput = _getStdInputHandle();

    startupInfo.ref
      ..cb = sizeOf<STARTUPINFO>()
      ..dwFlags = STARTF_USESTDHANDLES
      ..hStdInput = stdInput
      ..hStdOutput = stdoutPipe.write
      ..hStdError = stderrPipe.write;

    final created = _CreateProcessW(
      applicationName,
      commandLine,
      nullptr,
      nullptr,
      TRUE,
      CREATE_NO_WINDOW,
      nullptr,
      nullptr,
      startupInfo,
      processInfo,
    );

    calloc.free(commandLine);
    if (applicationName != nullptr) {
      calloc.free(applicationName);
    }

    if (created == 0) {
      _closeHandle(stdoutPipe.read);
      _closeHandle(stdoutPipe.write);
      _closeHandle(stderrPipe.read);
      _closeHandle(stderrPipe.write);
      calloc.free(startupInfo);
      calloc.free(processInfo);
      throw Exception('CreateProcessW failed: ${_GetLastError()}');
    }

    _closeHandle(stdoutPipe.write);
    _closeHandle(stderrPipe.write);

    final output = await _collectOutput(processInfo.ref.hProcess, stdoutPipe.read, stderrPipe.read);
    final exitCode = _getExitCode(processInfo.ref.hProcess);

    _closeHandle(stdoutPipe.read);
    _closeHandle(stderrPipe.read);
    _closeHandle(processInfo.ref.hThread);
    _closeHandle(processInfo.ref.hProcess);

    final pid = processInfo.ref.dwProcessId;
    calloc.free(startupInfo);
    calloc.free(processInfo);

    return ProcessResult(pid, exitCode, output.stdout, output.stderr);
  }

  static Future<int> _startHiddenWindows(String executable, List<String> arguments) async {
    final startupInfo = calloc<STARTUPINFO>();
    final processInfo = calloc<PROCESS_INFORMATION>();
    final commandLine = _buildCommandLine(executable, arguments).toNativeUtf16();
    final applicationName = _shouldSearchPath(executable) ? nullptr : executable.toNativeUtf16();

    startupInfo.ref.cb = sizeOf<STARTUPINFO>();

    final created = _CreateProcessW(
      applicationName,
      commandLine,
      nullptr,
      nullptr,
      FALSE,
      CREATE_NO_WINDOW,
      nullptr,
      nullptr,
      startupInfo,
      processInfo,
    );

    calloc.free(commandLine);
    if (applicationName != nullptr) {
      calloc.free(applicationName);
    }

    if (created == 0) {
      calloc.free(startupInfo);
      calloc.free(processInfo);
      throw Exception('CreateProcessW failed: ${_GetLastError()}');
    }

    final pid = processInfo.ref.dwProcessId;

    _closeHandle(processInfo.ref.hThread);
    _closeHandle(processInfo.ref.hProcess);
    calloc.free(startupInfo);
    calloc.free(processInfo);

    return pid;
  }

  static _Pipe _createPipe() {
    final readHandle = calloc<Pointer<Void>>();
    final writeHandle = calloc<Pointer<Void>>();
    final securityAttributes = calloc<SECURITY_ATTRIBUTES>();
    securityAttributes.ref
      ..nLength = sizeOf<SECURITY_ATTRIBUTES>()
      ..bInheritHandle = TRUE
      ..lpSecurityDescriptor = nullptr;

    final created = _CreatePipe(readHandle, writeHandle, securityAttributes, 0);
    calloc.free(securityAttributes);
    if (created == 0) {
      calloc.free(readHandle);
      calloc.free(writeHandle);
      throw Exception('CreatePipe failed: ${_GetLastError()}');
    }

    final readValue = readHandle.value;
    final writeValue = writeHandle.value;
    calloc.free(readHandle);
    calloc.free(writeHandle);

    final infoResult = _SetHandleInformation(readValue, HANDLE_FLAG_INHERIT, 0);
    if (infoResult == 0) {
      _closeHandle(readValue);
      _closeHandle(writeValue);
      throw Exception('SetHandleInformation failed: ${_GetLastError()}');
    }

    return _Pipe(readValue, writeValue);
  }

  static Pointer<Void> _getStdInputHandle() {
    final handle = _GetStdHandle(STD_INPUT_HANDLE);
    if (handle == INVALID_HANDLE_VALUE || handle == 0) {
      return nullptr;
    }
    return Pointer<Void>.fromAddress(handle);
  }

  static Future<_ProcessOutput> _collectOutput(
      Pointer<Void> process,
      Pointer<Void> stdoutHandle,
      Pointer<Void> stderrHandle,
      ) async {
    final stdoutBuilder = BytesBuilder();
    final stderrBuilder = BytesBuilder();

    while (true) {
      final stdoutRead = _drainPipe(stdoutHandle, stdoutBuilder);
      final stderrRead = _drainPipe(stderrHandle, stderrBuilder);
      final waitResult = _WaitForSingleObject(process, 0);
      if (waitResult == WAIT_OBJECT_0) {
        break;
      }
      if (waitResult == WAIT_FAILED) {
        throw Exception('WaitForSingleObject failed: ${_GetLastError()}');
      }
      if (!stdoutRead && !stderrRead) {
        await Future.delayed(const Duration(milliseconds: 10));
      } else {
        await Future<void>.delayed(Duration.zero);
      }
    }

    while (_drainPipe(stdoutHandle, stdoutBuilder) || _drainPipe(stderrHandle, stderrBuilder)) {
      await Future<void>.delayed(Duration.zero);
    }

    return _ProcessOutput(
      _decodeOutput(stdoutBuilder),
      _decodeOutput(stderrBuilder),
    );
  }

  static bool _drainPipe(Pointer<Void> handle, BytesBuilder builder) {
    final buffer = calloc<Uint8>(4096);
    final bytesRead = calloc<Uint32>();
    final available = calloc<Uint32>();
    var didRead = false;

    while (true) {
      final peekOk = _PeekNamedPipe(handle, nullptr, 0, nullptr, available, nullptr);
      if (peekOk == 0 || available.value == 0) {
        break;
      }
      final toRead = available.value < 4096 ? available.value : 4096;
      final ok = _ReadFile(handle, buffer.cast<Void>(), toRead, bytesRead, nullptr);
      final read = ok == 0 ? 0 : bytesRead.value;
      if (read == 0) {
        break;
      }
      builder.add(buffer.asTypedList(read));
      didRead = true;
    }

    calloc.free(buffer);
    calloc.free(bytesRead);
    calloc.free(available);

    return didRead;
  }

  static String _decodeOutput(BytesBuilder builder) {
    if (builder.length == 0) {
      return '';
    }
    final bytes = builder.toBytes();
    try {
      return systemEncoding.decode(bytes);
    } catch (_) {
      return utf8.decode(bytes, allowMalformed: true);
    }
  }

  static int _getExitCode(Pointer<Void> process) {
    final exitCode = calloc<Uint32>();
    final ok = _GetExitCodeProcess(process, exitCode);
    final code = ok == 0 ? -1 : exitCode.value;
    calloc.free(exitCode);
    return code;
  }

  static void _closeHandle(Pointer<Void> handle) {
    if (handle == nullptr) {
      return;
    }
    _CloseHandle(handle);
  }

  // 🔧 WinINet API helpers for proxy settings
  /// 查询当前系统代理设置
  static String? queryWindowsProxyServer() {
    if (!Platform.isWindows) return null;

    try {
      final bufferSize = calloc<Uint32>();
      bufferSize.value = sizeOf<INTERNET_PROXY_INFO>();

      final proxyInfo = calloc<INTERNET_PROXY_INFO>();

      final result = _InternetQueryOptionW(nullptr, INTERNET_OPTION_PROXY, proxyInfo.cast<Void>(), bufferSize);
      if (result == 0) {
        calloc.free(bufferSize);
        calloc.free(proxyInfo);
        return null;
      }

      final proxyServer = proxyInfo.ref.lpszProxy.toDartString();
      calloc.free(bufferSize);
      calloc.free(proxyInfo);

      return proxyServer.isEmpty ? null : proxyServer;
    } catch (e) {
      return null;
    }
  }

  /// 设置系统代理
  static bool setWindowsProxyServer(String? server) {
    if (!Platform.isWindows) return false;

    try {
      final proxyInfo = calloc<INTERNET_PROXY_INFO>();

      if (server != null && server.isNotEmpty) {
        // 设置代理模式
        proxyInfo.ref.dwAccessType = INTERNET_OPEN_TYPE_PROXY;
        proxyInfo.ref.lpszProxy = server.toNativeUtf16();
        proxyInfo.ref.lpszProxyBypass = ''.toNativeUtf16();
      } else {
        // 禁用代理
        proxyInfo.ref.dwAccessType = INTERNET_OPEN_TYPE_DIRECT;
        proxyInfo.ref.lpszProxy = nullptr;
        proxyInfo.ref.lpszProxyBypass = nullptr;
      }

      final result = _InternetSetOptionW(
        nullptr,
        INTERNET_OPTION_PROXY,
        proxyInfo.cast<Void>(),
        sizeOf<INTERNET_PROXY_INFO>(),
      );

      if (server != null && server.isNotEmpty) {
        calloc.free(proxyInfo.ref.lpszProxy);
        calloc.free(proxyInfo.ref.lpszProxyBypass);
      }
      calloc.free(proxyInfo);

      return result != 0;
    } catch (e) {
      return false;
    }
  }

  /// 禁用系统代理
  static bool disableWindowsProxy() {
    return setWindowsProxyServer(null);
  }
}

class _Pipe {
  final Pointer<Void> read;
  final Pointer<Void> write;

  _Pipe(this.read, this.write);
}

class _ProcessOutput {
  final String stdout;
  final String stderr;

  _ProcessOutput(this.stdout, this.stderr);
}

const int TRUE = 1;
const int FALSE = 0;

const int STARTF_USESTDHANDLES = 0x00000100;
const int CREATE_NO_WINDOW = 0x08000000;
const int HANDLE_FLAG_INHERIT = 0x00000001;
const int WAIT_OBJECT_0 = 0x00000000;
const int WAIT_FAILED = 0xFFFFFFFF;
const int STD_INPUT_HANDLE = -10;
const int INVALID_HANDLE_VALUE = -1;

final class SECURITY_ATTRIBUTES extends Struct {
  @Uint32()
  external int nLength;

  external Pointer<Void> lpSecurityDescriptor;

  @Int32()
  external int bInheritHandle;
}

final class STARTUPINFO extends Struct {
  @Uint32()
  external int cb;

  external Pointer<Utf16> lpReserved;

  external Pointer<Utf16> lpDesktop;

  external Pointer<Utf16> lpTitle;

  @Uint32()
  external int dwX;

  @Uint32()
  external int dwY;

  @Uint32()
  external int dwXSize;

  @Uint32()
  external int dwYSize;

  @Uint32()
  external int dwXCountChars;

  @Uint32()
  external int dwYCountChars;

  @Uint32()
  external int dwFillAttribute;

  @Uint32()
  external int dwFlags;

  @Uint16()
  external int wShowWindow;

  @Uint16()
  external int cbReserved2;

  external Pointer<Uint8> lpReserved2;

  external Pointer<Void> hStdInput;

  external Pointer<Void> hStdOutput;

  external Pointer<Void> hStdError;
}

final class PROCESS_INFORMATION extends Struct {
  external Pointer<Void> hProcess;

  external Pointer<Void> hThread;

  @Uint32()
  external int dwProcessId;

  @Uint32()
  external int dwThreadId;
}

final DynamicLibrary _kernel32 = DynamicLibrary.open('kernel32.dll');

final _CreatePipe = _kernel32.lookupFunction<
    Int32 Function(Pointer<Pointer<Void>>, Pointer<Pointer<Void>>, Pointer<SECURITY_ATTRIBUTES>, Uint32),
    int Function(Pointer<Pointer<Void>>, Pointer<Pointer<Void>>, Pointer<SECURITY_ATTRIBUTES>, int)>(
  'CreatePipe',
);

final _SetHandleInformation = _kernel32.lookupFunction<
    Int32 Function(Pointer<Void>, Uint32, Uint32),
    int Function(Pointer<Void>, int, int)>(
  'SetHandleInformation',
);

final _CreateProcessW = _kernel32.lookupFunction<
    Int32 Function(
        Pointer<Utf16>,
        Pointer<Utf16>,
        Pointer<SECURITY_ATTRIBUTES>,
        Pointer<SECURITY_ATTRIBUTES>,
        Int32,
        Uint32,
        Pointer<Void>,
        Pointer<Utf16>,
        Pointer<STARTUPINFO>,
        Pointer<PROCESS_INFORMATION>,
        ),
    int Function(
        Pointer<Utf16>,
        Pointer<Utf16>,
        Pointer<SECURITY_ATTRIBUTES>,
        Pointer<SECURITY_ATTRIBUTES>,
        int,
        int,
        Pointer<Void>,
        Pointer<Utf16>,
        Pointer<STARTUPINFO>,
        Pointer<PROCESS_INFORMATION>,
        )>(
  'CreateProcessW',
);

final _PeekNamedPipe = _kernel32.lookupFunction<
    Int32 Function(Pointer<Void>, Pointer<Void>, Uint32, Pointer<Uint32>, Pointer<Uint32>, Pointer<Uint32>),
    int Function(Pointer<Void>, Pointer<Void>, int, Pointer<Uint32>, Pointer<Uint32>, Pointer<Uint32>)>(
  'PeekNamedPipe',
);

final _ReadFile = _kernel32.lookupFunction<
    Int32 Function(Pointer<Void>, Pointer<Void>, Uint32, Pointer<Uint32>, Pointer<Void>),
    int Function(Pointer<Void>, Pointer<Void>, int, Pointer<Uint32>, Pointer<Void>)>(
  'ReadFile',
);

final _CloseHandle = _kernel32.lookupFunction<
    Int32 Function(Pointer<Void>),
    int Function(Pointer<Void>)>(
  'CloseHandle',
);

final _WaitForSingleObject = _kernel32.lookupFunction<
    Uint32 Function(Pointer<Void>, Uint32),
    int Function(Pointer<Void>, int)>(
  'WaitForSingleObject',
);

final _GetStdHandle = _kernel32.lookupFunction<
    IntPtr Function(Int32),
    int Function(int)>(
  'GetStdHandle',
);

final _GetExitCodeProcess = _kernel32.lookupFunction<
    Int32 Function(Pointer<Void>, Pointer<Uint32>),
    int Function(Pointer<Void>, Pointer<Uint32>)>(
  'GetExitCodeProcess',
);

final _GetLastError = _kernel32.lookupFunction<
    Uint32 Function(),
    int Function()>(
  'GetLastError',
);

// 🔧 WinINet API for proxy settings - 用于替代 reg 命令，消除黑屏
final DynamicLibrary _wininet = DynamicLibrary.open('wininet.dll');

const int INTERNET_OPTION_PROXY = 38;
const int INTERNET_OPEN_TYPE_PROXY = 3;
const int INTERNET_OPEN_TYPE_DIRECT = 1;

final class INTERNET_PROXY_INFO extends Struct {
  @Int32()
  external int dwAccessType;

  external Pointer<Utf16> lpszProxy;

  external Pointer<Utf16> lpszProxyBypass;
}

/// WinINet InternetSetOption API - 用于设置系统代理
final _InternetSetOptionW = _wininet.lookupFunction<
    Int32 Function(Pointer<Void>, Uint32, Pointer<Void>, Uint32),
    int Function(Pointer<Void>, int, Pointer<Void>, int)>(
  'InternetSetOptionW',
);

/// WinINet InternetQueryOption API - 用于查询系统代理
final _InternetQueryOptionW = _wininet.lookupFunction<
    Int32 Function(Pointer<Void>, Uint32, Pointer<Void>, Pointer<Uint32>),
    int Function(Pointer<Void>, int, Pointer<Void>, Pointer<Uint32>)>(
  'InternetQueryOptionW',
);
