//
// Created by Scott Moon on 2019-06-13.
// Copyright (c) 2019 Scott Moon. All rights reserved.
//

import XCTest
import UIKit
import AVKit
import MediaPlayer

@testable import PodCast_MVC

class PlayerDetailControllerSpec: XCTestCase {
  var sut: PlayerDetailController?

  override func setUp() {
    super.setUp()
  }

  override func tearDown() {
    super.tearDown()
    sut = nil
  }

  func test_init() {
    sut = PlayerDetailController()
    XCTAssertNotNil(sut)
  }

  func test_playStartCallBack() {
    sut = PlayerDetailController()
    sut?.startPlayCallBack()
    XCTAssertNotNil(sut)
  }

  func test_updatedPlayTimeCallBack() {
    sut = PlayerDetailController()
    sut?.updatedPlayTimeCallBack(CMTime(seconds: 1, preferredTimescale: 1))
  }

  func test_inject_inCorrect_viewCurrentTimeSliderValueCallback() {
    guard let url = Bundle.main.url(forResource: "MusicDummy", withExtension: "mp3") else {
      fatalError("Should be not nil")
    }
    let slider = UISlider()
    slider.value = 0.123

    let playerItem = AVPlayerItem(url: url)
    sut = PlayerDetailController()
    sut?.player.replaceCurrentItem(with: playerItem)

    sut?.handleCurrentTimesSlider(slider: slider)
  }

  func test_animatedDuringTransformCallback() {
    sut = PlayerDetailController()
    sut?.animatedDuringTransformCallback(.maximizeScreen, -123.123)
    sut?.animatedDuringTransformCallback(.minimizeScreen, -123.123)
    XCTAssertNotNil(sut)
  }

  func test_animatedFinishTransformCallback() {
    sut = PlayerDetailController()
    sut?.animatedFinishTransformCallback(.maximizeScreen, -400, -1_000)
    sut?.animatedFinishTransformCallback(.maximizeScreen, 400, 1_000)
    sut?.animatedFinishTransformCallback(.minimizeScreen, -400, -1_000)
    sut?.animatedFinishTransformCallback(.minimizeScreen, 0, 0)
    XCTAssertNotNil(sut)
  }

  func test_updatedGestureAnimation() {
    sut = PlayerDetailController()

    sut?.updatedGestureAnimation(screenStatus: .maximizeScreen, gestureState: .began, translationY: 200, velocityY: 0)
    sut?.updatedGestureAnimation(screenStatus: .maximizeScreen, gestureState: .changed, translationY: 100, velocityY: -100.0)
    sut?.updatedGestureAnimation(screenStatus: .maximizeScreen, gestureState: .ended, translationY: 200, velocityY: 0)
    sut?.updatedGestureAnimation(screenStatus: .maximizeScreen, gestureState: .cancelled, translationY: 200, velocityY: 0)
    XCTAssertNotNil(sut)
  }

  func test_handleVolumeSlider() {
    let expectedValue: Float = 0.5
    let slider = UISlider()
    slider.value = expectedValue

    sut = PlayerDetailController()
    sut?.handleVolumeSlider(slider: slider)
    XCTAssertEqual(expectedValue, sut?.player.volume)
  }

  func test_handleRewind() {
    sut = PlayerDetailController()
    sut?.handleRewind()
    XCTAssertNotNil(sut)
  }

  func test_handleFastForward() {
    sut = PlayerDetailController()
    sut?.handleFastForward()
    XCTAssertNotNil(sut)
  }

  func test_handleDismiss() {
    let dummyView = UIView()
    let rootView = MainTabBarController()
    let window = UIApplication.shared.windows.first
    window?.rootViewController = rootView

    sut = PlayerDetailController()

    guard let sut = sut else {
      fatalError("Should be not nil")
    }
    dummyView.addSubview(sut)

    sut.handleDismiss()
    XCTAssertNotNil(sut)
  }

  func test_handleTapMaximize() {
    let dummyView = UIView()
    let rootView = MainTabBarController()
    let window = UIApplication.shared.windows.first
    window?.rootViewController = rootView

    sut = PlayerDetailController()

    guard let sut = sut else {
      fatalError("Should be not nil")
    }
    dummyView.addSubview(sut)

    sut.handleTapMaximize()
    XCTAssertNotNil(sut)
  }

  func test_inject_blankString_playEpisode() {
    let expectedPlayerStatus: AVPlayer.TimeControlStatus = .paused
    let episode = Episode(title: "", pubDate: Date(), description: "", author: "", imageUrl: nil, streamUrl: "")
    sut = PlayerDetailController()
    sut?.episode = episode
    XCTAssertNotNil(sut)
    XCTAssertEqual(sut?.player.timeControlStatus, expectedPlayerStatus)
  }

  func test_inject_correct_playEpisode() {
    let expectedPlayerStatus: AVPlayer.TimeControlStatus = .playing
    let episode = Episode(title: "", pubDate: Date(), description: "", author: "", imageUrl: nil, streamUrl: "https://test-audio.com")
    sut = PlayerDetailController()
    sut?.episode = episode
    XCTAssertNotNil(sut)
    XCTAssertEqual(sut?.player.timeControlStatus, expectedPlayerStatus)
  }

  func test_handlePlayPause() {
    let expectedPlayerStatus: AVPlayer.TimeControlStatus = .paused
    sut = PlayerDetailController()

    sut?.player.play()
    sut?.handlePlayPause()

    XCTAssertEqual(expectedPlayerStatus, sut?.player.timeControlStatus)

    sut?.handlePlayPause()
    XCTAssertNotEqual(expectedPlayerStatus, sut?.player.timeControlStatus)
  }

  func test_handlePlay() {
    let expectedPlayerStatus: AVPlayer.TimeControlStatus = .paused
    sut = PlayerDetailController()
    sut?.handlePlay()
    XCTAssertNotEqual(expectedPlayerStatus, sut?.player.timeControlStatus)
  }

  func test_handlePause() {
    let expectedPlayerStatus: AVPlayer.TimeControlStatus = .paused
    sut = PlayerDetailController()
    sut?.handlePause()
    XCTAssertEqual(expectedPlayerStatus, sut?.player.timeControlStatus)
  }

  func test_handleMinMaximizePan() {
    let gesture = UIPanGestureRecognizer()
    sut = PlayerDetailController()
    sut?.handleMinMaximizePan(gesture: gesture)
    XCTAssertNotNil(sut)
  }

  func test_handleDismissalPan() {
    let gesture = UIPanGestureRecognizer()
    sut = PlayerDetailController()
    sut?.handleDismissalPan(gesture: gesture)
    XCTAssertNotNil(sut)
  }

  func test_correct_handleNextTrack() {
    let expectedEpisode = Episode(title: "test 2", pubDate: Date(), description: "test description", author: "test author", imageUrl: "https://test-image.com", streamUrl: "https://test-audio.com")
    sut = PlayerDetailController()
    sut?.handleNextTrack()
    sut?.playListEpisodes = getPlayListEpisodes()
    guard let currentEpisode = getPlayListEpisodes().first else {
      fatalError("Should be not nil")
    }
    sut?.episode = currentEpisode
    sut?.handleNextTrack()

    XCTAssertEqual(expectedEpisode.title, sut?.episode?.title)
    XCTAssertEqual(expectedEpisode.author, sut?.episode?.author)
  }

  func test_inject_notFoundIndex_handleNextTrack() {
    let episode = Episode(title: "test 4", pubDate: Date(), description: "test description", author: "test author", imageUrl: "https://test-image.com", streamUrl: "https://test-audio.com")
    sut = PlayerDetailController()
    sut?.playListEpisodes = getPlayListEpisodes()
    sut?.episode = episode
    sut?.handleNextTrack()
  }

  func test_inject_correct_handlePreviousTrack() {
    let expectedEpisode = Episode(title: "test 1", pubDate: Date(), description: "test description", author: "test author", imageUrl: "https://test-image.com", streamUrl: "https://test-audio.com")
    sut = PlayerDetailController()
    sut?.handlePrevTrack()
    sut?.playListEpisodes = getPlayListEpisodes()
    guard let currentEpisode = getPlayListEpisodes()[safe: 1] else {
      fatalError("Should be not nil")
    }
    sut?.episode = currentEpisode
    sut?.handlePrevTrack()

    XCTAssertEqual(expectedEpisode.title, sut?.episode?.title)
    XCTAssertEqual(expectedEpisode.author, sut?.episode?.author)
  }

  func test_inject_handlePreviousTrack() {
    let episode = Episode(title: "test 4", pubDate: Date(), description: "test description", author: "test author", imageUrl: "https://test-image.com", streamUrl: "https://test-audio.com")
    sut = PlayerDetailController()
    sut?.playListEpisodes = getPlayListEpisodes()
    sut?.episode = episode
    sut?.handlePrevTrack()
    XCTAssertNotNil(sut)
  }

  func test_inject_guardBreakNil_handleInterruption() {

    let inCorrectedTypeUserInfo = [
      AVAudioSessionInterruptionTypeKey: "inCorrect"
    ]
    let inCorrectedValueUserInfo: [String: UInt] = [
      AVAudioSessionInterruptionTypeKey: 1_000
    ]
    let emptyNotification = Notification(name: Notification.Name(rawValue: "test notification"), object: nil)
    let inCorrectedTypeNotification = Notification(name: Notification.Name(rawValue: "test notification"), object: nil, userInfo: inCorrectedTypeUserInfo)
    let inCorrectedValueNotification = Notification(name: Notification.Name(rawValue: "test notification"), object: nil, userInfo: inCorrectedValueUserInfo)

    sut = PlayerDetailController()
    sut?.handleInterruption(notification: emptyNotification)
    sut?.handleInterruption(notification: inCorrectedTypeNotification)
    sut?.handleInterruption(notification: inCorrectedValueNotification)

    XCTAssertNotNil(sut)
  }

  func test_inject_correct_handleInterruption() {

    let beganUserInfo: [String: UInt] = [
      AVAudioSessionInterruptionTypeKey: AVAudioSession.InterruptionType.began.rawValue
    ]
    let beganNotification = Notification(name: Notification.Name(rawValue: "test notification"), object: nil, userInfo: beganUserInfo)

    let endedUserInfo: [String: UInt] = [
      AVAudioSessionInterruptionTypeKey: AVAudioSession.InterruptionType.ended.rawValue,
      AVAudioSessionInterruptionOptionKey: AVAudioSession.InterruptionOptions.shouldResume.rawValue
    ]
    let endedNotification = Notification(name: Notification.Name(rawValue: "test notification"), object: nil, userInfo: endedUserInfo)

    sut = PlayerDetailController()
    sut?.handleInterruption(notification: beganNotification)
    sut?.handleInterruption(notification: endedNotification)

    XCTAssertNotNil(sut)
  }

  func test_setupLockScreenDuration() {
    guard let url = Bundle.main.url(forResource: "MusicDummy", withExtension: "mp3") else {
      fatalError("Should be not nil")
    }
    let playerItem = AVPlayerItem(url: url)
    sut = PlayerDetailController()
    sut?.player.replaceCurrentItem(with: playerItem)
    sut?.setupLockScreenDuration()

    XCTAssertNotNil(sut)
  }

  func getPlayListEpisodes() -> [Episode] {
    return [
      Episode(title: "test 1", pubDate: Date(), description: "test description", author: "test author", imageUrl: "https://test-image.com", streamUrl: "https://test-audio.com"),
      Episode(title: "test 2", pubDate: Date(), description: "test description", author: "test author", imageUrl: "https://test-image.com", streamUrl: "https://test-audio.com"),
      Episode(title: "test 3", pubDate: Date(), description: "test description", author: "test author", imageUrl: "https://test-image.com", streamUrl: "https://test-audio.com")
    ]
  }
}
