import AppKit
import ScreenCaptureKit

// ScreenSharePickerSheet — chooses *what* the SCREEN_AUDIO stream captures before sharing
// starts. ScreenCaptureKit filters audio per application (not per window), so the user picks
// a mode (everything / only-these / all-except-these) plus a set of apps, and a dedicated
// toggle to drop their own screen-reader (VoiceOver) speech from the mix.
//
// Mirrors the modal-sheet pattern used by the rest of the macOS client (UserPickerSheet,
// MoveUserSheet, …): an NSViewController presented via MainWindowController.presentSheet, with
// an `onComplete` callback that returns the chosen `ScreenAudioSelection` (or nil on cancel).
//
// The app list comes from `SCShareableContent.current`, fetched asynchronously — that first
// access is also what surfaces the Screen Recording (TCC) prompt, which is why the picker is
// the natural place for it to appear, before any capture begins.
final class ScreenSharePickerSheet: NSViewController, NSTableViewDataSource, NSTableViewDelegate {

    /// Called with the chosen selection, or `nil` if the user cancelled.
    var onComplete: ((ScreenAudioSelection?) -> Void)?

    private enum Mode: Int { case everything = 0, only = 1, except = 2 }

    private struct AppEntry { let name: String; let bundleID: String; let icon: NSImage? }

    private let initialSelection: ScreenAudioSelection
    private var mode: Mode
    private var excludeScreenReader: Bool
    private var checked: Set<String>          // bundle IDs ticked in the app table

    private var apps: [AppEntry] = []
    private let tableView = NSTableView()
    private var modeControl: NSSegmentedControl?
    private var screenReaderCheckbox: NSButton?
    private var statusLabel: NSTextField?

    init(selection: ScreenAudioSelection) {
        self.initialSelection = selection
        switch selection.scope {
        case .entireDesktop:      mode = .everything; checked = []
        case .onlyApps(let ids):  mode = .only;       checked = Set(ids)
        case .allExcept(let ids): mode = .except;     checked = Set(ids)
        }
        self.excludeScreenReader = selection.excludeScreenReader
        super.init(nibName: nil, bundle: nil)
    }

    required init?(coder: NSCoder) { fatalError() }

    override func loadView() {
        view = NSView(frame: NSRect(x: 0, y: 0, width: 360, height: 420))
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        buildUI()
        loadApps()
    }

    // MARK: - UI

    private func buildUI() {
        let titleLabel = NSTextField(labelWithString: "Choose what to share:")
        titleLabel.font = .boldSystemFont(ofSize: 13)
        titleLabel.setAccessibilityLabel("Choose what to share")

        let modeControl = NSSegmentedControl(
            labels: ["Everything", "Only selected", "All except selected"],
            trackingMode: .selectOne, target: self, action: #selector(modeChanged))
        modeControl.selectedSegment = mode.rawValue
        modeControl.segmentDistribution = .fillEqually
        modeControl.setAccessibilityLabel("Share mode")
        self.modeControl = modeControl

        let col = NSTableColumn(identifier: NSUserInterfaceItemIdentifier("app"))
        tableView.addTableColumn(col)
        tableView.headerView = nil
        tableView.dataSource = self
        tableView.delegate = self
        tableView.rowHeight = 22
        tableView.setAccessibilityLabel("Application list")

        let scroll = NSScrollView()
        scroll.documentView = tableView
        scroll.hasVerticalScroller = true
        scroll.borderType = .bezelBorder
        scroll.translatesAutoresizingMaskIntoConstraints = false
        scroll.setContentHuggingPriority(.defaultLow, for: .vertical)

        let statusLabel = NSTextField(labelWithString: "Loading apps…")
        statusLabel.textColor = .secondaryLabelColor
        statusLabel.font = .systemFont(ofSize: 11)
        self.statusLabel = statusLabel

        let screenReaderCheckbox = NSButton(checkboxWithTitle: "Exclude screen reader (VoiceOver) audio",
                                            target: self, action: #selector(screenReaderToggled))
        screenReaderCheckbox.state = excludeScreenReader ? .on : .off
        screenReaderCheckbox.toolTip = "Keep your VoiceOver speech out of the shared audio."
        self.screenReaderCheckbox = screenReaderCheckbox

        let cancelButton = NSButton(title: "Cancel", target: self, action: #selector(cancelClicked))
        cancelButton.bezelStyle = .rounded
        cancelButton.keyEquivalent = "\u{1b}"  // Esc

        let shareButton = NSButton(title: "Share", target: self, action: #selector(shareClicked))
        shareButton.bezelStyle = .rounded
        shareButton.keyEquivalent = "\r"

        let buttonRow = NSStackView(views: [NSView(), cancelButton, shareButton])
        buttonRow.orientation = .horizontal
        buttonRow.spacing = 8

        let stack = NSStackView(views: [titleLabel, modeControl, scroll, statusLabel,
                                        screenReaderCheckbox, buttonRow])
        stack.orientation = .vertical
        stack.spacing = 8
        stack.edgeInsets = NSEdgeInsets(top: 16, left: 16, bottom: 16, right: 16)
        stack.translatesAutoresizingMaskIntoConstraints = false
        view.addSubview(stack)
        NSLayoutConstraint.activate([
            stack.topAnchor.constraint(equalTo: view.topAnchor),
            stack.leadingAnchor.constraint(equalTo: view.leadingAnchor),
            stack.trailingAnchor.constraint(equalTo: view.trailingAnchor),
            stack.bottomAnchor.constraint(equalTo: view.bottomAnchor),
        ])

        updateEnabledStates()
    }

    /// Reflect the current mode: the app table only matters for only/except; the screen-reader
    /// toggle is moot for `.only` (include-only already excludes the screen reader).
    private func updateEnabledStates() {
        let listActive = (mode != .everything)
        tableView.isEnabled = listActive
        tableView.alphaValue = listActive ? 1.0 : 0.45
        screenReaderCheckbox?.isEnabled = (mode != .only)
    }

    private func loadApps() {
        Task { @MainActor in
            do {
                let content = try await SCShareableContent.current
                var seen = Set<String>()
                var entries: [AppEntry] = []
                for app in content.applications {
                    let bid = app.bundleIdentifier
                    guard !bid.isEmpty, !seen.contains(bid) else { continue }
                    // Hide our own app (its audio is already excluded) and the screen reader
                    // (handled by its own toggle).
                    if bid == Bundle.main.bundleIdentifier { continue }
                    if ScreenAudioCapture.screenReaderBundleIDs.contains(bid) { continue }
                    seen.insert(bid)
                    let name = app.applicationName.isEmpty ? bid : app.applicationName
                    let icon = NSRunningApplication
                        .runningApplications(withBundleIdentifier: bid).first?.icon
                    entries.append(AppEntry(name: name, bundleID: bid, icon: icon))
                }
                entries.sort { $0.name.localizedCaseInsensitiveCompare($1.name) == .orderedAscending }
                self.apps = entries
                self.statusLabel?.isHidden = true
                self.tableView.reloadData()
            } catch {
                self.statusLabel?.stringValue = "Screen Recording permission needed to list apps — "
                    + "grant it in System Settings ▸ Privacy & Security, then reopen this."
            }
        }
    }

    // MARK: - Actions

    @objc private func modeChanged() {
        mode = Mode(rawValue: modeControl?.selectedSegment ?? 0) ?? .everything
        updateEnabledStates()
    }

    @objc private func screenReaderToggled() {
        excludeScreenReader = (screenReaderCheckbox?.state == .on)
    }

    @objc private func appCheckboxToggled(_ sender: NSButton) {
        let row = sender.tag
        guard row >= 0, row < apps.count else { return }
        let bid = apps[row].bundleID
        if sender.state == .on { checked.insert(bid) } else { checked.remove(bid) }
    }

    @objc private func shareClicked() {
        let scope: ScreenAudioScope
        switch mode {
        case .everything: scope = .entireDesktop
        case .only:       scope = .onlyApps(Array(checked))
        case .except:     scope = .allExcept(Array(checked))
        }
        dismiss(nil)
        onComplete?(ScreenAudioSelection(scope: scope, excludeScreenReader: excludeScreenReader))
    }

    @objc private func cancelClicked() {
        dismiss(nil)
        onComplete?(nil)
    }

    // MARK: - NSTableViewDataSource / Delegate

    func numberOfRows(in tableView: NSTableView) -> Int { apps.count }

    func tableView(_ tableView: NSTableView, viewFor tableColumn: NSTableColumn?, row: Int) -> NSView? {
        let app = apps[row]
        let cell = AppCheckboxCell()
        cell.checkbox.title = app.name
        cell.checkbox.state = checked.contains(app.bundleID) ? .on : .off
        cell.checkbox.isEnabled = (mode != .everything)
        cell.checkbox.tag = row
        cell.checkbox.target = self
        cell.checkbox.action = #selector(appCheckboxToggled(_:))
        cell.iconView.image = app.icon
        return cell
    }

    // Selecting a row shouldn't visually highlight — interaction is via the checkbox.
    func tableView(_ tableView: NSTableView, shouldSelectRow row: Int) -> Bool { false }
}

// One row: a leading app icon and a checkbox titled with the app name.
private final class AppCheckboxCell: NSTableCellView {
    let iconView = NSImageView()
    let checkbox = NSButton(checkboxWithTitle: "", target: nil, action: nil)

    init() {
        super.init(frame: .zero)
        iconView.translatesAutoresizingMaskIntoConstraints = false
        checkbox.translatesAutoresizingMaskIntoConstraints = false
        addSubview(iconView)
        addSubview(checkbox)
        NSLayoutConstraint.activate([
            iconView.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 4),
            iconView.centerYAnchor.constraint(equalTo: centerYAnchor),
            iconView.widthAnchor.constraint(equalToConstant: 16),
            iconView.heightAnchor.constraint(equalToConstant: 16),
            checkbox.leadingAnchor.constraint(equalTo: iconView.trailingAnchor, constant: 6),
            checkbox.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -4),
            checkbox.centerYAnchor.constraint(equalTo: centerYAnchor),
        ])
    }

    required init?(coder: NSCoder) { fatalError() }
}
