import Foundation
import Network
import CYaneuraOu

/// やねうら王エンジンとのブリッジ（ソケット通信版）
public class YaneuraOuBridge {
    
    // MARK: - Properties
    
    private var connection: NWConnection?
    private var isRunning = false
    private let port: UInt16
    private var receiveQueue: DispatchQueue
    private var responseBuffer: [String] = []
    private var bufferLock = NSLock()
    
    // MARK: - Initialization
    
    public init(port: UInt16 = 12345) {
        self.port = port
        self.receiveQueue = DispatchQueue(label: "com.yaneuraou.receive", qos: .userInitiated)
    }
    
    deinit {
        stop()
    }
    
    // MARK: - Engine Control
    
    /// エンジンを起動
    public func start() async throws {
        guard !isRunning else { return }
        
        // エンジンのソケットサーバーを別スレッドで起動
        // （yaneuraou_ios_server_startはaccept()でブロックするため）
        Task.detached {
            _ = yaneuraou_ios_server_start(Int32(self.port))
        }
        
        // サーバーが起動してリッスン状態になるまで待機
        try await Task.sleep(nanoseconds: 1_000_000_000) // 1秒
        
        // ソケット接続を確立
        try await connectToEngine()
        
        isRunning = true
    }
    
    /// エンジンを停止
    public func stop() {
        if let conn = connection {
            conn.cancel()
            connection = nil
        }
        
        yaneuraou_ios_server_stop()
        isRunning = false
        
        bufferLock.lock()
        responseBuffer.removeAll()
        bufferLock.unlock()
    }
    
    // MARK: - Socket Connection
    
    private func connectToEngine() async throws {
        let host = NWEndpoint.Host("127.0.0.1")
        let port = NWEndpoint.Port(integerLiteral: self.port)
        
        let connection = NWConnection(host: host, port: port, using: .tcp)
        self.connection = connection
        
        return try await withCheckedThrowingContinuation { continuation in
            var resumed = false
            
            connection.stateUpdateHandler = { [weak self] state in
                switch state {
                case .ready:
                    if !resumed {
                        resumed = true
                        self?.startReceiving()
                        continuation.resume()
                    }
                case .failed(let error):
                    if !resumed {
                        resumed = true
                        print("❌ [YaneuraOuBridge] ソケット接続失敗: \(error)")
                        continuation.resume(throwing: EngineError.initializationFailed("Connection failed: \(error)"))
                    }
                case .cancelled:
                    if !resumed {
                        resumed = true
                        continuation.resume(throwing: EngineError.initializationFailed("Connection cancelled"))
                    }
                default:
                    break
                }
            }
            
            connection.start(queue: receiveQueue)
        }
    }
    
    private func startReceiving() {
        guard let connection = connection else { return }
        
        connection.receive(minimumIncompleteLength: 1, maximumLength: 65536) { [weak self] data, _, isComplete, error in
            if let data = data, !data.isEmpty {
                if let text = String(data: data, encoding: .utf8) {
                    self?.handleReceivedData(text)
                }
            }
            
            if let error = error {
                print("❌ [YaneuraOuBridge] 受信エラー: \(error)")
                return
            }
            
            if !isComplete {
                self?.startReceiving()
            }
        }
    }
    
    private func handleReceivedData(_ text: String) {
        let lines = text.components(separatedBy: .newlines).filter { !$0.isEmpty }
        
        bufferLock.lock()
        for line in lines {
            // エラーメッセージと不正な指し手をログ出力
            if line.contains("Error!") {
                print("⚠️ [YaneuraOuBridge] \(line)")
            } else if line.contains("Illegal") {
                // 不正な指し手の詳細を表示
                print("❌ [YaneuraOuBridge] 不正な指し手: \(line)")
            }
            responseBuffer.append(line)
        }
        bufferLock.unlock()
    }
    
    // MARK: - USI Communication
    
    /// USIコマンドを送信
    public func sendCommand(_ command: String) throws {
        guard isRunning, let connection = connection else {
            throw EngineError.engineNotReady
        }
        
        let data = (command + "\n").data(using: .utf8)!
        connection.send(content: data, completion: .contentProcessed { error in
            if let error = error {
                print("❌ [YaneuraOuBridge] 送信エラー: \(error)")
            }
        })
    }
    
    /// 特定の応答を待つ
    public func waitForResponse(containing keyword: String, timeout: TimeInterval = 10.0) async throws -> String {
        guard isRunning else {
            throw EngineError.engineNotReady
        }
        
        let startTime = Date()
        
        while Date().timeIntervalSince(startTime) < timeout {
            bufferLock.lock()
            if let index = responseBuffer.firstIndex(where: { $0.contains(keyword) }) {
                let response = responseBuffer[index]
                responseBuffer.remove(at: index)
                bufferLock.unlock()
                return response
            }
            bufferLock.unlock()
            
            try await Task.sleep(nanoseconds: 10_000_000) // 0.01秒
        }
        
        throw EngineError.timeout
    }
    
    /// 複数行の応答を収集
    public func collectResponses(until keyword: String = "bestmove", timeout: TimeInterval = 30.0) async throws -> [String] {
        guard isRunning else {
            throw EngineError.engineNotReady
        }
        
        var responses: [String] = []
        let startTime = Date()
        
        while Date().timeIntervalSince(startTime) < timeout {
            bufferLock.lock()
            if !responseBuffer.isEmpty {
                let batch = responseBuffer
                responseBuffer.removeAll()
                bufferLock.unlock()
                
                responses.append(contentsOf: batch)
                
                // キーワードを含む行が見つかったら終了
                if batch.contains(where: { $0.hasPrefix(keyword) }) {
                    return responses
                }
            } else {
                bufferLock.unlock()
            }
            
            try await Task.sleep(nanoseconds: 50_000_000) // 0.05秒
        }
        
        if responses.isEmpty {
            print("⏱️ [YaneuraOuBridge] タイムアウト: 応答なし")
            throw EngineError.timeout
        }
        
        print("⏱️ [YaneuraOuBridge] タイムアウト: \(responses.count)行受信したが'\(keyword)'が見つからない")
        return responses
    }
    
    /// 複数行の応答を収集（リアルタイムコールバック版）
    public func collectResponsesWithCallback(
        until keyword: String = "bestmove",
        timeout: TimeInterval = 30.0,
        onUpdate: @escaping ([String]) -> Void
    ) async throws -> [String] {
        guard isRunning else {
            throw EngineError.engineNotReady
        }
        
        var allResponses: [String] = []
        let startTime = Date()
        var lastResponseTime = Date()
        let initialResponseTimeout: TimeInterval = 2.0  // 最初の応答を2秒以内に期待
        
        while Date().timeIntervalSince(startTime) < timeout {
            bufferLock.lock()
            if !responseBuffer.isEmpty {
                let batch = responseBuffer
                responseBuffer.removeAll()
                bufferLock.unlock()
                
                allResponses.append(contentsOf: batch)
                lastResponseTime = Date()  // 応答があったので更新
                
                // 新しい応答があればコールバックを呼ぶ
                onUpdate(allResponses)
                
                // キーワードを含む行が見つかったら終了
                if batch.contains(where: { $0.hasPrefix(keyword) }) {
                    return allResponses
                }
            } else {
                bufferLock.unlock()
                
                // 最初の応答がない場合、早期にタイムアウト
                if allResponses.isEmpty && Date().timeIntervalSince(startTime) > initialResponseTimeout {
                    print("⏱️ [YaneuraOuBridge] 早期タイムアウト: \(initialResponseTimeout)秒以内に応答なし")
                    throw EngineError.timeout
                }
            }
            
            try await Task.sleep(nanoseconds: 50_000_000) // 0.05秒
        }
        
        if allResponses.isEmpty {
            print("⏱️ [YaneuraOuBridge] タイムアウト: 応答なし")
            throw EngineError.timeout
        }
        
        print("⏱️ [YaneuraOuBridge] タイムアウト: \(allResponses.count)行受信したが'\(keyword)'が見つからない")
        return allResponses
    }
    
    /// バッファをクリア
    public func clearBuffer() {
        bufferLock.lock()
        responseBuffer.removeAll()
        bufferLock.unlock()
    }
}

// MARK: - Engine Error

public enum EngineError: Error {
    case initializationFailed(String)
    case engineNotReady
    case invalidSFEN(String)
    case timeout
    case cancelled
}
