import Cocoa
import FlutterMacOS

public class TsnetFlutterPlugin: NSObject, FlutterPlugin {

    private lazy var stateDir: String = {
        let appSupport = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask).first!
        let dir = appSupport.appendingPathComponent("tailscale_state", isDirectory: true)
        try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
        return dir.path
    }()

    public static func register(with registrar: FlutterPluginRegistrar) {
        let channel = FlutterMethodChannel(name: "tsnet_flutter", binaryMessenger: registrar.messenger)
        let instance = TsnetFlutterPlugin()
        registrar.addMethodCallDelegate(instance, channel: channel)
    }

    public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        switch call.method {
        case "start":
            handleStart(call, result: result)
        case "startProxy":
            handleStartProxy(call, result: result)
        case "stopProxy":
            handleStopProxy(result: result)
        case "stop":
            handleStop(result: result)
        case "status":
            handleStatus(result: result)
        case "tailscaleIP":
            handleTailscaleIP(result: result)
        default:
            result(FlutterMethodNotImplemented)
        }
    }

    private func consumeCString(_ ptr: UnsafeMutablePointer<CChar>?) -> String? {
        guard let ptr = ptr else { return nil }
        let str = String(cString: ptr)
        TailscaleFreeString(ptr)
        return str
    }

    private func handleStart(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let args = call.arguments as? [String: Any],
              let authKey = args["authKey"] as? String,
              let hostname = args["hostname"] as? String else {
            result(FlutterError(code: "INVALID_ARGS", message: "Missing authKey or hostname", details: nil))
            return
        }

        let dir = self.stateDir
        DispatchQueue.global(qos: .userInitiated).async { [self] in
            let errPtr = dir.withCString { dirC in
                authKey.withCString { keyC in
                    hostname.withCString { hostC in
                        TailscaleStart(
                            UnsafeMutablePointer(mutating: dirC),
                            UnsafeMutablePointer(mutating: keyC),
                            UnsafeMutablePointer(mutating: hostC)
                        )
                    }
                }
            }
            let err = self.consumeCString(errPtr)
            DispatchQueue.main.async {
                if let err = err {
                    result(FlutterError(code: "START_FAILED", message: err, details: nil))
                } else {
                    result(nil)
                }
            }
        }
    }

    private func handleStartProxy(_ call: FlutterMethodCall, result: @escaping FlutterResult) {
        guard let args = call.arguments as? [String: Any],
              let ip = args["ip"] as? String,
              let port = args["port"] as? Int else {
            result(FlutterError(code: "INVALID_ARGS", message: "Missing ip or port", details: nil))
            return
        }

        DispatchQueue.global(qos: .userInitiated).async { [self] in
            var localPort: CInt = 0
            let errPtr = ip.withCString { ipC in
                TailscaleStartProxy(
                    UnsafeMutablePointer(mutating: ipC),
                    CInt(port),
                    &localPort
                )
            }
            let err = self.consumeCString(errPtr)
            DispatchQueue.main.async {
                if let err = err {
                    result(FlutterError(code: "PROXY_FAILED", message: err, details: nil))
                } else {
                    result(Int(localPort))
                }
            }
        }
    }

    private func handleStopProxy(result: @escaping FlutterResult) {
        DispatchQueue.global(qos: .userInitiated).async { [self] in
            let err = self.consumeCString(TailscaleStopProxy())
            DispatchQueue.main.async {
                if let err = err {
                    result(FlutterError(code: "STOP_PROXY_FAILED", message: err, details: nil))
                } else {
                    result(nil)
                }
            }
        }
    }

    private func handleStop(result: @escaping FlutterResult) {
        DispatchQueue.global(qos: .userInitiated).async { [self] in
            let err = self.consumeCString(TailscaleStop())
            DispatchQueue.main.async {
                if let err = err {
                    result(FlutterError(code: "STOP_FAILED", message: err, details: nil))
                } else {
                    result(nil)
                }
            }
        }
    }

    private func handleStatus(result: @escaping FlutterResult) {
        DispatchQueue.global(qos: .userInitiated).async { [self] in
            var errPtr: UnsafeMutablePointer<CChar>? = nil
            let jsonPtr = TailscaleStatus(&errPtr)
            let err = self.consumeCString(errPtr)
            let json = self.consumeCString(jsonPtr)
            DispatchQueue.main.async {
                if let err = err {
                    result(FlutterError(code: "STATUS_FAILED", message: err, details: nil))
                } else {
                    result(json)
                }
            }
        }
    }

    private func handleTailscaleIP(result: @escaping FlutterResult) {
        DispatchQueue.global(qos: .userInitiated).async { [self] in
            var errPtr: UnsafeMutablePointer<CChar>? = nil
            let ipPtr = TailscaleIP(&errPtr)
            let err = self.consumeCString(errPtr)
            let ip = self.consumeCString(ipPtr)
            DispatchQueue.main.async {
                if let err = err {
                    result(FlutterError(code: "IP_FAILED", message: err, details: nil))
                } else {
                    result(ip)
                }
            }
        }
    }
}
