import SwiftUI
import AppKit
import Combine
import TokenLensCore

@main
struct TokenLensApp: App {
    @NSApplicationDelegateAdaptor(AppDelegate.self) private var appDelegate

    var body: some Scene {
        Settings {
            SettingsView()
                .preferredColorScheme(.dark)
        }
        .commands {
            CommandGroup(replacing: .newItem) {}
        }
    }
}

final class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
    private var dashboardWindow: NSWindow?
    private var floatingPanel: NSPanel?
    private var statusItem: NSStatusItem?
    private var statusPopover: NSPopover?
    private var statusPopoverCloseTask: Task<Void, Never>?
    private var cancellables: Set<AnyCancellable> = []
    private var isTerminating = false

    func applicationDidFinishLaunching(_ notification: Notification) {
        NSApp.setActivationPolicy(.regular)
        UsageStore.shared.start()
        bindAppState()
        updateFloatingPanelVisibility()
        updateStatusItemVisibility()
    }

    func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {
        Task { @MainActor in
            showDashboardWindow()
        }
        return false
    }

    func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
        isTerminating = true
        return .terminateNow
    }

    @MainActor
    func showDashboardWindow() {
        closeStatusPopover()

        if let dashboardWindow {
            NSApp.activate(ignoringOtherApps: true)
            if dashboardWindow.isMiniaturized {
                dashboardWindow.deminiaturize(nil)
            }
            dashboardWindow.makeKeyAndOrderFront(nil)
            dashboardWindow.orderFrontRegardless()
            return
        }

        let hostingView = NSHostingView(
            rootView: DashboardView()
                .frame(minWidth: 1180, minHeight: 760)
                .preferredColorScheme(.dark)
        )
        let window = NSWindow(
            contentRect: NSRect(x: 0, y: 0, width: 1280, height: 860),
            styleMask: [.titled, .closable, .miniaturizable, .resizable, .fullSizeContentView],
            backing: .buffered,
            defer: false
        )
        window.title = "TokenLens"
        window.identifier = NSUserInterfaceItemIdentifier("main-dashboard-window")
        window.titleVisibility = .hidden
        window.titlebarAppearsTransparent = true
        window.isMovableByWindowBackground = false
        window.backgroundColor = .clear
        window.isOpaque = false
        window.minSize = NSSize(width: 1180, height: 760)
        window.contentView = hostingView
        window.delegate = self
        window.center()

        dashboardWindow = window
        NSApp.activate(ignoringOtherApps: true)
        window.makeKeyAndOrderFront(nil)
        window.orderFrontRegardless()
    }

    func windowWillClose(_ notification: Notification) {
        if notification.object as? NSWindow === dashboardWindow {
            dashboardWindow = nil
        }
    }

    func windowShouldClose(_ sender: NSWindow) -> Bool {
        guard sender === dashboardWindow, !isTerminating else {
            return true
        }
        sender.miniaturize(nil)
        return false
    }

    @MainActor
    private func bindAppState() {
        AppSettings.shared.$showFloatingWindow
            .sink { [weak self] isVisible in self?.updateFloatingPanelVisibility(isVisible: isVisible) }
            .store(in: &cancellables)
        AppSettings.shared.$showStatusBarIndicator
            .sink { [weak self] isVisible in self?.updateStatusItemVisibility(isVisible: isVisible) }
            .store(in: &cancellables)
        UsageStore.shared.$summary
            .sink { [weak self] _ in self?.updateStatusTitle() }
            .store(in: &cancellables)
        AppSettings.shared.$floatingAppearance
            .sink { [weak self] _ in
                self?.applyFloatingPanelAppearance()
                self?.applyStatusPopoverAppearance()
            }
            .store(in: &cancellables)
    }

    @MainActor
    private func updateFloatingPanelVisibility(isVisible: Bool = AppSettings.shared.showFloatingWindow) {
        if isVisible {
            if floatingPanel == nil {
                installFloatingPanel()
            }
            floatingPanel?.orderFrontRegardless()
        } else {
            floatingPanel?.orderOut(nil)
        }
    }

    @MainActor
    private func installFloatingPanel() {
        let initialFloatingSize = NSSize(width: 164, height: 36)
        let panel = NSPanel(
            contentRect: NSRect(origin: NSPoint(x: 80, y: 80), size: initialFloatingSize),
            styleMask: [.borderless, .nonactivatingPanel],
            backing: .buffered,
            defer: false
        )
        panel.title = "TokenLens Floating"
        panel.level = .floating
        panel.collectionBehavior = [.canJoinAllSpaces, .fullScreenAuxiliary]
        panel.isOpaque = false
        panel.backgroundColor = .clear
        panel.hasShadow = false
        panel.isMovableByWindowBackground = true
        panel.hidesOnDeactivate = false
        panel.appearance = AppSettings.shared.floatingAppearance.nsAppearance

        let hostingView = TransparentHostingView(
            rootView: FloatingTokenWindow(store: UsageStore.shared) { [weak self] size in
                Task { @MainActor in
                    self?.resizeFloatingPanel(to: size)
                }
            }
                .background(Color.clear)
        )
        makeTransparent(hostingView)
        panel.contentView = hostingView
        if let contentView = panel.contentView {
            makeTransparent(contentView)
        }

        if let screenFrame = NSScreen.main?.visibleFrame {
            let x = screenFrame.maxX - 360
            let y = screenFrame.maxY - 170
            panel.setFrameOrigin(NSPoint(x: x, y: y))
        }
        panel.orderFrontRegardless()
        floatingPanel = panel
    }

    @MainActor
    private func resizeFloatingPanel(to size: CGSize) {
        guard let panel = floatingPanel else { return }
        let newSize = NSSize(width: ceil(size.width), height: ceil(size.height))
        guard abs(panel.frame.width - newSize.width) > 0.5 || abs(panel.frame.height - newSize.height) > 0.5 else {
            return
        }
        let currentFrame = panel.frame
        let newFrame = NSRect(
            x: currentFrame.minX,
            y: currentFrame.maxY - newSize.height,
            width: newSize.width,
            height: newSize.height
        )
        panel.setFrame(newFrame, display: true)
    }

    @MainActor
    private func updateStatusItemVisibility(isVisible: Bool = AppSettings.shared.showStatusBarIndicator) {
        if isVisible {
            if statusItem == nil {
                let item = NSStatusBar.system.statusItem(withLength: NSStatusItem.variableLength)
                item.button?.target = self
                item.button?.action = #selector(statusItemClicked)
                item.button?.image = NSImage(systemSymbolName: "chart.line.uptrend.xyaxis", accessibilityDescription: "Tokens")
                item.button?.imagePosition = .imageLeading
                statusItem = item
            }
            updateStatusTitle()
        } else if let item = statusItem {
            NSStatusBar.system.removeStatusItem(item)
            statusItem = nil
        }
    }

    @MainActor
    private func updateStatusTitle() {
        guard let button = statusItem?.button else { return }
        button.title = " \(TokenFormatter.format(UsageStore.shared.summary.cards.todayTokens))"
        button.toolTip = "TokenLens 今日 Tokens：\(TokenFormatter.format(UsageStore.shared.summary.cards.todayTokens))"
    }

    @objc
    @MainActor
    private func statusItemClicked() {
        guard let button = statusItem?.button else { return }
        if statusPopover?.isShown == true {
            closeStatusPopover()
            return
        }
        let popover = NSPopover()
        popover.behavior = .transient
        popover.contentSize = statusPopoverSize()
        let controller = NSHostingController(
            rootView: StatusBarDetailView(store: UsageStore.shared)
        )
        makeTransparent(controller.view)
        popover.contentViewController = controller
        popover.show(relativeTo: button.bounds, of: button, preferredEdge: .minY)
        statusPopover = popover
        applyStatusPopoverAppearance()
        scheduleStatusPopoverClose()
    }

    @MainActor
    private func scheduleStatusPopoverClose() {
        statusPopoverCloseTask?.cancel()
        let delay = UInt64(max(1, AppSettings.shared.autoCollapseSeconds)) * 1_000_000_000
        statusPopoverCloseTask = Task { @MainActor in
            try? await Task.sleep(nanoseconds: delay)
            guard !Task.isCancelled else { return }
            closeStatusPopover()
        }
    }

    @MainActor
    private func closeStatusPopover() {
        statusPopoverCloseTask?.cancel()
        statusPopover?.close()
        statusPopover = nil
    }

    @MainActor
    private func statusPopoverSize() -> NSSize {
        let activeProviderCount = Provider.allCases.filter {
            (UsageStore.shared.summary.todayProviderTotals[$0]?.totalTokens ?? 0) > 0
        }.count
        let rowCount = max(activeProviderCount, 1)
        return NSSize(width: 330, height: 128 + CGFloat(rowCount) * 54 + CGFloat(max(rowCount - 1, 0)) * 8)
    }

    @MainActor
    private func applyFloatingPanelAppearance() {
        floatingPanel?.appearance = AppSettings.shared.floatingAppearance.nsAppearance
        if let contentView = floatingPanel?.contentView {
            makeTransparent(contentView)
        }
    }

    @MainActor
    private func applyStatusPopoverAppearance() {
        guard let popover = statusPopover else { return }
        let appearance = AppSettings.shared.floatingAppearance.nsAppearance
        popover.contentViewController?.view.window?.appearance = appearance
        if let view = popover.contentViewController?.view {
            makeTransparent(view)
        }
    }

    @MainActor
    private func makeTransparent(_ view: NSView) {
        view.wantsLayer = true
        view.layer?.backgroundColor = NSColor.clear.cgColor
        view.layer?.isOpaque = false
        view.layer?.masksToBounds = false
    }
}

final class TransparentHostingView<Content: View>: NSHostingView<Content> {
    override var isOpaque: Bool { false }

    required init(rootView: Content) {
        super.init(rootView: rootView)
        prepareForTransparentCompositing()
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        nil
    }

    override func viewDidMoveToWindow() {
        super.viewDidMoveToWindow()
        prepareForTransparentCompositing()
        window?.isOpaque = false
        window?.backgroundColor = .clear
        window?.hasShadow = true
    }

    override func draw(_ dirtyRect: NSRect) {
        NSColor.clear.setFill()
        dirtyRect.fill()
        super.draw(dirtyRect)
    }

    private func prepareForTransparentCompositing() {
        wantsLayer = true
        layer?.backgroundColor = NSColor.clear.cgColor
        layer?.isOpaque = false
        layer?.masksToBounds = false
    }
}

struct StatusBarDetailView: View {
    @ObservedObject var store: UsageStore
    @StateObject private var settings = AppSettings.shared

    private var visibleProviders: [Provider] {
        Provider.allCases.filter {
            (store.summary.todayProviderTotals[$0]?.totalTokens ?? 0) > 0
        }
    }

    var body: some View {
        VStack(alignment: .leading, spacing: 12) {
            HStack(spacing: 10) {
                Image(systemName: "chart.line.uptrend.xyaxis")
                    .font(.system(size: 20, weight: .bold))
                    .foregroundStyle(.white)
                    .frame(width: 36, height: 36)
                    .background(.blue.gradient, in: RoundedRectangle(cornerRadius: 10, style: .continuous))
                VStack(alignment: .leading, spacing: 2) {
                    Text("今日 Tokens")
                        .font(.system(size: 13, weight: .semibold))
                        .foregroundStyle(.secondary)
                    Text(TokenFormatter.format(store.summary.cards.todayTokens))
                        .font(.system(size: 24, weight: .bold, design: .rounded))
                        .monospacedDigit()
                }
                Spacer()
                Text("实时")
                    .font(.system(size: 12, weight: .bold))
                    .foregroundStyle(.secondary)
            }

            VStack(spacing: 8) {
                if visibleProviders.isEmpty {
                    Text("今日暂无工具流量")
                        .font(.system(size: 13, weight: .semibold))
                        .foregroundStyle(.secondary)
                        .frame(maxWidth: .infinity)
                        .frame(height: 46)
                        .background(.white.opacity(0.06), in: RoundedRectangle(cornerRadius: 10, style: .continuous))
                } else {
                    ForEach(visibleProviders, id: \.self) { provider in
                        StatusProviderRow(
                            provider: provider,
                            bucket: store.summary.todayProviderTotals[provider] ?? TokenBucket(provider: provider)
                        )
                    }
                }
            }

            Button {
                WindowActions.showMainWindow()
            } label: {
                Label("打开主界面", systemImage: "rectangle.grid.2x2")
                    .frame(maxWidth: .infinity)
            }
            .buttonStyle(.borderedProminent)
        }
        .padding(14)
        .frame(width: 330)
        .background(.ultraThinMaterial)
        .preferredColorScheme(settings.floatingAppearance.colorScheme)
    }
}

private struct StatusProviderRow: View {
    let provider: Provider
    let bucket: TokenBucket

    var body: some View {
        HStack(spacing: 9) {
            Circle()
                .fill(provider.color)
                .frame(width: 8, height: 8)
            VStack(alignment: .leading, spacing: 2) {
                HStack {
                    Text(provider.rawValue)
                        .font(.system(size: 13, weight: .bold))
                    Spacer()
                    Text(TokenFormatter.format(bucket.totalTokens))
                        .font(.system(size: 13, weight: .bold, design: .rounded))
                        .monospacedDigit()
                }
                Text("输入 \(TokenFormatter.format(bucket.inputTokens))   输出 \(TokenFormatter.format(bucket.outputTokens))   缓存 \(TokenFormatter.format(bucket.cachedTokens))")
                    .font(.system(size: 10, weight: .medium))
                    .foregroundStyle(.secondary)
            }
        }
        .padding(.horizontal, 10)
        .padding(.vertical, 8)
        .background(provider.color.opacity(0.10), in: RoundedRectangle(cornerRadius: 10, style: .continuous))
    }
}

struct WindowAccessor: NSViewRepresentable {
    func makeNSView(context: Context) -> NSView {
        let view = NSView()
        DispatchQueue.main.async {
            guard let window = view.window else { return }
            window.title = "TokenLens"
            window.identifier = NSUserInterfaceItemIdentifier("main-dashboard-window")
            window.titleVisibility = .hidden
            window.titlebarAppearsTransparent = true
            window.isMovableByWindowBackground = false
            window.backgroundColor = .clear
            window.isOpaque = false
            window.setContentSize(NSSize(width: 1280, height: 860))
            window.minSize = NSSize(width: 1180, height: 760)
        }
        return view
    }

    func updateNSView(_ nsView: NSView, context: Context) {}
}
