import Foundation
import BlazeSDK

extension [String: AnyHashable] {
    
    var toDataSourceType: BlazeDataSourceType? {
        var datasourceType: BlazeDataSourceType? = nil
        
        if let type = self["type"] as? String, type == "search",
           let searchText = self["searchText"] as? String {
            let maxItems = self["maxItems"] as? Int
            let labels = self["labels"]?.asBlazeWidgetLabel
            datasourceType = .search(searchText: searchText, maxItems: maxItems, labels: labels)
        } else if let widgetLabel = self["labels"]?.asBlazeWidgetLabel {
            
            let orderType = self["orderType"]?.asOrderType
            let maxItems = self["maxItems"] as? Int
            let advancedOrderType = self["advancedOrderType"]?.asAdvancedOrderType
            let personalizedType = self["personalizedType"]?.asPersonalizedType
            
            let labelsPriority: [BlazeWidgetLabel]?
            if let labelsPriorityArray = self["labelsPriority"] as? [AnyHashable] {
                labelsPriority = labelsPriorityArray.compactMap({ item in
                    return item.asBlazeWidgetLabel
                })
            } else {
                labelsPriority = nil
            }
            
            datasourceType = .labels(widgetLabel,
                                     labelsPriority: labelsPriority,
                                     orderType: orderType,
                                     advancedOrderType: advancedOrderType,
                                     maxItems: maxItems,
                                     personalizedType: personalizedType)
        } else if let idsDataSource = self["ids"] as? [String] {
            let orderType = self["orderType"]?.asOrderType
            let advancedOrderType = self["advancedOrderType"]?.asAdvancedOrderType
            datasourceType = .ids(idsDataSource,
                                  orderType: orderType,
                                  advancedOrderType: advancedOrderType)
            
        } else if let recommendationsDataSource = self["recommendationsType"]?.asBlazeRecommendations {
            datasourceType = .recommendations(recommendationsDataSource)
        }
        
        return datasourceType
    }
    
    var toReactWidgetLayout: BlazeReactWidgetLayout? {
        var result: BlazeReactWidgetLayout?
        
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: self, options: [])
            result = try JSONDecoder().decode(BlazeReactWidgetLayout.self, from: jsonData)
        } catch {
            blazeLogDebug("Error decoding JSON: \(error)")
        }
        
        return result
    }
    
    var toReactStoryPlayerStyle: BlazeReactStoryPlayerStyle? {
        var result: BlazeReactStoryPlayerStyle?
        
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: self, options: [])
            result = try JSONDecoder().decode(BlazeReactStoryPlayerStyle.self, from: jsonData)
        } catch {
            blazeLogDebug("Error decoding JSON: \(error)")
        }
        
        return result

    }

    var toReactMomentsPlayerStyle: BlazeReactMomentsPlayerStyle? {
        var result: BlazeReactMomentsPlayerStyle?
        
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: self, options: [])
            result = try JSONDecoder().decode(BlazeReactMomentsPlayerStyle.self, from: jsonData)
        } catch {
            blazeLogDebug("Error decoding JSON: \(error)")
        }
        
        return result

    }
    
    var toReactVideosPlayerStyle: BlazeReactVideosPlayerStyle? {
        var result: BlazeReactVideosPlayerStyle?
        
        do {
            let jsonData = try JSONSerialization.data(withJSONObject: self, options: [])
            result = try JSONDecoder().decode(BlazeReactVideosPlayerStyle.self, from: jsonData)
        } catch {
            blazeLogDebug("Error decoding JSON: \(error)")
        }
        
        return result

    }

}

extension AnyHashable {
    
    var asOrderType: BlazeOrderType? {
        guard let orderTypeString = self as? String else { return nil }
        
        switch orderTypeString {
        case "manual":
            return .manual
        case "recentlyUpdatedFirst":
            return .recentlyUpdatedFirst
        case "recentlyUpdatedLast":
            return .recentlyUpdatedLast
        case "aToZ":
            return .aToZ
        case "zToA":
            return .zToA
        case "recentlyCreatedFirst":
            return .recentlyCreatedFirst
        case "recentlyCreatedLast":
            return .recentlyCreatedLast
        case "random":
            return .random
        default:
            return nil
        }
    }
    
    var asAdvancedOrderType: BlazeAdvancedOrderType? {
        guard let orderTypeString = self as? String else { return nil }
        
        switch orderTypeString {
        case "LiveFirst":
            return .liveFirst
        default:
            return nil
        }
    }
    
    var asPersonalizedType: BlazeDataSourcePersonalizedType? {
        guard let rawObject = self as? [String : AnyHashable],
              let type = rawObject["type"] as? String else {
            return nil
        }
        
        switch type {
        case "Ids":
            
            let idsDictRaw = rawObject["ids"] as? [String: [String]] ?? [:]
            let idsDict: [BlazeDataSourcePersonalizedType.ContentType: [String]] = Dictionary(
                uniqueKeysWithValues: idsDictRaw.compactMap { key, value in
                    guard let contentType = key.asPersonalizedTypeContentType else {
                        return nil
                    }
                    return (contentType, value)
                }
            )
            
            return .ids(idsDict)
        case "Labels":
            
            guard let labelsFilter = rawObject["labelsFilter"]?.asBlazeWidgetLabel else {
                return nil
            }
            
            let labelsPriority: [BlazeWidgetLabel]?
            if let labelsPriorityArray = rawObject["labelsPriority"] as? [AnyHashable] {
                labelsPriority = labelsPriorityArray.compactMap({ item in
                    return item.asBlazeWidgetLabel
                })
            } else {
                labelsPriority = nil
            }
            
            return .labels(labelsFilter: labelsFilter,
                           labelsPriority: labelsPriority ?? [])
        default:
            return nil
        }
    }
    
}

extension AnyHashable {
    
    var asBlazeWidgetLabel: BlazeWidgetLabel? {
        guard let labelDataSource = self as? [String: Any],
              let labelValue = labelDataSource["value"] as? String else {
                  return nil
        }
        
        return .singleLabel(labelValue)
    }
    
    var asBlazeRecommendations: BlazeRecommendationsType? {
        guard let recommendationsDataSource = self as? [String: Any],
              let type = recommendationsDataSource["type"] as? String else {
            return nil
        }

        let anyLabelFilter = recommendationsDataSource["anyLabelFilter"] as? [String] ?? []
        let promotedLabels = recommendationsDataSource["promotedLabels"] as? [String] ?? []

        return BlazeRecommendationsType.from(type: type, anyLabelFilter: anyLabelFilter, promotedLabels: promotedLabels)
    }
}

extension BlazeRecommendationsType {
    static func from(type: String, anyLabelFilter: [String], promotedLabels: [String]) -> BlazeRecommendationsType? {
        switch type {
        case "ForYou":
            return .forYou(anyLabelFilter: anyLabelFilter, promotedLabels: promotedLabels)
        case "Trending":
            // We support promoted labels only for ForYou
            return .trending(anyLabelFilter: anyLabelFilter)
        default:
            return nil
        }
    }
}

extension Dictionary where Key == String, Value == AnyHashable {
    /// Transforms the dictionary into a map of BlazeReactWidgetItemCustomMapping to BlazeReactWidgetStylesOverrides
    var toReactWidgetStylesOverridesMap: [BlazeReactWidgetItemCustomMapping: BlazeReactWidgetStylesOverrides]? {
        var resultMap = [BlazeReactWidgetItemCustomMapping: BlazeReactWidgetStylesOverrides]()

        // Iterate through each key-value pair in the dictionary
        for (key, value) in self {
            // Attempt to decode the key into BlazeReactWidgetItemCustomMapping
            guard let keyData = key.data(using: .utf8),
                  let keyValue = try? JSONDecoder().decode(BlazeReactWidgetItemCustomMapping.self, from: keyData) else {
                continue
            }

            // Attempt to serialize the value dictionary and decode it into BlazeReactWidgetStylesOverrides
            guard let valueDict = value as? [String: AnyHashable],
                  let valueData = try? JSONSerialization.data(withJSONObject: valueDict, options: []),
                  let valueValue = try? JSONDecoder().decode(BlazeReactWidgetStylesOverrides.self, from: valueData) else {
                continue
            }

            // Add to the result map
            resultMap[keyValue] = valueValue
        }

        return resultMap.isEmpty ? nil : resultMap
    }
}

extension String {
    var asCachingLevel: BlazeCachePolicyLevel? {
        switch self {
        case "LOW":
            return .Low
        case "DEFAULT":
            return .Default
        case "HIGH":
            return .High
        case "EXTREME":
            return .Extreme
        default:
            return nil
        }
    }
    
    var asPersonalizedTypeContentType: BlazeDataSourcePersonalizedType.ContentType? {
        switch self {
        case "players":
            return .players
        case "teams":
            return .teams
        default:
            return nil
        }
    }
}

extension String {
    
    var asWidgetLayoutPreset: BlazeWidgetLayout? {
        switch self {
            
            // Stories Row
        case "StoriesWidget.Row.circles":
            return .Presets.StoriesWidget.Row.circles
        case "StoriesWidget.Row.verticalRectangles":
            return .Presets.StoriesWidget.Row.verticalRectangles
        case "StoriesWidget.Row.horizontalRectangles":
            return .Presets.StoriesWidget.Row.horizontalRectangles
        case "StoriesWidget.Row.singleItemVerticalRectangle":
            return .Presets.StoriesWidget.Row.singleItemVerticalRectangle
        case "StoriesWidget.Row.singleItemHorizontalRectangle":
            return .Presets.StoriesWidget.Row.singleItemHorizontalRectangle
            
            // Stories Grid
        case "StoriesWidget.Grid.oneColumnHorizontalRectangles":
            return .Presets.StoriesWidget.Grid.oneColumnHorizontalRectangles
        case "StoriesWidget.Grid.twoColumnsVerticalRectangles":
            return .Presets.StoriesWidget.Grid.twoColumnsVerticalRectangles
        case "StoriesWidget.Grid.twoColumnsHorizontalRectangles":
            return .Presets.StoriesWidget.Grid.twoColumnsHorizontalRectangles
        case "StoriesWidget.Grid.threeColumnsVerticalRectangles":
            return .Presets.StoriesWidget.Grid.threeColumnsVerticalRectangles
        case "StoriesWidget.Grid.threeColumnsHorizontalRectangles":
            return .Presets.StoriesWidget.Grid.threeColumnsHorizontalRectangles
        case "StoriesWidget.Grid.singleItemVerticalRectangle":
            return .Presets.StoriesWidget.Grid.singleItemVerticalRectangle
        case "StoriesWidget.Grid.singleItemHorizontalRectangle":
            return .Presets.StoriesWidget.Grid.singleItemHorizontalRectangle
            
            // Moments Row
        case "MomentsWidget.Row.verticalRectangles":
            return .Presets.MomentsWidget.Row.verticalRectangles
        case "MomentsWidget.Row.horizontalRectangles":
            return .Presets.MomentsWidget.Row.horizontalRectangles
        case "MomentsWidget.Row.singleItemVerticalRectangle":
            return .Presets.MomentsWidget.Row.singleItemVerticalRectangle
        case "MomentsWidget.Row.singleItemHorizontalRectangle":
            return .Presets.MomentsWidget.Row.singleItemHorizontalRectangle
        case "MomentsWidget.Row.verticalAnimatedThumbnailsRectangles":
            return .Presets.MomentsWidget.Row.verticalAnimatedThumbnailsRectangles
            
            // Moments Grid
        case "MomentsWidget.Grid.oneColumnHorizontalRectangles":
            return .Presets.MomentsWidget.Grid.oneColumnHorizontalRectangles
        case "MomentsWidget.Grid.twoColumnsVerticalRectangles":
            return .Presets.MomentsWidget.Grid.twoColumnsVerticalRectangles
        case "MomentsWidget.Grid.twoColumnsHorizontalRectangles":
            return .Presets.MomentsWidget.Grid.twoColumnsHorizontalRectangles
        case "MomentsWidget.Grid.threeColumnsVerticalRectangles":
            return .Presets.MomentsWidget.Grid.threeColumnsVerticalRectangles
        case "MomentsWidget.Grid.threeColumnsHorizontalRectangles":
            return .Presets.MomentsWidget.Grid.threeColumnsHorizontalRectangles
        case "MomentsWidget.Grid.singleItemVerticalRectangle":
            return .Presets.MomentsWidget.Grid.singleItemVerticalRectangle
        case "MomentsWidget.Grid.singleItemHorizontalRectangle":
            return .Presets.MomentsWidget.Grid.singleItemHorizontalRectangle
            
            // Videos Row
        case "VideosWidget.Row.verticalRectangles":
            return .Presets.VideosWidget.Row.verticalRectangles
        case "VideosWidget.Row.horizontalRectangles":
            return .Presets.VideosWidget.Row.horizontalRectangles
        case "VideosWidget.Row.singleItemVerticalRectangle":
            return .Presets.VideosWidget.Row.singleItemVerticalRectangle
        case "VideosWidget.Row.singleItemHorizontalRectangle":
            return .Presets.VideosWidget.Row.singleItemHorizontalRectangle
        case "VideosWidget.Row.verticalAnimatedThumbnailsRectangles":
            return .Presets.VideosWidget.Row.verticalAnimatedThumbnailsRectangles
            
            // Videos Grid
        case "VideosWidget.Grid.oneColumnHorizontalRectangles":
            return .Presets.VideosWidget.Grid.oneColumnHorizontalRectangles
        case "VideosWidget.Grid.twoColumnsVerticalRectangles":
            return .Presets.VideosWidget.Grid.twoColumnsVerticalRectangles
        case "VideosWidget.Grid.twoColumnsHorizontalRectangles":
            return .Presets.VideosWidget.Grid.twoColumnsHorizontalRectangles
        case "VideosWidget.Grid.threeColumnsVerticalRectangles":
            return .Presets.VideosWidget.Grid.threeColumnsVerticalRectangles
        case "VideosWidget.Grid.threeColumnsHorizontalRectangles":
            return .Presets.VideosWidget.Grid.threeColumnsHorizontalRectangles
        case "VideosWidget.Grid.singleItemVerticalRectangle":
            return .Presets.VideosWidget.Grid.singleItemVerticalRectangle
        case "VideosWidget.Grid.singleItemHorizontalRectangle":
            return .Presets.VideosWidget.Grid.singleItemHorizontalRectangle
        default:
            return nil
        }
    }
}
