import SwiftUI

struct PlayerBar: View {
    @Environment(AppState.self) private var app
    @AppStorage("skipDurationSeconds") private var skipSeconds: Int = 30
    @AppStorage("historyEnabled") private var historyEnabled: Bool = false
    @State private var scrubbing: Bool = false
    @State private var scrubValue: Double = 0
    @State private var showDetails: Bool = false
    @State private var showFullHistory: Bool = false
    @State private var showResumePrompt: Bool = false

    var body: some View {
        if let item = app.currentItem {
            VStack(spacing: 0) {
                Divider()
                content(item: item)
                    .padding(.horizontal, 16)
                    #if os(iOS)
                    .padding(.top, 8)
                    .padding(.bottom, 10)
                    #else
                    .padding(.vertical, 10)
                    #endif
                    .background(.bar)
            }
            .transition(.move(edge: .bottom).combined(with: .opacity))
            .sheet(isPresented: $showDetails) {
                PlaybackDetailsView()
                    .environment(app)
            }
            .sheet(isPresented: $showFullHistory) {
                FullHistoryView()
                    .environment(app)
            }
            .alert("Neuerer Stand auf dem Server", isPresented: $showResumePrompt, presenting: app.pendingServerProgress) { server in
                Button("Hier weiter (\(formatTime(app.player.absoluteCurrentTime)))") {
                    app.dismissPendingServerProgress()
                }
                Button("Server-Stand übernehmen (\(formatTime(server.currentTime)))") {
                    app.acceptPendingServerProgress()
                }
            } message: { server in
                Text("Auf einem anderen Gerät wurde dieses Hörbuch bis \(formatTime(server.currentTime)) gehört.")
            }
        } else if app.isPreparingPlayback {
            VStack(spacing: 0) {
                Divider()
                HStack(spacing: 12) {
                    ProgressView()
                    Text("Wiedergabe wird vorbereitet …").font(.subheadline).foregroundStyle(.secondary)
                    Spacer()
                }
                .padding(.horizontal, 16)
                .padding(.vertical, 14)
                .background(.bar)
            }
        }
    }

    // MARK: - Platform layouts

    #if os(iOS)
    @ViewBuilder
    private func content(item: LibraryItem) -> some View {
        VStack(spacing: 8) {
            // Header row: cover, title/author, play button
            HStack(spacing: 12) {
                cover(item: item)
                VStack(alignment: .leading, spacing: 2) {
                    Text(item.title).font(.subheadline).bold().lineLimit(1)
                    Text(item.author).font(.caption).foregroundStyle(.secondary).lineLimit(1)
                    if let err = app.player.errorMessage {
                        Text(err).font(.caption2).foregroundStyle(.red).lineLimit(1)
                    }
                }
                Spacer(minLength: 0)
                Button { handlePlayTap() } label: {
                    Image(systemName: app.player.isPlaying ? "pause.circle.fill" : "play.circle.fill")
                        .font(.system(size: 36))
                }
                .buttonStyle(.plain)
                .disabled(!app.player.isReady)
            }

            scrubber
            HStack(spacing: 20) {
                Button { app.skip(by: -Double(skipSeconds)) } label: {
                    Image(systemName: skipBackImage).font(.system(size: 22))
                }
                .buttonStyle(.plain)
                .disabled(!app.player.isReady)

                Button { app.skip(by: Double(skipSeconds)) } label: {
                    Image(systemName: skipForwardImage).font(.system(size: 22))
                }
                .buttonStyle(.plain)
                .disabled(!app.player.isReady)

                Spacer()

                if historyEnabled {
                    historyQuickMenu
                }

                Button { showDetails = true } label: {
                    Image(systemName: "list.bullet.indent")
                        .font(.system(size: 22))
                }
                .buttonStyle(.plain)
                .disabled(!app.player.isReady)

                sleepMenu

                rateMenu

                AirPlayPickerButton()
                    .frame(width: 26, height: 26)
                    .disabled(!app.player.isReady)

                Button {
                    app.stopPlayback()
                } label: {
                    Image(systemName: "xmark.circle.fill")
                        .font(.system(size: 22))
                        .foregroundStyle(.secondary)
                }
                .buttonStyle(.plain)
            }
            .padding(.top, 2)
        }
    }
    #else
    @ViewBuilder
    private func content(item: LibraryItem) -> some View {
        HStack(spacing: 14) {
            cover(item: item)

            VStack(alignment: .leading, spacing: 2) {
                Text(item.title).font(.subheadline).bold().lineLimit(1)
                Text(item.author).font(.caption).foregroundStyle(.secondary).lineLimit(1)
                if let err = app.player.errorMessage {
                    Text(err).font(.caption2).foregroundStyle(.red).lineLimit(1)
                }
            }
            .frame(minWidth: 160, idealWidth: 200, maxWidth: 240, alignment: .leading)

            transportControls

            scrubber
                .frame(minWidth: 200)

            rateMenu

            sleepMenu

            AirPlayPickerButton()
                .frame(width: 24, height: 24)
                .disabled(!app.player.isReady)
                .help("Auf anderem Gerät abspielen (AirPlay)")

            Spacer(minLength: 0)

            if historyEnabled {
                historyQuickMenu
            }

            Button { showDetails = true } label: {
                Image(systemName: "list.bullet.indent")
            }
            .buttonStyle(.plain)
            .disabled(!app.player.isReady)
            .help("Kapitel & Lesezeichen")

            statusIndicator

            Button {
                app.stopPlayback()
            } label: {
                Image(systemName: "xmark.circle.fill")
                    .foregroundStyle(.secondary)
            }
            .buttonStyle(.plain)
            .help("Wiedergabe beenden")
        }
    }

    private var transportControls: some View {
        HStack(spacing: 14) {
            Button { app.skip(by: -Double(skipSeconds)) } label: {
                Image(systemName: skipBackImage).font(.system(size: 18))
            }
            .buttonStyle(.plain)
            .disabled(!app.player.isReady)

            Button { handlePlayTap() } label: {
                Image(systemName: app.player.isPlaying ? "pause.circle.fill" : "play.circle.fill")
                    .font(.system(size: 34))
            }
            .buttonStyle(.plain)
            .disabled(!app.player.isReady)
            .keyboardShortcut(.space, modifiers: [])

            Button { app.skip(by: Double(skipSeconds)) } label: {
                Image(systemName: skipForwardImage).font(.system(size: 18))
            }
            .buttonStyle(.plain)
            .disabled(!app.player.isReady)
        }
    }

    private var statusIndicator: some View {
        HStack(spacing: 4) {
            Circle()
                .fill(app.network.isOnline ? .green : .orange)
                .frame(width: 6, height: 6)
            if app.sync.queuedCount > 0 {
                Text("\(app.sync.queuedCount)")
                    .font(.caption2)
                    .foregroundStyle(.secondary)
            }
        }
        .help(app.network.isOnline
              ? "Online – Fortschritt wird synchronisiert"
              : "Offline – \(app.sync.queuedCount) Eintrag/Einträge wartend")
    }
    #endif

    // MARK: - Shared subviews

    private func cover(item: LibraryItem) -> some View {
        Group {
            if let url = app.client.coverURL(itemId: item.id) {
                AsyncImage(url: url) { phase in
                    if let img = phase.image {
                        img.resizable().aspectRatio(contentMode: .fill)
                    } else {
                        Color.gray.opacity(0.3)
                    }
                }
            } else {
                Color.gray.opacity(0.3)
            }
        }
        #if os(iOS)
        .frame(width: 44, height: 44)
        #else
        .frame(width: 48, height: 48)
        #endif
        .clipShape(RoundedRectangle(cornerRadius: 6))
    }

    private var scrubber: some View {
        ScrubberView(scrubbing: $scrubbing, scrubValue: $scrubValue)
    }

    private var detailsButtonVisible: Bool {
        app.player.isReady
    }

    private var historyQuickMenu: some View {
        Menu {
            let recent = Array(app.history.entries
                .filter { $0.itemId == app.currentItem?.id && $0.episodeId == app.currentItem?.episodeId }
                .prefix(5))
            if recent.isEmpty {
                Text(String(localized: "history.empty"))
                    .foregroundStyle(.secondary)
            } else {
                ForEach(recent) { entry in
                    Button {
                        app.seekAbsolute(entry.position)
                    } label: {
                        let timeStr = formatTime(entry.position)
                        let label = entry.chapterTitle.map { "\($0) · \(timeStr)" } ?? timeStr
                        Label(label, systemImage: "clock")
                    }
                }
                Divider()
            }
            Button {
                showFullHistory = true
            } label: {
                Label(String(localized: "player.history_all"), systemImage: "clock.arrow.circlepath")
            }
        } label: {
            Image(systemName: "clock.arrow.circlepath")
                #if os(iOS)
                .font(.system(size: 22))
                #else
                .font(.system(size: 16))
                #endif
        }
        #if os(macOS)
        .menuStyle(.borderlessButton)
        .fixedSize()
        #endif
        .menuIndicator(.hidden)
        .help(String(localized: "player.history_recent"))
    }

    private var sleepMenu: some View {
        Menu {
            sleepOption(title: "Aus", mode: .off)
            Divider()
            sleepOption(title: "10 Minuten", mode: .minutes(10))
            sleepOption(title: "20 Minuten", mode: .minutes(20))
            sleepOption(title: "30 Minuten", mode: .minutes(30))
            sleepOption(title: "1 Stunde", mode: .minutes(60))
            sleepOption(title: endOfPlaybackLabel, mode: .endOfBook)
            if !(app.currentItem?.chapters.isEmpty ?? true) {
                sleepOption(title: "Bis Ende des Kapitels", mode: .endOfChapter)
            }
        } label: {
            Image(systemName: app.player.sleepTimer == .off ? "moon.zzz" : "moon.zzz.fill")
                #if os(iOS)
                .font(.system(size: 22))
                #else
                .font(.system(size: 16))
                #endif
                .foregroundStyle(app.player.sleepTimer == .off ? Color.secondary : Color.accentColor)
        }
        #if os(macOS)
        .menuStyle(.borderlessButton)
        .fixedSize()
        #endif
        .menuIndicator(.hidden)
        .help("Sleep-Timer")
        .disabled(!app.player.isReady)
    }

    @ViewBuilder
    private func sleepOption(title: String, mode: SleepTimerMode) -> some View {
        Button {
            app.player.setSleepTimer(mode)
        } label: {
            if app.player.sleepTimer == mode {
                Label(title, systemImage: "checkmark")
            } else {
                Text(title)
            }
        }
    }

    private var endOfPlaybackLabel: String {
        app.currentItem?.isPodcast == true
            ? "Bis Ende der Folge"
            : "Bis Ende des Hörbuchs"
    }

    /// Intercept Play to surface the resume-prompt if another device pushed a
    /// newer position while we were paused/playing.
    private func handlePlayTap() {
        if !app.player.isPlaying, app.pendingServerProgress != nil {
            showResumePrompt = true
        } else {
            app.togglePlay()
        }
    }

    private var rateMenu: some View {
        Menu {
            ForEach([0.75, 1.0, 1.25, 1.5, 1.75, 2.0], id: \.self) { r in
                Button {
                    app.setRate(Float(r))
                } label: {
                    HStack {
                        Text(String(format: "%.2g×", r))
                        if abs(Double(app.player.rate) - r) < 0.01 {
                            Image(systemName: "checkmark")
                        }
                    }
                }
            }
        } label: {
            Text(String(format: "%.2g×", Double(app.player.rate)))
                .font(.caption.monospacedDigit())
                .padding(.horizontal, 10).padding(.vertical, 5)
                .overlay(Capsule().stroke(Color.secondary.opacity(0.4)))
        }
        #if os(macOS)
        .menuStyle(.borderlessButton)
        .fixedSize()
        .help("Geschwindigkeit")
        #endif
    }

    private var skipForwardImage: String {
        switch skipSeconds {
        case ...10: return "goforward.10"
        case 11...15: return "goforward.15"
        case 16...30: return "goforward.30"
        case 31...45: return "goforward.45"
        case 46...60: return "goforward.60"
        default: return "goforward.90"
        }
    }

    private var skipBackImage: String {
        switch skipSeconds {
        case ...10: return "gobackward.10"
        case 11...15: return "gobackward.15"
        case 16...30: return "gobackward.30"
        case 31...45: return "gobackward.45"
        case 46...60: return "gobackward.60"
        default: return "gobackward.90"
        }
    }

    private func formatTime(_ seconds: Double) -> String {
        guard seconds.isFinite, seconds >= 0 else { return "0:00" }
        let total = Int(seconds)
        let h = total / 3600
        let m = (total % 3600) / 60
        let s = total % 60
        if h > 0 {
            return String(format: "%d:%02d:%02d", h, m, s)
        }
        return String(format: "%d:%02d", m, s)
    }
}

/// Extracted into its own View so that per-second updates to
/// `player.absoluteCurrentTime` / `sleepRemainingSeconds` only re-render this
/// subtree — not the entire `PlayerBar` (which would also rebuild open menus,
/// causing visible flicker on iOS).
private struct ScrubberView: View {
    @Environment(AppState.self) private var app
    @Binding var scrubbing: Bool
    @Binding var scrubValue: Double

    var body: some View {
        VStack(spacing: 2) {
            Slider(
                value: Binding(
                    get: { scrubbing ? scrubValue : app.player.absoluteCurrentTime },
                    set: { scrubValue = $0; scrubbing = true }
                ),
                in: 0...max(app.player.totalDuration, 1),
                onEditingChanged: { editing in
                    if !editing {
                        app.seekAbsolute(scrubValue)
                        scrubbing = false
                    }
                }
            )
            .disabled(!app.player.isReady)
            HStack {
                Text(formatTime(scrubbing ? scrubValue : app.player.absoluteCurrentTime))
                    .font(.caption2.monospacedDigit())
                    .foregroundStyle(.secondary)
                Spacer()
                Text(formatTime(app.player.totalDuration))
                    .font(.caption2.monospacedDigit())
                    .foregroundStyle(.secondary)
            }
            if app.player.sleepTimer != .off {
                HStack(spacing: 4) {
                    Image(systemName: "moon.zzz.fill")
                        .font(.caption2)
                    Text("\(formatTime(app.player.sleepRemainingSeconds)) · endet \(formatWallTime(sleepEndsAt))")
                        .font(.caption2.monospacedDigit())
                }
                .foregroundStyle(.tint)
                .frame(maxWidth: .infinity, alignment: .center)
            }
        }
    }

    private var sleepEndsAt: Date {
        Date().addingTimeInterval(app.player.sleepRemainingSeconds)
    }

    private func formatWallTime(_ date: Date) -> String {
        date.formatted(.dateTime.hour().minute())
    }

    private func formatTime(_ seconds: Double) -> String {
        guard seconds.isFinite, seconds >= 0 else { return "0:00" }
        let total = Int(seconds)
        let h = total / 3600
        let m = (total % 3600) / 60
        let s = total % 60
        if h > 0 {
            return String(format: "%d:%02d:%02d", h, m, s)
        }
        return String(format: "%d:%02d", m, s)
    }
}
