//
//  NutPlayerViewFactory.swift
//  nut_player_ios
//
//  Created by Nut.Tech on 25.09.2023.
//

import Foundation
import Flutter
import NutPlayer

protocol NutPlayerViewFactoryDelegate: AnyObject {
    
    typealias Parameters = [String: Any]
    
    func playerInstance(for playerId: Int64) -> NutPlayer?
    func playerParams(for playerId: Int64) -> Parameters?
    
    @discardableResult
    func playerView(for playerId: Int64, view: UIView?) -> UIView?
    
    /// Call method in flutter side.
    func playerCall(for playerId: Int64, method: String, arguments: Parameters?)
}

final class NutPlayerViewFactory: NSObject, FlutterPlatformViewFactory {
    
    private var delegate: NutPlayerViewFactoryDelegate?
    private let messenger: FlutterBinaryMessenger
    
    init(with messenger: FlutterBinaryMessenger, delegate: NutPlayerViewFactoryDelegate?) {
        self.messenger = messenger
        self.delegate = delegate
        super.init()
    }
    
    func create(withFrame frame: CGRect,
                viewIdentifier viewId: Int64,
                arguments args: Any?) -> FlutterPlatformView {
        
        guard let delegate else {
            return PlayerFlutterPlatformView(withFrame: frame, viewIdentifier: viewId, view: UIView(frame: .zero))
        }
        
        let view: UIView
        let isSkinByDefault: Bool
        
        if let arguments = args as? [String: Any],
           let playerId = arguments["playerId"] as? Int64,
           let player = delegate.playerInstance(for: playerId) {
            
            if let view = delegate.playerView(for: playerId, view: nil) {
                return PlayerFlutterPlatformView(withFrame: frame, viewIdentifier: viewId, view: view)
            }
            
            let parameters = delegate.playerParams(for: playerId)
            
            let settings: NutPlayerSkinPlugin.Settings
            let namingOption: VideoQualityTitleFormat
            if let parameters {
                let namingString = parameters["qualityNaming"] as? String ?? ""
                namingOption = Self.mapToNamingFormatOption(string: namingString)
                isSkinByDefault = parameters["isSkinByDefault"] as? Bool ?? true
                settings = NutPlayerSkinPlugin.Settings(
                    onSettings: parameters["enableSettings"] as? Bool ?? false
                        ? { [weak delegate] in
                            guard let delegate else { return }
                            delegate.playerCall(for: playerId,
                                                method: "pluginSkinSettingsAction",
                                                arguments: nil)
                        } : nil,
                    onPip: parameters["enablePip"] as? Bool ?? false
                        ? { onEnter in
                            player.pipController.value >>- {
                            onEnter ? $0.start() : $0.stop()
                            }
                        } : nil,
                    onFullscreen: parameters["enableFullscreen"] as? Bool ?? false
                        ? { [weak delegate] onEnter in
                            guard let delegate else { return }
                            delegate.playerCall(for: playerId,
                                                method: "pluginSkinDefaultAction",
                                                arguments: ["onEnter": onEnter])
                        } : nil
                )
            } else {
                namingOption = .common
                isSkinByDefault = true
                settings = NutPlayerSkinPlugin.Settings()
            }

            var builder = NutPlayerViewBuilder()
            let qualityTitleMapper = DefaultQualityTitleMapper(policy: namingOption, localeLanguage: .rus)
            let qualitySettings = NutQualityPlugin.Settings(mapper: qualityTitleMapper)
            builder.attach(plugins: [NutPlayerViewBuilder.PluginRecord(plugin: NutQualityPlugin.self,
                                                                       priority: .userInitiated,
                                                                       settings: qualitySettings)])
            builder = isSkinByDefault
                ? builder.defaultSkin(settings: settings)
                : builder.defaultSkin(false)
            view = builder.build(with: player)
            view.translatesAutoresizingMaskIntoConstraints = true
            
            delegate.playerView(for: playerId, view: view)
        } else {
            view = UIView(frame: .zero)
            view.backgroundColor = .lightGray
        }
        
        return PlayerFlutterPlatformView(withFrame: frame, viewIdentifier: viewId, view: view)
    }
    
    /// Implementing this method is only necessary when the `arguments` in `createWithFrame` is not `nil`.
    public func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol {
          return FlutterStandardMessageCodec.sharedInstance()
    }
    
    private static func mapToNamingFormatOption(string: String) -> VideoQualityTitleFormat {
        switch string {
        case "common":
            return .common
        case "rus":
            return .rus
        case "eng":
            return .eng
        case "resolution":
            return .resolution
        default:
            return .common
        }
    }
    
}
