package net.taehui.twilight.system

import net.taehui.twilight.Logger
import org.openqa.selenium.By
import org.openqa.selenium.TimeoutException
import org.openqa.selenium.WebDriver
import org.openqa.selenium.edge.EdgeDriver
import org.openqa.selenium.edge.EdgeOptions
import org.openqa.selenium.support.ui.ExpectedConditions
import org.openqa.selenium.support.ui.WebDriverWait
import java.time.Duration
import java.util.concurrent.Executors
import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.ScheduledFuture
import java.util.concurrent.TimeUnit

object TVSystem : Logger {
    class TVItem(val href: String, val title: String, val text: String) {
        override fun hashCode(): Int {
            return href.hashCode()
        }

        override fun equals(other: Any?): Boolean {
            return href == (other as TVItem).href
        }

        override fun toString(): String {
            return text
        }
    }

    private val ses: ScheduledExecutorService = Executors.newSingleThreadScheduledExecutor {
        Executors.defaultThreadFactory().newThread(it).apply {
            isDaemon = true
        }
    }
    private var future: ScheduledFuture<*>? = null
    private var tv: WebDriver? = null

    fun handleSystem() {
        if (Configure.mode.tv) {
            try {
                tv = EdgeDriver(EdgeOptions().apply {
                    addArguments("--headless", "--no-sandbox")
                })
                val pendingElements = mutableMapOf<TVItem, Int>()
                var lastElements = emptySet<TVItem>()

                future = ses.scheduleWithFixedDelay({
                    tv?.let { it ->
                        it.get("https://www.twitch.tv/directory/game/Qwilight")
                        val dataTest = By.cssSelector("[data-test-selector=TitleAndChannel]")
                        val elements = try {
                            WebDriverWait(
                                it,
                                Duration.ofSeconds(1)
                            ).until(ExpectedConditions.visibilityOfAllElementsLocatedBy(dataTest))
                            it.findElements(dataTest) ?: throw TimeoutException()
                        } catch (e: TimeoutException) {
                            emptyList()
                        }.map { element ->
                            TVItem(
                                element.getAttribute("href"),
                                element.findElement(By.tagName("h3")).text,
                                element.findElement(By.tagName("p")).text
                            )
                        }.toSet()
                        elements.subtract(lastElements).filter {
                            pendingElements.remove(it) == null
                        }.forEach {
                            SiteHandler.putSiteYell(it)
                        }
                        lastElements.subtract(elements).forEach {
                            pendingElements[it] = 0
                        }
                        pendingElements.toMap().forEach {
                            if (it.value < 5) {
                                pendingElements[it.key] = it.value + 1
                            } else {
                                pendingElements.remove(it.key)
                            }
                        }
                        lastElements = elements
                    }
                }, 0L, 1L, TimeUnit.MINUTES)
            } catch (e: Throwable) {
                logFault(e)
            }
        }
    }

    val tvStatus: String
        get() = "{Title: ${tv?.title}, Page: ${tv?.pageSource}, URL: ${tv?.currentUrl}, Handles: [${tv?.windowHandles?.joinToString()}]}"

    fun dispose() {
        ses.shutdown()
        tv?.quit()
    }
}