import Foundation

/// エンジンコーディネーター
/// やねうら王エンジンとの通信を管理するGPL3ライセンスのクラス
public class EngineCoordinator {
    
    // MARK: - Properties
    
    private let stateManager = EngineStateManager()
    private var isCancelled: Bool = false
    private var bridge: YaneuraOuBridge?
    
    // MARK: - Initialization
    
    public init() {}
    
    // MARK: - Public Interface
    
    /// エンジンを初期化
    public func initialize(evalDir: String? = nil) async throws {
        guard stateManager.tryTransition(to: .initializing) else {
            throw EngineError.initializationFailed("Invalid state transition")
        }
        
        // YaneuraOuブリッジを作成して起動
        bridge = YaneuraOuBridge()
        try await bridge?.start()
        
        // USI初期化シーケンス
        try bridge?.sendCommand("usi")
        _ = try await bridge?.waitForResponse(containing: "usiok", timeout: 5.0)
        
        // 定跡を無効化（USI_OwnBook=false, BookOnTheFly=true）
        try bridge?.sendCommand("setoption name USI_OwnBook value false")
        try bridge?.sendCommand("setoption name BookOnTheFly value true")
        
        // スレッド数を設定（GameSettingsから取得）
        let threadCount = getThreadCount()
        let processorCount = ProcessInfo.processInfo.activeProcessorCount
        try bridge?.sendCommand("setoption name Threads value \(threadCount)")
        print("🧵 [EngineCoordinator] スレッド数設定: \(threadCount) (端末コア数: \(processorCount))")
        
        // ハッシュサイズを設定（GameSettingsから取得）
        let hashSize = getHashSize()
        try bridge?.sendCommand("setoption name USI_Hash value \(hashSize)")
        print("💾 [EngineCoordinator] ハッシュサイズ設定: \(hashSize)MB")
        
        // 評価関数ディレクトリを設定（指定された場合）
        if let evalDir = evalDir {
            print("📁 [EngineCoordinator] 評価関数ディレクトリを設定: \(evalDir)")
            try bridge?.sendCommand("setoption name EvalDir value \(evalDir)")
        } else {
            print("⚠️ [EngineCoordinator] 評価関数ディレクトリが指定されていません")
        }
        
        // 短い待機時間（設定反映のため）
        try await Task.sleep(nanoseconds: 50_000_000) // 0.05秒
        
        // エンジンを準備
        print("⏳ [EngineCoordinator] エンジン準備中...")
        try bridge?.sendCommand("isready")
        let readyResponse = try await bridge?.waitForResponse(containing: "readyok", timeout: 10.0)
        print("✅ [EngineCoordinator] エンジン準備完了: \(readyResponse ?? "no response")")
        
        _ = stateManager.tryTransition(to: .ready)
    }
    
    /// 局面を解析
    public func analyzePosition(sfen: String, timeLimit: TimeInterval) async throws -> PositionEvaluation {
        // エンジンの状態をチェック
        let currentState = stateManager.getState()
        print("🔍 [EngineCoordinator] analyzePosition呼び出し - 現在の状態: \(currentState)")
        
        // 解析中の場合は停止してから開始
        if stateManager.isAnalyzing() {
            print("🛑 [EngineCoordinator] 前の解析を停止して新しい解析を開始")
            stopCurrentSearch()
            // 状態をreadyに戻す
            _ = stateManager.tryTransition(to: .ready)
            // 少し待つ
            try await Task.sleep(nanoseconds: 100_000_000) // 0.1秒
        }
        
        // 初期化中の場合は完了を待つ（最大5秒）
        if case .initializing = currentState {
            print("⏳ [EngineCoordinator] エンジン初期化中 - 完了を待機")
            for _ in 0..<50 {
                try await Task.sleep(nanoseconds: 100_000_000) // 0.1秒
                if stateManager.isReady() {
                    print("✅ [EngineCoordinator] エンジン初期化完了")
                    break
                }
            }
        }
        
        guard stateManager.isReady() else {
            let finalState = stateManager.getState()
            print("❌ [EngineCoordinator] エンジンが準備完了状態ではありません: \(finalState)")
            throw EngineError.engineNotReady
        }
        
        guard let bridge = bridge else {
            print("❌ [EngineCoordinator] ブリッジが初期化されていません")
            throw EngineError.engineNotReady
        }
        
        if isCancelled {
            throw EngineError.cancelled
        }
        
        _ = stateManager.tryTransition(to: .analyzing)
        
        // 局面を設定
        // startposで始まる場合は"position"のみを前置、それ以外は"position sfen"を前置
        let positionCommand: String
        if sfen.hasPrefix("startpos") {
            positionCommand = "position \(sfen)"
        } else {
            positionCommand = "position sfen \(sfen)"
        }
        try bridge.sendCommand(positionCommand)
        
        // 解析開始（時間制限付き）
        let timeMs = Int(timeLimit * 1000)
        try bridge.sendCommand("go movetime \(timeMs)")
        
        // 全ての応答を収集（bestmoveまで）
        let responses = try await bridge.collectResponses(until: "bestmove", timeout: timeLimit + 1.0)
        
        _ = stateManager.tryTransition(to: .ready)
        
        // 応答をパース
        let evaluation = try parseAnalysisResponses(responses)
        
        // エラーが発生した場合、エンジンの状態をリセット
        if evaluation.hasError {
            print("🔄 [EngineCoordinator] エラー検出 - エンジン状態をリセット")
            try await resetEngineState()
        }
        
        return evaluation
    }
    
    /// MultiPVモードで複数の候補手を解析
    public func analyzeWithMultiPV(sfen: String, pvCount: Int, timeLimit: TimeInterval) async throws -> [CandidateMove] {
        // エンジンの状態をチェック
        let currentState = stateManager.getState()
        print("🔍 [EngineCoordinator] analyzeWithMultiPV呼び出し - 現在の状態: \(currentState)")
        
        // 解析中の場合は停止してから開始
        if stateManager.isAnalyzing() {
            print("🛑 [EngineCoordinator] 前の解析を停止して新しい解析を開始")
            stopCurrentSearch()
            // 状態をreadyに戻す
            _ = stateManager.tryTransition(to: .ready)
            // 少し待つ
            try await Task.sleep(nanoseconds: 100_000_000) // 0.1秒
        }
        
        // 初期化中の場合は完了を待つ（最大5秒）
        if case .initializing = currentState {
            print("⏳ [EngineCoordinator] エンジン初期化中 - 完了を待機")
            for _ in 0..<50 {
                try await Task.sleep(nanoseconds: 100_000_000) // 0.1秒
                if stateManager.isReady() {
                    print("✅ [EngineCoordinator] エンジン初期化完了")
                    break
                }
            }
        }
        
        guard stateManager.isReady() else {
            let finalState = stateManager.getState()
            print("❌ [EngineCoordinator] エンジンが準備完了状態ではありません: \(finalState)")
            throw EngineError.engineNotReady
        }
        
        guard let bridge = bridge else {
            print("❌ [EngineCoordinator] ブリッジが初期化されていません")
            throw EngineError.engineNotReady
        }
        
        if isCancelled {
            throw EngineError.cancelled
        }
        
        _ = stateManager.tryTransition(to: .analyzing)
        
        // MultiPVオプションを設定
        try bridge.sendCommand("setoption name MultiPV value \(pvCount)")
        
        // 短い待機時間
        try await Task.sleep(nanoseconds: 100_000_000) // 0.1秒
        
        // 局面を設定
        let positionCommand: String
        if sfen.hasPrefix("startpos") {
            positionCommand = "position \(sfen)"
        } else {
            positionCommand = "position sfen \(sfen)"
        }
        try bridge.sendCommand(positionCommand)
        
        // 解析開始（時間制限付き）
        let timeMs = Int(timeLimit * 1000)
        try bridge.sendCommand("go movetime \(timeMs)")
        
        // 全ての応答を収集（bestmoveまで）
        let responses = try await bridge.collectResponses(until: "bestmove", timeout: timeLimit + 1.0)
        
        // MultiPVを1に戻す
        try bridge.sendCommand("setoption name MultiPV value 1")
        
        _ = stateManager.tryTransition(to: .ready)
        
        // 応答をパースして候補手リストを作成
        let candidates = try parseMultiPVResponses(responses)
        
        return candidates
    }
    
    /// MultiPVモードで複数の候補手を解析（リアルタイム更新版）
    public func analyzeWithMultiPVRealtime(
        sfen: String,
        pvCount: Int,
        timeLimit: TimeInterval,
        onUpdate: @escaping ([CandidateMove]) -> Void
    ) async throws -> [CandidateMove] {
        // エンジンの状態をチェック
        let currentState = stateManager.getState()
        print("🔍 [EngineCoordinator] analyzeWithMultiPVRealtime呼び出し - 現在の状態: \(currentState)")
        
        // 解析中の場合は停止してから開始
        if stateManager.isAnalyzing() {
            print("🛑 [EngineCoordinator] 前の解析を停止して新しい解析を開始")
            stopCurrentSearch()
            // 状態をreadyに戻す
            _ = stateManager.tryTransition(to: .ready)
            // 少し待つ
            try await Task.sleep(nanoseconds: 100_000_000) // 0.1秒
        }
        
        // 初期化中の場合は完了を待つ（最大5秒）
        if case .initializing = currentState {
            print("⏳ [EngineCoordinator] エンジン初期化中 - 完了を待機")
            for _ in 0..<50 {
                try await Task.sleep(nanoseconds: 100_000_000) // 0.1秒
                if stateManager.isReady() {
                    print("✅ [EngineCoordinator] エンジン初期化完了")
                    break
                }
            }
        }
        
        guard stateManager.isReady() else {
            let finalState = stateManager.getState()
            print("❌ [EngineCoordinator] エンジンが準備完了状態ではありません: \(finalState)")
            throw EngineError.engineNotReady
        }
        
        guard let bridge = bridge else {
            print("❌ [EngineCoordinator] ブリッジが初期化されていません")
            throw EngineError.engineNotReady
        }
        
        if isCancelled {
            throw EngineError.cancelled
        }
        
        _ = stateManager.tryTransition(to: .analyzing)
        
        // MultiPVオプションを設定
        try bridge.sendCommand("setoption name MultiPV value \(pvCount)")
        
        // 短い待機時間
        try await Task.sleep(nanoseconds: 100_000_000) // 0.1秒
        
        // 局面を設定
        let positionCommand: String
        if sfen.hasPrefix("startpos") {
            positionCommand = "position \(sfen)"
        } else {
            positionCommand = "position sfen \(sfen)"
        }
        try bridge.sendCommand(positionCommand)
        
        // 解析開始（時間制限付き）
        let timeMs = Int(timeLimit * 1000)
        try bridge.sendCommand("go movetime \(timeMs)")
        
        print("🔍 [EngineCoordinator] MultiPV解析開始 - pvCount: \(pvCount), timeLimit: \(timeLimit)秒")
        
        // リアルタイムコールバック付きで応答を収集
        // タイムアウトは検索時間 + 0.5秒（エンジンが応答しない場合は早めに諦める）
        let responses = try await bridge.collectResponsesWithCallback(until: "bestmove", timeout: timeLimit + 0.5) { currentResponses in
            // 現在までの応答をパースして候補手を更新
            do {
                let candidates = try self.parseMultiPVResponses(currentResponses)
                if !candidates.isEmpty {
                    onUpdate(candidates)
                }
            } catch {
                print("⚠️ [EngineCoordinator] リアルタイムパースエラー: \(error)")
            }
        }
        
        // MultiPVを1に戻す
        try bridge.sendCommand("setoption name MultiPV value 1")
        
        _ = stateManager.tryTransition(to: .ready)
        
        // 最終的な候補手リストを作成
        let candidates = try parseMultiPVResponses(responses)
        
        print("✅ [EngineCoordinator] MultiPV解析完了 - 候補手数: \(candidates.count)")
        
        return candidates
    }
    
    /// エンジンの状態をリセット
    private func resetEngineState() async throws {
        guard let bridge = bridge else { return }
        
        // usinewgameコマンドでエンジンの内部状態をリセット
        try bridge.sendCommand("usinewgame")
        
        // 短い待機時間
        try await Task.sleep(nanoseconds: 100_000_000) // 0.1秒
        
        // isreadyで準備完了を待つ
        try bridge.sendCommand("isready")
        _ = try await bridge.waitForResponse(containing: "readyok", timeout: 2.0)
        
        print("✅ [EngineCoordinator] エンジン状態リセット完了")
    }
    
    /// 解析応答をパース（複数行）
    private func parseAnalysisResponses(_ responses: [String]) throws -> PositionEvaluation {
        var bestMove: String?
        var score: Int = 0
        var depth: Int = 0
        var pv: [String] = []
        var nps: Int? = nil
        var hasError = false
        
        // エラーチェック（定跡エラーは無視）
        for response in responses {
            if response.contains("Illegal Input Move") {
                hasError = true
                print("❌ [EngineCoordinator] 不正な指し手エラー: \(response)")
            } else if response.contains("Error!") && !response.contains("book") {
                hasError = true
                print("❌ [EngineCoordinator] エンジンエラー: \(response)")
            }
        }
        
        for response in responses {
            let parsed = USIProtocolHandler.parseResponse(response)
            
            switch parsed {
            case .bestmove(let move, _):
                bestMove = move
                
            case .info(let d, let s, let scoreType, let pvMoves, _):
                // upperbound/lowerboundがある行はスキップ
                if response.contains("upperbound") || response.contains("lowerbound") {
                    continue
                }
                
                // 最後の有効なinfo行の情報を使用
                if let d = d {
                    depth = d
                }
                if let s = s {
                    // v8.60git/v9.00ではFV_SCALE=16がデフォルト
                    // 整数除算で小さい値が0になる問題を回避するため、そのまま使用
                    // （FV_SCALEは内部的に既に適用されているため、追加の除算は不要）
                    if scoreType == .mate {
                        // 詰みの場合：30000 + 詰み手数（正の値）または -30000 - 詰み手数（負の値）
                        score = s > 0 ? (30000 + s) : (-30000 + s)
                    } else {
                        score = s
                    }
                }
                if !pvMoves.isEmpty {
                    pv = pvMoves
                }
                
                // NPSを抽出
                if let npsMatch = response.range(of: "nps (\\d+)", options: .regularExpression) {
                    let npsString = String(response[npsMatch]).replacingOccurrences(of: "nps ", with: "")
                    nps = Int(npsString)
                }
                
            default:
                break
            }
        }
        
        guard let move = bestMove else {
            throw EngineError.invalidSFEN("No bestmove in response")
        }
        
        return PositionEvaluation(
            score: score,
            bestMove: move,
            depth: depth,
            pv: pv.isEmpty ? [move] : pv,
            hasError: hasError,
            nps: nps
        )
    }
    
    /// MultiPV応答をパース
    private func parseMultiPVResponses(_ responses: [String]) throws -> [CandidateMove] {
        var candidates: [CandidateMove] = []
        var candidatesByMultiPV: [Int: (score: Int, pv: [String], depth: Int)] = [:]
        
        print("📊 [EngineCoordinator] MultiPV応答をパース中 - 応答数: \(responses.count)")
        
        var infoLineCount = 0
        for response in responses {
            // info行をカウント
            if response.hasPrefix("info") {
                infoLineCount += 1
                if infoLineCount <= 5 || infoLineCount % 10 == 0 {
                    print("📋 [EngineCoordinator] info[\(infoLineCount)]: \(response.prefix(100))...")
                }
            }
            
            // MultiPV番号を抽出
            var multiPVIndex = 1
            if let multiPVMatch = response.range(of: "multipv (\\d+)", options: .regularExpression) {
                let multiPVString = String(response[multiPVMatch]).replacingOccurrences(of: "multipv ", with: "")
                multiPVIndex = Int(multiPVString) ?? 1
            }
            
            let parsed = USIProtocolHandler.parseResponse(response)
            
            switch parsed {
            case .info(let d, let s, let scoreType, let pvMoves, _):
                // upperbound/lowerboundがある行はスキップ
                if response.contains("upperbound") || response.contains("lowerbound") {
                    continue
                }
                
                if let s = s, !pvMoves.isEmpty {
                    let score: Int
                    if scoreType == .mate {
                        // 詰みの場合：30000 + 詰み手数
                        score = s > 0 ? (30000 + s) : (-30000 + s)
                    } else {
                        score = s
                    }
                    
                    candidatesByMultiPV[multiPVIndex] = (score: score, pv: pvMoves, depth: d ?? 0)
                }
                
            default:
                break
            }
        }
        
        print("📊 [EngineCoordinator] info行の総数: \(infoLineCount), 候補手数: \(candidatesByMultiPV.count)")
        
        // MultiPV番号順にソートして候補手リストを作成
        // 候補手が0の場合は空の配列を返す
        guard candidatesByMultiPV.count > 0 else {
            print("⚠️ [EngineCoordinator] 候補手が見つかりませんでした")
            return candidates
        }
        
        for index in 1...candidatesByMultiPV.count {
            if let candidate = candidatesByMultiPV[index], !candidate.pv.isEmpty {
                candidates.append(CandidateMove(
                    moveUSI: candidate.pv[0],
                    score: candidate.score,
                    pv: candidate.pv,
                    depth: candidate.depth
                ))
            }
        }
        
        return candidates
    }
    
    /// 解析をキャンセル
    public func cancel() {
        isCancelled = true
        // TODO: 実際のエンジン停止処理
    }
    
    /// 現在の検索を停止
    public func stopCurrentSearch() {
        guard let bridge = bridge else { return }
        
        do {
            // USIプロトコルのstopコマンドを送信
            try bridge.sendCommand("stop")
            print("🛑 [EngineCoordinator] stopコマンド送信")
        } catch {
            print("⚠️ [EngineCoordinator] stopコマンド送信エラー: \(error)")
        }
    }
    
    /// エンジンをシャットダウン
    public func shutdown() {
        bridge?.stop()
        bridge = nil
        _ = stateManager.tryTransition(to: .notInitialized)
    }
    
    /// エンジンオプションを設定
    public func setOption(name: String, value: String) async throws {
        guard let bridge = bridge else {
            throw EngineError.engineNotReady
        }
        
        try bridge.sendCommand("setoption name \(name) value \(value)")
        
        // 短い待機時間
        try await Task.sleep(nanoseconds: 50_000_000) // 0.05秒
        
        print("⚙️ [EngineCoordinator] オプション設定: \(name)=\(value)")
    }
    
    /// エンジン設定を適用
    public func applyConfiguration(_ config: EngineConfiguration) async throws {
        try await setOption(name: "Threads", value: "\(config.threads)")
        try await setOption(name: "USI_Hash", value: "\(config.hashSize)")
        
        print("🔧 [EngineCoordinator] 設定適用: Threads=\(config.threads), Hash=\(config.hashSize)MB")
    }
    
    // MARK: - Private Helpers
    
    /// GameSettingsからスレッド数を取得
    private func getThreadCount() -> Int {
        // UserDefaultsから直接読み取り（GameSettingsへの依存を避けるため）
        if let savedValue = UserDefaults.standard.string(forKey: "engineThreadCount") {
            print("🔍 [EngineCoordinator] UserDefaultsからスレッド数取得: \(savedValue)")
            switch savedValue {
            case "two": return 2
            case "four": return 4
            case "six": return 6
            case "max": return ProcessInfo.processInfo.activeProcessorCount
            default: break
            }
        }
        
        // デフォルト値は上限（端末のコア数）
        let defaultCount = ProcessInfo.processInfo.activeProcessorCount
        print("🔍 [EngineCoordinator] デフォルトスレッド数を使用: \(defaultCount)")
        return defaultCount
    }
    
    /// GameSettingsからハッシュサイズを取得
    private func getHashSize() -> Int {
        // UserDefaultsから直接読み取り（GameSettingsへの依存を避けるため）
        if let savedValue = UserDefaults.standard.string(forKey: "engineHashSize") {
            let hashSize = Int(savedValue) ?? 512
            print("🔍 [EngineCoordinator] UserDefaultsからハッシュサイズ取得: \(hashSize)MB")
            return hashSize
        }
        
        // デフォルト値を計算（物理メモリに基づく）
        let physicalMemoryBytes = ProcessInfo.processInfo.physicalMemory
        let physicalMemoryGB = Double(physicalMemoryBytes) / 1_073_741_824.0
        
        let defaultHashSize: Int
        // 5GB以上: 1024MB
        if physicalMemoryGB >= 5.0 {
            defaultHashSize = 1024
        }
        // 3GB以上: 768MB
        else if physicalMemoryGB >= 3.0 {
            defaultHashSize = 768
        }
        // 3GB未満: 512MB
        else {
            defaultHashSize = 512
        }
        
        print("🔍 [EngineCoordinator] デフォルトハッシュサイズを使用: \(defaultHashSize)MB (物理メモリ: \(String(format: "%.1f", physicalMemoryGB))GB)")
        return defaultHashSize
    }
}

// MARK: - Supporting Types

public struct PositionEvaluation {
    public let score: Int
    public let bestMove: String
    public let depth: Int
    public let pv: [String]
    public let hasError: Bool
    public let nps: Int?  // Nodes per second
    
    public init(score: Int, bestMove: String, depth: Int, pv: [String], hasError: Bool = false, nps: Int? = nil) {
        self.score = score
        self.bestMove = bestMove
        self.depth = depth
        self.pv = pv
        self.hasError = hasError
        self.nps = nps
    }
}

public struct CandidateMove {
    public let moveUSI: String
    public let score: Int
    public let pv: [String]
    public let depth: Int
    
    public init(moveUSI: String, score: Int, pv: [String], depth: Int) {
        self.moveUSI = moveUSI
        self.score = score
        self.pv = pv
        self.depth = depth
    }
}

public struct EngineConfiguration {
    public let threads: Int
    public let hashSize: Int  // MB
    
    public static let foreground = EngineConfiguration(threads: 3, hashSize: 512)
    public static let background = EngineConfiguration(threads: 1, hashSize: 128)
    
    public init(threads: Int, hashSize: Int) {
        self.threads = threads
        self.hashSize = hashSize
    }
}


