import Foundation

private enum EventType: String {
    case content = "content"
    case toolCall = "tool_call"
    case finished = "finished"
    case error = "error"
    
    static func fromValue(_ value: String?) -> EventType? {
        guard let value = value else { return nil }
        return EventType(rawValue: value)
    }
}

public enum ToolName: String {
    case writeLyrics = "write_lyrics"
    case generateSong = "generate_song"
    
    public static func fromValue(_ value: String?) -> ToolName? {
        guard let value = value else { return nil }
        return ToolName(rawValue: value)
    }
}

/// Parser for Orpheus real-time messaging events.
/// Handles deserialization and transformation of raw event data into domain events.
public struct OrpheusModelParser {
    private let decoder: JSONDecoder
    
    public init(decoder: JSONDecoder = JSONDecoder()) {
        self.decoder = decoder
    }
    
    /// Parses a message event string into an OrpheusRealtimeEvent
    public func parseMessageString(_ messageString: String) -> OrpheusRealtimeEvent? {
        guard let data = messageString.data(using: .utf8) else {
            print("❌ Failed to parse message event data: \(messageString)")
            return nil
        }
        
        do {
            let messageEvent = try decoder.decode(OrpheusMessageEvent.self, from: data)
            return parseMessageEvent(messageEvent)
        } catch {
            print("❌ Failed to parse message event data: \(messageString), error: \(error)")
            return nil
        }
    }
    
    private func parseMessageEvent(_ event: OrpheusMessageEvent) -> OrpheusRealtimeEvent? {
        let eventData = event.data
        guard let messageId = eventData.messageId else {
            print("⚠️ orpheus-message missing message_id, eventData: \(eventData)")
            return nil
        }
        
        print("📡 Parsing orpheus-message: type=\(eventData.type ?? "nil"), messageId=\(messageId)")
        
        let eventType = EventType.fromValue(eventData.type)
        switch eventType {
        case .content:
            return .messageContent(
                messageId: messageId,
                content: eventData.content ?? "",
                userId: eventData.userId
            )
            
        case .toolCall:
            return parseToolCallFromMessage(
                messageId: messageId,
                toolCallJson: eventData.toolCall
            )
            
        case .finished:
            return .messageFinished(
                messageId: messageId,
                reason: eventData.reason
            )
            
        case .error:
            return .messageError(
                messageId: messageId,
                error: eventData.error ?? "Unknown error"
            )
            
        case .none:
            print("📡 Ignoring orpheus-message with unknown type: \(eventData.type ?? "nil")")
            return nil
        }
    }
    
    private func parseToolCallFromMessage(
        messageId: String,
        toolCallJson: JSONValue?
    ) -> OrpheusRealtimeEvent? {
        guard case .object(let toolCallDict) = toolCallJson else {
            print("⚠️ orpheus-message type=tool_call has invalid toolCall format")
            return nil
        }
        
        let toolCallId = toolCallDict["id"]?.toString()
        let function = toolCallDict["function"]
        
        var toolCallName: String?
        var toolCallArguments: String?
        
        if case .object(let functionDict) = function {
            toolCallName = functionDict["name"]?.toString()
            toolCallArguments = functionDict["arguments"]?.toString()
        }
        
        return .messageToolCall(
            messageId: messageId,
            toolCallId: toolCallId,
            toolCallName: toolCallName,
            toolCallArguments: toolCallArguments
        )
    }
    
    /// Parses a tool call event string into an OrpheusRealtimeEvent
    public func parseToolString(_ toolCallString: String) -> OrpheusRealtimeEvent? {
        guard let data = toolCallString.data(using: .utf8) else {
            print("❌ Failed to parse tool call event data: \(toolCallString)")
            return nil
        }
        
        do {
            let toolCallEvent = try decoder.decode(OrpheusToolCallEvent.self, from: data)
            return parseToolCallEvent(toolCallEvent)
        } catch {
            print("❌ Failed to parse tool call event data: \(toolCallString), error: \(error)")
            return nil
        }
    }
    
    private func parseToolCallEvent(_ eventData: OrpheusToolCallEvent) -> OrpheusRealtimeEvent? {
        guard let toolCallId = eventData.toolCallId else {
            return nil
        }
        
        let toolCallData = eventData.data
        let eventType = EventType.fromValue(toolCallData?.type)
        let toolCallName = eventData.toolCallName ?? "unknown"
        
        switch eventType {
        case .content:
            guard let toolCallData = toolCallData else { return nil }
            return parseToolCallContent(
                toolCallId: toolCallId,
                toolCallName: toolCallName,
                toolCallData: toolCallData
            )
            
        case .error:
            guard let toolCallData = toolCallData else { return nil }
            return parseToolCallFailure(
                toolCallId: toolCallId,
                toolCallName: toolCallName,
                toolCallData: toolCallData
            )
            
        case .finished:
            print("📡 Tool call finished: \(toolCallId)")
            return .toolCallContent(
                toolCallId: toolCallId,
                toolCallName: toolCallName,
                content: ""
            )
            
        case .toolCall, .none:
            print("📡 Ignoring tool call type: \(toolCallData?.type ?? "nil")")
            return nil
        }
    }
    
    private func parseToolCallContent(
        toolCallId: String,
        toolCallName: String,
        toolCallData: OrpheusToolCallData
    ) -> OrpheusRealtimeEvent? {
        let toolName = ToolName.fromValue(toolCallName)
        
        switch toolName {
        case .generateSong:
            return parseGenerateSongContent(
                toolCallId: toolCallId,
                toolCallName: toolCallName,
                toolCallData: toolCallData
            )
            
        case .writeLyrics:
            return parseLyricsContent(
                toolCallId: toolCallId,
                toolCallName: toolCallName,
                toolCallData: toolCallData
            )
            
        case .none:
            print("📡 Ignoring unsupported tool: \(toolCallName)")
            return nil
        }
    }
    
    private func parseToolCallFailure(
        toolCallId: String,
        toolCallName: String,
        toolCallData: OrpheusToolCallData
    ) -> OrpheusRealtimeEvent {
        let errorMessage: String
        if let content = toolCallData.content {
            errorMessage = content.toString()
        } else {
            errorMessage = "Unknown error"
        }
        
        print("⚠️ Tool call failed: \(toolCallName) - \(errorMessage)")
        return .toolCallFailure(
            toolCallId: toolCallId,
            toolCallName: toolCallName,
            errorMessage: errorMessage
        )
    }
    
    private func parseLyricsContent(
        toolCallId: String,
        toolCallName: String,
        toolCallData: OrpheusToolCallData
    ) -> OrpheusRealtimeEvent {
        let contentString: String
        if let content = toolCallData.content {
            contentString = content.toString()
        } else {
            contentString = ""
        }
        
        return .toolCallContent(
            toolCallId: toolCallId,
            toolCallName: toolCallName,
            content: contentString
        )
    }
    
    private func parseGenerateSongContent(
        toolCallId: String,
        toolCallName: String,
        toolCallData: OrpheusToolCallData
    ) -> OrpheusRealtimeEvent? {
        guard let content = toolCallData.content,
              case .object = content else {
            let errorMsg = toolCallData.content?.toString() ?? "Invalid content format"
            return .toolCallFailure(
                toolCallId: toolCallId,
                toolCallName: toolCallName,
                errorMessage: errorMsg
            )
        }
        
        // Convert JSONValue.object to JSON and decode as GenerateSongContent
        do {
            // Convert the dictionary to JSON data
            let jsonData = try JSONSerialization.data(withJSONObject: convertJSONValueToAny(content))
            let generateSongContent = try decoder.decode(GenerateSongContent.self, from: jsonData)
            
            return .toolCallResult(
                toolCallId: toolCallId,
                toolCallName: toolCallName,
                clipIds: generateSongContent.clipIds
            )
        } catch {
            print("❌ Error decoding generate_song content: \(error)")
            return .error(error)
        }
    }
    
    private func convertJSONValueToAny(_ value: JSONValue) -> Any {
        switch value {
        case .string(let str):
            return str
        case .number(let num):
            return num
        case .bool(let bool):
            return bool
        case .object(let dict):
            return dict.mapValues { convertJSONValueToAny($0) }
        case .array(let arr):
            return arr.map { convertJSONValueToAny($0) }
        case .null:
            return NSNull()
        }
    }
}
