import AppKit

class CmdFile {
    private var timer: DispatchSourceTimer?
    private(set) var lastExec = Int(Date().timeIntervalSince1970)
    var untilNextExec: Int {
        get {
            let res =
                reloadTime - (Int(Date().timeIntervalSince1970) - lastExec)
            if res <= 0 { return reloadTime }
            return res
        }
    }

    private(set) var url: URL
    private(set) var reloadTime: Int
    private(set) var statusItem: CBMenuBarItem = CBMenuBarItem()

    private var shouldWait = false

    init(url: URL, reloadTime: Int) {
        self.url = url
        self.reloadTime = reloadTime

        statusItem.setAutosaveName(url.lastPathComponent)
        statusItem.setImage(title: "clock.fill", description: "Clock")
        statusItem.setFile(self)

        setupTimer()
        resumeTimer()
    }

    deinit {
        cancelTimer()
    }

    private func reloadWidget() {
        DispatchQueue.global(qos: .userInitiated).async { [weak self] in
            if let url = self?.url {
                self?.shouldWait = true
                let execResult = execute(atPath: url)
                DispatchQueue.main.async {
                    if execResult.title.isEmpty {
                        self?.statusItem
                            .setImage(title: "exclamationmark.triangle.fill",
                                      description: "Warning")
                        self?.statusItem.setTitle("")
                    } else {
                        self?.statusItem
                            .setImage(title: nil, description: "")
                        self?.statusItem.setTitle(execResult.0)
                    }
                    self?.statusItem.setContents(to: execResult.body)
                }
                self?.shouldWait = false
            }
        }
    }

    private func setupTimer() {
        timer = DispatchSource.makeTimerSource(queue: DispatchQueue.global())
        timer?.schedule(deadline: .now(), repeating: .seconds(reloadTime))
        timer?.setEventHandler { [weak self] in
            guard let file = self, !file.shouldWait else { return }
            file.reloadWidget()
            file.lastExec = Int(Date().timeIntervalSince1970)
        }
    }

    private func resumeTimer() {
        timer?.schedule(deadline: .now(), repeating: .seconds(reloadTime))
        timer?.resume()
    }

    private func pauseTimer() {
        timer?.suspend()
    }

    private func cancelTimer() {
        if timer != nil {
            timer?.cancel()
            timer = nil
        }
    }

    func restartTimer() {
        pauseTimer()
        resumeTimer()
    }
}

class CmdManager {
    static let standard = CmdManager()

    var paths: [CmdFile] = []
    private var defaultStatusItem: NSStatusItem?

    func configure() {
        getPaths()
        if paths.isEmpty {
            setupMenu()
        } else {
            defaultStatusItem = nil
        }
    }

    private func getPaths() {
        let manager = FileManager.default
        let path =
            manager.homeDirectoryForCurrentUser.appending(path: ".cmdbar")
        if let contents =
                try? manager.contentsOfDirectory(atPath: path.path())
        {
            for content in contents {
                let filePath = path.appending(path: content)

                let domains = filePath.path().components(separatedBy: ".")
                if (isValidTimeFormat(domains[domains.count - 1])) {
                    paths.append(
                        CmdFile(url: filePath,
                                reloadTime: intervalToSeconds(
                                    from: domains[domains.count - 1])
                                )
                    )
                } else if domains.last == "sh" &&
                    isValidTimeFormat(domains[domains.count - 2])
                {
                    paths.append(
                        CmdFile(url: filePath,
                                reloadTime: intervalToSeconds(
                                    from: domains[domains.count - 2]
                                )
                        )
                    )
                }
            }
        }
    }

    private func setupMenu() {
        defaultStatusItem = NSStatusBar.system
            .statusItem(withLength: NSStatusItem.variableLength)
        if let btn = defaultStatusItem?.button {
            let image = NSApp.applicationIconImage
            image!.size = NSSize(width: 22, height: 22)
            btn.image = image
        }
        guard defaultStatusItem != nil else { return }
        let menu = NSMenu()
        menu.addItem(NSMenuItem(title: "Reload",
                     action: #selector(reloadWidgets), keyEquivalent: ""))
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "About",
                     action: #selector(showAbout), keyEquivalent: ""))
        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Quit",
                     action: #selector(NSApplication.terminate(_:)),
                     keyEquivalent: "q"))
        defaultStatusItem!.menu = menu
    }

    func clearItems() {
        paths.removeAll()
    }

    func reloadItems() {
        clearItems()
        configure()
    }

    func resetTimers() {
        for item in paths {
            item.restartTimer()
        }
    }

    @objc private func reloadWidgets() {
        reloadItems()
    }

    @objc private func showAbout() {
        delegate.showAbout()
    }
}
