import SwiftUI

struct PodcastDetailView: View {
    @Environment(AppState.self) private var app
    let podcast: LibraryItem

    @State private var episodes: [PodcastEpisode] = []
    @State private var podcastDetail: LibraryItem?
    @State private var isLoading: Bool = true
    @State private var errorMessage: String?
    @State private var searchText: String = ""

    /// Bevorzugt die nach `fetchEpisodes` aufgefrischte Detail-Version
    /// des Podcasts (mit Cover, Description etc.), fällt auf den initial
    /// übergebenen Container zurück solange der Fetch noch läuft oder
    /// fehlgeschlagen ist.
    private var resolvedPodcast: LibraryItem { podcastDetail ?? podcast }

    private var filteredEpisodes: [PodcastEpisode] {
        let q = searchText.trimmingCharacters(in: .whitespacesAndNewlines)
        guard !q.isEmpty else { return episodes }
        return episodes.filter { ep in
            ep.title.localizedCaseInsensitiveContains(q)
        }
    }

    var body: some View {
        VStack(spacing: 0) {
            header
            Divider()
            content
        }
        .navigationTitle(resolvedPodcast.title)
        #if os(iOS)
        .navigationBarTitleDisplayMode(.inline)
        #endif
        .searchable(text: $searchText, placement: .automatic, prompt: "Folge suchen")
        .task { await load() }
    }

    private var header: some View {
        HStack(spacing: 14) {
            if let url = app.client.coverURL(itemId: podcast.id) {
                AsyncImage(url: url) { phase in
                    if let img = phase.image { img.resizable().aspectRatio(contentMode: .fill) }
                    else { Color.gray.opacity(0.3) }
                }
                .frame(width: 72, height: 72)
                .clipShape(RoundedRectangle(cornerRadius: 6))
            }
            VStack(alignment: .leading, spacing: 4) {
                #if os(iOS)
                Text(podcast.title).font(.headline).lineLimit(2)
                #else
                Text(podcast.title).font(.title3).bold().lineLimit(2)
                #endif
                Text(podcast.author).font(.subheadline).foregroundStyle(.secondary).lineLimit(1)
                if !episodes.isEmpty {
                    Text("\(episodes.count) Folge\(episodes.count == 1 ? "" : "n")")
                        .font(.caption)
                        .foregroundStyle(.secondary)
                }
            }
            Spacer()
        }
        .padding(16)
    }

    @ViewBuilder
    private var content: some View {
        if isLoading {
            ProgressView("Lade Folgen …")
                .frame(maxWidth: .infinity, maxHeight: .infinity)
        } else if let err = errorMessage {
            ContentUnavailableView("Fehler", systemImage: "exclamationmark.triangle", description: Text(err))
        } else if episodes.isEmpty {
            ContentUnavailableView("Keine Folgen", systemImage: "music.note.list", description: Text("Dieser Podcast enthält noch keine Folgen."))
        } else if filteredEpisodes.isEmpty {
            ContentUnavailableView.search(text: searchText)
        } else {
            #if os(iOS)
            List {
                ForEach(filteredEpisodes, id: \.id) { ep in
                    EpisodeRow(podcast: resolvedPodcast, episode: ep)
                        .contentShape(Rectangle())
                        .onTapGesture {
                            Task { await app.play(podcast: resolvedPodcast, episode: ep) }
                        }
                }
            }
            .listStyle(.plain)
            #else
            ScrollView {
                LazyVStack(spacing: 0) {
                    ForEach(Array(filteredEpisodes.enumerated()), id: \.element.id) { idx, ep in
                        EpisodeRow(podcast: resolvedPodcast, episode: ep)
                            .contentShape(Rectangle())
                            .onTapGesture {
                                Task { await app.play(podcast: resolvedPodcast, episode: ep) }
                            }
                        if idx < filteredEpisodes.count - 1 {
                            Divider().padding(.leading, 16)
                        }
                    }
                }
            }
            #endif
        }
    }

    private func load() async {
        isLoading = true
        do {
            let (detail, eps) = try await app.client.fetchEpisodes(podcastItemId: podcast.id)
            podcastDetail = detail
            episodes = eps
            errorMessage = nil
        } catch {
            errorMessage = error.localizedDescription
        }
        isLoading = false
    }
}

private struct EpisodeRow: View {
    @Environment(AppState.self) private var app
    let podcast: LibraryItem
    let episode: PodcastEpisode

    private var syntheticItem: LibraryItem {
        var item = LibraryItem(
            id: podcast.id,
            title: episode.title,
            author: podcast.title,
            durationSeconds: episode.durationSeconds > 0 ? episode.durationSeconds : episode.audioFile.durationSeconds,
            audioFiles: [episode.audioFile]
        )
        item.mediaType = "podcast"
        item.episodeId = episode.id
        return item
    }

    private var isListened: Bool {
        app.progressCache[syntheticItem.syncKey]?.isFinished ?? false
    }

    var body: some View {
        HStack(alignment: .top, spacing: 12) {
            Image(systemName: "play.circle.fill")
                .font(.title)
                .foregroundStyle(.tint)
                #if os(macOS)
                .frame(width: 28)
                #endif
                .padding(.top, 2)

            VStack(alignment: .leading, spacing: 4) {
                Text(episode.title)
                    #if os(iOS)
                    .font(.subheadline).bold()
                    #else
                    .font(.headline)
                    #endif
                    .lineLimit(2)
                HStack(spacing: 10) {
                    if let date = episode.formattedDate {
                        Label(date, systemImage: "calendar")
                            .labelStyle(.titleAndIcon)
                            .font(.caption)
                            .foregroundStyle(.secondary)
                    }
                    if episode.durationSeconds > 0 {
                        Label(formatDuration(episode.durationSeconds), systemImage: "clock")
                            .labelStyle(.titleAndIcon)
                            .font(.caption)
                            .foregroundStyle(.secondary)
                    }
                    #if os(macOS)
                    if let season = episode.season, !season.isEmpty {
                        Text("S\(season)").font(.caption).foregroundStyle(.secondary)
                    }
                    if let ep = episode.episode, !ep.isEmpty {
                        Text("F\(ep)").font(.caption).foregroundStyle(.secondary)
                    }
                    #endif
                }
                let frac = app.progressFraction(itemId: podcast.id, episodeId: episode.id)
                if frac > 0 && !isListened {
                    GeometryReader { geo in
                        ZStack(alignment: .leading) {
                            RoundedRectangle(cornerRadius: 1.5).fill(Color.gray.opacity(0.3))
                            RoundedRectangle(cornerRadius: 1.5).fill(Color.green)
                                .frame(width: max(2, geo.size.width * frac))
                        }
                    }
                    .frame(height: 3)
                    .padding(.top, 2)
                    #if os(macOS)
                    .padding(.trailing, 40)
                    #endif
                }
            }
            Spacer(minLength: 0)
            if isListened {
                Image(systemName: "checkmark.circle.fill")
                    .foregroundStyle(.white, .green)
                    .font(.title3)
                    #if os(macOS)
                    .padding(.top, 4)
                    #endif
            }
            downloadButton
                #if os(macOS)
                .frame(width: 32)
                .padding(.top, 4)
                #endif
        }
        #if os(macOS)
        .padding(.horizontal, 16)
        .padding(.vertical, 10)
        #else
        .padding(.vertical, 4)
        #endif
        .contextMenu { mediaItemContextMenu(for: syntheticItem, app: app) }
    }

    @ViewBuilder
    private var downloadButton: some View {
        let key = syntheticItem.downloadKey
        let state = app.downloads.state(for: key)
        switch state {
        case .notDownloaded:
            Button {
                app.downloads.startDownload(item: syntheticItem)
            } label: {
                Image(systemName: "arrow.down.circle")
                    .font(.title3)
                    .foregroundStyle(.secondary)
            }
            .buttonStyle(.plain)
            #if os(macOS)
            .help("Episode für Offline herunterladen")
            #endif
        case .downloading(let p):
            DownloadProgressRing(progress: p)
                .frame(width: 22, height: 22)
                .onTapGesture { app.downloads.cancel(downloadKey: key) }
        case .downloaded:
            // Down-arrow = downloaded; checkmark is reserved for the listened
            // indicator that may sit next to this in the row trailing area.
            Image(systemName: "arrow.down.circle.fill")
                .foregroundStyle(.white, .green)
                .font(.title3)
        case .failed(let msg):
            Button {
                app.downloads.startDownload(item: syntheticItem)
            } label: {
                Image(systemName: "exclamationmark.arrow.circlepath")
                    .font(.title3)
                    .foregroundStyle(.red)
            }
            .buttonStyle(.plain)
            .help("Fehlgeschlagen: \(msg)")
        }
    }

    private func formatDuration(_ seconds: Double) -> String {
        guard seconds.isFinite, seconds > 0 else { return "" }
        let total = Int(seconds)
        let h = total / 3600
        let m = (total % 3600) / 60
        if h > 0 { return "\(h) h \(m) min" }
        return "\(m) min"
    }
}
