//
//  LNVideoPlayerView.swift
//  Gami
//
//  Created by OneeChan on 2026/3/2.
//

import Foundation
import UIKit
import AVKit
import SnapKit
import Combine


protocol LNVideoPlayerViewDelegate: AnyObject {
    func onVideoDidLoad(view: LNVideoPlayerView)
    func onVideoDidStart(view: LNVideoPlayerView)
    func onVideoDidStop(view: LNVideoPlayerView)
    func onVideoDidPause(view: LNVideoPlayerView)
    func onVideoMutedChanged(view: LNVideoPlayerView)
    func onVideoProgressChanged(view: LNVideoPlayerView, cur: Float64, total: Float64)
}
extension LNVideoPlayerViewDelegate {
    func onVideoDidLoad(view: LNVideoPlayerView) { }
    func onVideoDidStart(view: LNVideoPlayerView) { }
    func onVideoDidStop(view: LNVideoPlayerView) { }
    func onVideoDidPause(view: LNVideoPlayerView) { }
    func onVideoMutedChanged(view: LNVideoPlayerView) { }
    func onVideoProgressChanged(view: LNVideoPlayerView, cur: Float64, total: Float64) { }
}


class LNVideoPlayerView: UIView {
    static let updateInterval: Double = 0.05
    private let videoPlayer = AVPlayer()
    private let videoLayer = AVPlayerLayer()
    private let coverImageView = UIImageView()
    private let touchView = UIView()
    private let playButton = UIButton()
    private var timeObserver: Any?
    var playButtonSize: CGFloat = 42 {
        didSet {
            playButton.snp.updateConstraints { make in
                make.width.height.equalTo(playButtonSize)
            }
        }
    }
    var loop: Bool = false
    weak var delegate: LNVideoPlayerViewDelegate?
    
    private(set) var duration: TimeInterval = 0
    private(set) var videoSize: CGSize = .zero
    private(set) var curSource: String?
    var showCover: Bool = true {
        didSet {
            coverImageView.isHidden = !showCover
        }
    }
    var isPlaying: Bool {
        videoPlayer.rate > 0 && videoPlayer.error == nil && videoPlayer.currentItem?.status == .readyToPlay
    }
    var isMuted: Bool {
        videoPlayer.isMuted
    }
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        
        setupViews()
        setupObservsers()
    }
    
    func loadVideo(_ url: String, coverUrl: String?) {
        if curSource == url { return }
        curSource = url
        
        stop()
        guard let videoUrl = URL(string: url) else {
            duration = 0
            videoSize = .zero
            coverImageView.image = UIImage(systemName: "film")
            videoPlayer.replaceCurrentItem(with: nil)
            return
        }
        
        DispatchQueue.global().async { [weak self] in
            guard let self else { return }
            guard curSource == url else { return }
            
            let asset = AVAsset(url: videoUrl)
            let videoDuration = CMTimeGetSeconds(asset.duration)
            duration = TimeInterval(videoDuration)
            
            if let coverUrl {
                coverImageView.sd_setImage(with: URL(string: coverUrl))
            } else {
                let imageGenerator = AVAssetImageGenerator(asset: asset)
                imageGenerator.appliesPreferredTrackTransform = true
                let time = CMTimeMakeWithSeconds(0, preferredTimescale: 600)
                do {
                    let cgImage = try imageGenerator.copyCGImage(at: time, actualTime: nil)
                    let thumbnailImage = UIImage(cgImage: cgImage)
                    runOnMain {
                        self.coverImageView.image = thumbnailImage
                    }
                } catch {
                    runOnMain {
                        self.coverImageView.image = UIImage(systemName: "film")
                    }
                }
            }
            
            videoPlayer.pause()
            let item = AVPlayerItem(url: videoUrl)
            videoPlayer.replaceCurrentItem(with: item)
            
            guard let videoTrack = asset.tracks(withMediaType: .video).first else {
                return
            }
            let naturalSize = videoTrack.naturalSize
            
            let transform = videoTrack.preferredTransform
            videoSize = naturalSize
            if transform.a == 0 && transform.b == 1.0 && transform.c == -1.0 && transform.d == 0 {
                // 90度旋转
                videoSize = CGSize(width: naturalSize.height, height: naturalSize.width)
            } else if transform.a == 0 && transform.b == -1.0 && transform.c == 1.0 && transform.d == 0 {
                // 270度旋转
                videoSize = CGSize(width: naturalSize.height, height: naturalSize.width)
            }
            
            runOnMain { [weak self] in
                guard let self else { return }
                guard curSource == url else { return }
                delegate?.onVideoDidLoad(view: self)
            }
        }
    }
    
    func start() {
        videoPlayer.play()
        playButton.isHidden = true
        coverImageView.isHidden = true
        
        delegate?.onVideoDidStart(view: self)
    }
    
    func pause() {
        videoPlayer.pause()
        playButton.isHidden = false
        
        delegate?.onVideoDidPause(view: self)
    }
    
    func seekTo(_ to: Float, handler: ((Bool) -> Void)? = nil) {
        let time = CMTimeMakeWithSeconds(Float64(to), preferredTimescale: 1)
        videoPlayer.seek(to: time) { finish in
            handler?(finish)
        }
    }
    
    func stop() {
        videoPlayer.pause()
        videoPlayer.seek(to: .zero)
        coverImageView.isHidden = !showCover
        playButton.isHidden = false
        
        delegate?.onVideoDidStop(view: self)
    }
    
    func mute() {
        videoPlayer.isMuted = true
        delegate?.onVideoMutedChanged(view: self)
    }
    
    func unmute() {
        videoPlayer.isMuted = false
        delegate?.onVideoMutedChanged(view: self)
    }
    
    func setScaleMode(_ model: AVLayerVideoGravity) {
        videoLayer.videoGravity = model
        if model == .resizeAspect {
            coverImageView.contentMode = .scaleAspectFit
        } else {
            coverImageView.contentMode = .scaleAspectFill
        }
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension LNVideoPlayerView {
    private func setupViews() {
        backgroundColor = .black
        clipsToBounds = true
        
        videoLayer.player = videoPlayer
        videoLayer.videoGravity = .resizeAspect
        layer.addSublayer(videoLayer)
        publisher(for: \.bounds).removeDuplicates().sink { [weak self] newValue in
            guard let self else { return }
            videoLayer.frame = newValue
        }.store(in: &cancellables)
        
        touchView.onTap { [weak self] in
            guard let self else { return }
            if isPlaying {
                pause()
            } else {
                start()
            }
        }
        addSubview(touchView)
        touchView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        
        coverImageView.contentMode = .scaleAspectFit
        addSubview(coverImageView)
        coverImageView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        
        playButton.setBackgroundImage(.icVoicePlayWithBlackBg, for: .normal)
        playButton.addAction(UIAction(handler: { [weak self] _ in
            guard let self else { return }
            if isPlaying {
                pause()
            } else {
                start()
            }
        }), for: .touchUpInside)
        addSubview(playButton)
        playButton.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.width.height.equalTo(playButtonSize)
        }
    }
    
    private func setupObservsers() {
        let time = CMTime(seconds: Self.updateInterval, preferredTimescale: CMTimeScale(NSEC_PER_SEC))
        timeObserver = videoPlayer.addPeriodicTimeObserver(forInterval: time, queue: .main)
        { [weak self] currentTime in
            guard let self, let playerItem = videoPlayer.currentItem else { return }
            
            let currentSeconds = CMTimeGetSeconds(currentTime)
            let totalSeconds = CMTimeGetSeconds(playerItem.duration)
            guard !currentSeconds.isNaN, !totalSeconds.isNaN, totalSeconds > 0 else { return }
            
            if currentSeconds == totalSeconds {
                if loop {
                    // 循环播放
                    videoPlayer.pause()
                    videoPlayer.seek(to: .zero)
                    videoPlayer.play()
                } else {
                    // 自动停止
                    stop()
                }
            } else {
                delegate?.onVideoProgressChanged(view: self, cur: currentSeconds, total: totalSeconds)
            }
        }
    }
}
