//
// MenuManager.swift
// IceGlass
//
// Copyright 2026 Rouslan Zenetl. All Rights Reserved.
//

import AppKit

class MenuManager: @unchecked Sendable {
    private let logger = IceGlassLogger(
        subsystem: Bundle.main.bundleIdentifier ?? "dev.rzen.indie.IceGlass",
        category: "MenuManager"
    )

    static let shared = MenuManager()

    private lazy var settings = AppSettings.shared
    private lazy var mainService = MainService.shared
    private lazy var statusItemManager = StatusItemManager.shared

    private var menuUpdateTimer: Timer?

    private init() {
        DispatchQueue.main.async { [weak self] in
            guard let self = self else {
                AppTerminator.terminate()
                return
            }
            self.logger.info("Initializing")

            self.menuUpdateTimer = Timer.startTimer(
                timer: self.menuUpdateTimer,
                interval: PollingInterval.bootstrap.rawValue
            ) { [weak self] _ in
                guard let self = self else { return }
                self.updateMenu()
            }
        }
    }

    @MainActor private func isSafe() -> Bool {
        if let currentEvent = NSApp.currentEvent,
           currentEvent.type == .leftMouseDown || currentEvent.type == .rightMouseDown {
            return false
        }
        return true
    }

    func scoreboardChanged() {
        Task { @MainActor [weak self] in
            guard let self = self else { return }
            let interval = self.mainService.anyGameLive
                ? PollingInterval.everyMinute.rawValue
                : PollingInterval.idle.rawValue
            self.logger.debug("Starting menu update timer \(interval)")
            self.menuUpdateTimer = Timer.startTimer(
                timer: self.menuUpdateTimer,
                interval: interval
            ) { [weak self] _ in
                guard let self = self else { return }
                self.updateMenu()
            }
        }
        updateMenuSafely()
    }

    func updateMenuSafely() {
        Task { @MainActor in
            if self.isSafe() {
                self.updateMenu()
            }
        }
    }

    private func updateMenu() {
        logger.info("Setting up menu")

        let menu = NSMenu()

        // Refresh Now
        menu.addItem(
            NSMenuItem(
                title: "Refresh Now",
                action: #selector(refreshStats),
                keyEquivalent: "r"
            ).withTarget(self)
        )
        menu.addItem(NSMenuItem.separator())

        let monoFont = NSFont.monospacedSystemFont(ofSize: NSFont.systemFontSize, weight: .regular)
        let boldFont = NSFont.systemFont(ofSize: NSFont.smallSystemFontSize, weight: .bold)

        let roundGroups = mainService.roundSeriesGroups
        let latestRound = roundGroups.last?.round
        for group in roundGroups where !group.items.isEmpty {
            let headerText = "ROUND \(group.round)"
            let headerItem = NSMenuItem(title: headerText, action: nil, keyEquivalent: "")
            headerItem.attributedTitle = NSAttributedString(
                string: headerText,
                attributes: [.font: boldFont]
            )
            menu.addItem(headerItem)

            for item in group.items {
                menu.addItem(createRoundSeriesItem(for: item, showStatus: group.round == latestRound, font: monoFont))
            }

            menu.addItem(NSMenuItem.separator())
        }

        let gameDays = mainService.gamesByDate

        if gameDays.isEmpty {
            menu.addItem(NSMenuItem(title: "No Games Available", action: nil, keyEquivalent: ""))
        } else {
            for (index, gameDay) in gameDays.enumerated() {
                // Date header with game count
                let dateLabel = Date.fullDateLabel(for: gameDay.date)
                let gameCount = gameDay.games.count
                let headerText = "\(dateLabel) (\(gameCount))"
                let headerItem = NSMenuItem(title: headerText, action: nil, keyEquivalent: "")
                headerItem.attributedTitle = NSAttributedString(
                    string: headerText,
                    attributes: [.font: boldFont]
                )
                menu.addItem(headerItem)

                if gameDay.games.isEmpty {
                    let noGames = NSMenuItem(title: "  No games scheduled", action: nil, keyEquivalent: "")
                    noGames.attributedTitle = NSAttributedString(
                        string: "  No games scheduled",
                        attributes: [
                            .font: monoFont,
                            .foregroundColor: NSColor.secondaryLabelColor
                        ]
                    )
                    menu.addItem(noGames)
                } else {
                    for game in gameDay.games {
                        let items = createGameMenuItems(for: game, font: monoFont)
                        for item in items {
                            menu.addItem(item)
                        }
                    }
                }

                if index < gameDays.count - 1 {
                    menu.addItem(NSMenuItem.separator())
                }
            }
        }

        menu.addItem(NSMenuItem.separator())

        // Display Options submenu
        let displayMenuItem = NSMenuItem()
        displayMenuItem.title = "Display Options"
        let displaySubmenu = NSMenu()

        // Date range options
        for option in AppSettings.DisplayOption.allCases {
            let item = NSMenuItem(
                title: option.title,
                action: #selector(changeDisplayOption(_:)),
                keyEquivalent: ""
            )
            item.target = self
            item.representedObject = option.rawValue
            item.state = option == settings.displayOption ? .on : .off
            displaySubmenu.addItem(item)
        }

        displaySubmenu.addItem(NSMenuItem.separator())

        // Status bar options
        for option in AppSettings.StatusBarOption.allCases {
            let item = NSMenuItem(
                title: option.title,
                action: #selector(changeStatusBarOption(_:)),
                keyEquivalent: ""
            )
            item.target = self
            item.representedObject = option.rawValue
            item.state = option == settings.statusBarOption ? .on : .off
            displaySubmenu.addItem(item)
        }

        displayMenuItem.submenu = displaySubmenu
        menu.addItem(displayMenuItem)

        // About IceGlass
        menu.addItem(
            NSMenuItem(
                title: "About IceGlass",
                action: #selector(showAbout),
                keyEquivalent: ""
            ).withTarget(self)
        )

        // Launch at Login
        let launchItem = NSMenuItem(
            title: "Launch at Login",
            action: #selector(toggleLaunchAtLogin(_:)),
            keyEquivalent: ""
        )
        launchItem.target = self
        launchItem.state = settings.launchAtLogin ? .on : .off
        menu.addItem(launchItem)

#if DEBUG
        let devMenuItem = NSMenuItem()
        devMenuItem.title = "Developer"
        let devSubmenu = NSMenu()
        devSubmenu.addItem(
            NSMenuItem(title: "Force Refresh", action: #selector(refreshStats), keyEquivalent: "").withTarget(self)
        )
        devSubmenu.addItem(
            NSMenuItem(title: "Test Game Start Notification", action: #selector(triggerTestGameStart), keyEquivalent: "").withTarget(self)
        )
        devSubmenu.addItem(
            NSMenuItem(title: "Test Goal Scored Notification", action: #selector(triggerTestGoalScored), keyEquivalent: "").withTarget(self)
        )
        devSubmenu.addItem(
            NSMenuItem(title: "Test Game Ended Notification", action: #selector(triggerTestGameEnded), keyEquivalent: "").withTarget(self)
        )
        devSubmenu.addItem(
            NSMenuItem(title: "Thumbnail Size Test", action: #selector(triggerThumbnailSizeTest), keyEquivalent: "").withTarget(self)
        )
        devMenuItem.submenu = devSubmenu
        menu.addItem(devMenuItem)
#endif

        menu.addItem(NSMenuItem.separator())
        menu.addItem(NSMenuItem(title: "Quit", action: #selector(NSApplication.terminate(_:)), keyEquivalent: "q"))

        statusItemManager.statusItem?.menu = menu
    }

    // MARK: - Menu Actions

    @objc private func refreshStats() {
        Task { @MainActor in
            await mainService.fetchAll()
        }
    }

    @objc private func handleGameMenuClick(_ sender: NSMenuItem) {
        if NSEvent.modifierFlags.contains(.option) {
            openGameStream(sender)
        } else {
            openGame(sender)
        }
    }

    @objc private func openGame(_ sender: NSMenuItem) {
        guard let game = sender.representedObject as? Scoreboard.Game,
              let urlString = game.gameCenterUrl,
              let url = URL(string: urlString) else { return }
        NSWorkspace.shared.open(url)
    }

    @objc private func openGameStream(_ sender: NSMenuItem) {
        guard let game = sender.representedObject as? Scoreboard.Game,
              let url = URL(string: game.videocastUrl) else { return }
        NSWorkspace.shared.open(url)
    }

    @objc private func openSeriesPage(_ sender: NSMenuItem) {
        guard let urlString = sender.representedObject as? String,
              let url = URL(string: urlString) else { return }
        NSWorkspace.shared.open(url)
    }

    @objc private func changeDisplayOption(_ sender: NSMenuItem) {
        guard let rawValue = sender.representedObject as? String,
              let option = AppSettings.DisplayOption(rawValue: rawValue) else { return }
        settings.displayOption = option
        mainService.updateUI()
    }

    @objc private func changeStatusBarOption(_ sender: NSMenuItem) {
        guard let rawValue = sender.representedObject as? String,
              let option = AppSettings.StatusBarOption(rawValue: rawValue) else { return }
        settings.statusBarOption = option
        mainService.updateUI()
    }

    @objc private func toggleLaunchAtLogin(_ sender: NSMenuItem) {
        settings.launchAtLogin.toggle()
        settings.updateLoginItem(enabled: settings.launchAtLogin)
        updateMenu()
    }

#if DEBUG
    @objc private func triggerTestGameStart() {
        let notificationManager = NotificationManager.shared
        if let game = mainService.gamesByDate.flatMap(\.games).first {
            notificationManager.notifyGameStarted(game, bypassDedup: true)
        }
    }

    @objc private func triggerTestGoalScored() {
        let notificationManager = NotificationManager.shared
        if let game = mainService.gamesByDate.flatMap(\.games).first {
            let sampleScorer = GoalScorer(name: "J. Eriksson Ek", sweaterNumber: 14, strength: "PPG")
            notificationManager.notifyGoalScored(
                game,
                scoringTeam: game.homeTeam,
                scorer: sampleScorer,
                bypassDedup: true
            )
        }
    }

    @objc private func triggerTestGameEnded() {
        let notificationManager = NotificationManager.shared
        if let game = mainService.gamesByDate.flatMap(\.games).first {
            notificationManager.notifyGameEnded(game, bypassDedup: true)
        }
    }

    @objc private func triggerThumbnailSizeTest() {
        NotificationManager.shared.sendThumbnailSizeTest()
    }
#endif

    @objc private func showAbout() {
        Task { @MainActor in
            showAboutWindow()
        }
    }

    // MARK: - Menu Item Creation

    private func createGameMenuItems(for game: Scoreboard.Game, font: NSFont) -> [NSMenuItem] {
        let prefix = game.gameType == 2
            ? String(format: "#%4d  ", game.seasonGameNumber)
            : "  "
        let title = "\(prefix)\(game.menuTitle)"

        let item = NSMenuItem(title: title, action: #selector(handleGameMenuClick(_:)), keyEquivalent: "")
        item.target = self
        item.representedObject = game
        item.attributedTitle = NSAttributedString(string: title, attributes: [.font: font])

        let altItem = NSMenuItem(title: title, action: #selector(openGameStream(_:)), keyEquivalent: "")
        altItem.target = self
        altItem.representedObject = game
        altItem.attributedTitle = NSAttributedString(string: title, attributes: [.font: font])
        altItem.isAlternate = true
        altItem.keyEquivalentModifierMask = .option

        return [item, altItem]
    }

    private func createRoundSeriesItem(for roundItem: MainService.RoundSeriesItem, showStatus: Bool, font: NSFont) -> NSMenuItem {
        let series = roundItem.series
        let top = series.topSeedTeam?.abbrev ?? "TBD"
        let bottom = series.bottomSeedTeam?.abbrev ?? "TBD"
        let matchup = "\(bottom) @ \(top)"
        let score = "\(series.bottomSeedWins)-\(series.topSeedWins)"

        // Completed earlier rounds show just the matchup and score — the struck-out
        // loser already conveys the result, so the "(Final … wins)" tag is dropped.
        let title: String
        if showStatus {
            let statusTag: String
            let trailing: String
            if let winner = series.winner {
                statusTag = "(Final \(winner) wins)"
                trailing = ""
            } else if let n = series.nextGameNumber {
                statusTag = "(Game \(n))"
                trailing = roundItem.nextGame?.nextGameLabel ?? ""
            } else {
                statusTag = ""
                trailing = ""
            }
            let tagColumn = statusTag.padding(toLength: 20, withPad: " ", startingAt: 0)
            title = "  \(matchup)  \(score) \(tagColumn)\(trailing)"
        } else {
            title = "  \(matchup)  \(score)"
        }

        let item = NSMenuItem(title: title, action: #selector(openSeriesPage(_:)), keyEquivalent: "")
        item.target = self
        item.representedObject = series.fullSeriesUrl

        let attributed = NSMutableAttributedString(string: title, attributes: [.font: font])
        // Strike through the eliminated team's tricode, searching only within the
        // matchup region so the same abbrev inside the "(Final … wins)" tag stays plain.
        if let loser = series.loser {
            let matchupRange = NSRange(location: 2, length: (matchup as NSString).length)
            let loserRange = (title as NSString).range(of: loser, options: [], range: matchupRange)
            if loserRange.location != NSNotFound {
                attributed.addAttribute(.strikethroughStyle, value: NSUnderlineStyle.single.rawValue, range: loserRange)
            }
        }
        item.attributedTitle = attributed
        return item
    }

}
