import SwiftUI

struct LibraryItemCell: View {
    @Environment(AppState.self) private var app
    let item: LibraryItem
    var dimDownloading: Bool = false

    var body: some View {
        VStack(alignment: .leading, spacing: 2) {
            ZStack(alignment: .bottom) {
                cover
                    .opacity(dimDownloading && isActivelyDownloading ? 0.55 : 1.0)
                    .overlay(alignment: .topTrailing) {
                        if isListened {
                            listenedBadge.padding(4)
                        }
                    }
                    .overlay(alignment: .bottomTrailing) {
                        if !(dimDownloading && isActivelyDownloading) {
                            downloadBadge.padding(.trailing, 4).padding(.bottom, 10)
                        }
                    }
                    .overlay {
                        if dimDownloading, case .downloading(let p) = app.downloads.state(for: item.downloadKey) {
                            LargeDownloadOverlay(progress: p)
                        }
                    }
                if !isListened {
                    CoverProgressBar(fraction: app.progressFraction(itemId: item.id, episodeId: item.episodeId))
                        .padding(.horizontal, 3)
                        .padding(.bottom, 3)
                }
            }
            Text(item.title)
                #if os(iOS)
                .font(.system(size: 11, weight: .bold))
                #else
                .font(.headline)
                #endif
                .lineLimit(2)
                .multilineTextAlignment(.leading)
                .opacity(dimDownloading && isActivelyDownloading ? 0.55 : 1.0)
            HStack(spacing: 4) {
                Text(item.author)
                    .font(.system(size: 9))
                    .foregroundStyle(.secondary)
                    .lineLimit(1)
                if dimDownloading {
                    let bytes = app.downloads.downloadedBytes(for: item.downloadKey)
                    if bytes > 0 {
                        Text("·")
                            .font(.system(size: 9))
                            .foregroundStyle(.secondary)
                        Text(formatBytes(bytes))
                            .font(.system(size: 9, weight: .medium).monospacedDigit())
                            .foregroundStyle(.secondary)
                            .lineLimit(1)
                            .fixedSize()
                    }
                }
            }
            .opacity(dimDownloading && isActivelyDownloading ? 0.55 : 1.0)
        }
        // Ensure the cell fills its full grid column width
        .frame(maxWidth: .infinity, alignment: .leading)
        .contextMenu { mediaItemContextMenu(for: item, app: app) }
    }

    private var isActivelyDownloading: Bool {
        if case .downloading = app.downloads.state(for: item.downloadKey) { return true }
        return false
    }

    /// "Already listened" — server's authoritative finished flag. The
    /// Audiobookshelf server flips this once `markAsFinishedPercentComplete`
    /// (set per library by the admin) is crossed, so we don't need our own
    /// threshold here.
    private var isListened: Bool {
        app.progressCache[item.syncKey]?.isFinished ?? false
    }

    // MARK: - Cover

    private var cover: some View {
        #if os(iOS)
        iOSCover
        #else
        macosCover
        #endif
    }

    #if os(iOS)
    private var iOSCover: some View {
        coverContainer(background: AnyShapeStyle(Color(.systemGray6)))
    }
    #endif

    #if os(macOS)
    private var macosCover: some View {
        coverContainer(background: AnyShapeStyle(.quaternary))
    }
    #endif

    /// Quadratischer Cover-Container: füllt die Spaltenbreite, behält 1:1-Aspektverhältnis
    /// und zeigt das Bild **ohne Verzerrung** (`.scaledToFit`). Nicht-quadratische Cover
    /// bekommen Letterbox-/Pillarbox-Ränder im neutralen Hintergrund.
    private func coverContainer(background: AnyShapeStyle) -> some View {
        Rectangle()
            .fill(background)
            .frame(maxWidth: .infinity)
            .aspectRatio(1, contentMode: .fit)
            .overlay {
                if let url = app.client.coverURL(itemId: item.id) {
                    AsyncImage(url: url) { phase in
                        switch phase {
                        case .success(let img):
                            img.resizable().scaledToFit()
                        case .empty:
                            ProgressView()
                                #if os(macOS)
                                .controlSize(.small)
                                #else
                                .tint(.accentColor)
                                #endif
                        case .failure:
                            Image(systemName: "book.closed")
                                .foregroundStyle(.secondary)
                        @unknown default:
                            EmptyView()
                        }
                    }
                } else {
                    Image(systemName: "book.closed")
                        .foregroundStyle(.secondary)
                }
            }
            .clipShape(RoundedRectangle(cornerRadius: 8))
    }

    // MARK: - Badges

    /// Top-right corner: green checkmark if the server has marked this item
    /// as finished. Surfaces the "I've listened to this" status that used to
    /// be tangled with the download checkmark.
    @ViewBuilder
    private var listenedBadge: some View {
        Image(systemName: "checkmark.circle.fill")
            .foregroundStyle(.white, .green)
            .font(.title3)
            .shadow(radius: 2)
    }

    /// Bottom-right corner: download status. Was previously a checkmark in the
    /// top-right; the checkmark now signals "listened", and the download icon
    /// is a downward arrow so the two states are visually unambiguous.
    @ViewBuilder
    private var downloadBadge: some View {
        let state = app.downloads.state(for: item.downloadKey)
        switch state {
        case .downloaded:
            Image(systemName: "arrow.down.circle.fill")
                .foregroundStyle(.white, .green)
                .font(.title3)
                .shadow(radius: 2)
        case .downloading(let p):
            DownloadProgressRing(progress: p)
                #if os(iOS)
                .frame(width: 22, height: 22)
                #else
                .frame(width: 26, height: 26)
                #endif
        case .failed:
            Image(systemName: "exclamationmark.circle.fill")
                .foregroundStyle(.white, .red)
                .font(.title3)
                .shadow(radius: 2)
        case .notDownloaded:
            EmptyView()
        }
    }

}

// MARK: - Shared components

struct CoverProgressBar: View {
    let fraction: Double

    var body: some View {
        if fraction > 0 {
            GeometryReader { geo in
                ZStack(alignment: .leading) {
                    RoundedRectangle(cornerRadius: 2, style: .continuous)
                        .fill(Color.black.opacity(0.55))
                        .frame(height: 4)
                    RoundedRectangle(cornerRadius: 2, style: .continuous)
                        .fill(Color.accentColor)
                        .frame(width: max(2, geo.size.width * fraction), height: 4)
                }
            }
            .frame(height: 4)
            .shadow(color: .black.opacity(0.35), radius: 1, y: 1)
        }
    }
}

struct DownloadProgressRing: View {
    let progress: Double

    var body: some View {
        ZStack {
            Circle()
                .fill(Color.black.opacity(0.75))
            Circle()
                .stroke(Color.white.opacity(0.25), lineWidth: 3)
                .padding(4)
            Circle()
                .trim(from: 0, to: max(0.03, min(progress, 1)))
                .stroke(Color.accentColor, style: StrokeStyle(lineWidth: 3, lineCap: .round))
                .rotationEffect(.degrees(-90))
                .padding(4)
                .animation(.easeInOut(duration: 0.25), value: progress)
            Image(systemName: "arrow.down")
                .font(.system(size: 10, weight: .bold))
                .foregroundStyle(.white)
        }
        .shadow(color: .black.opacity(0.4), radius: 3, x: 0, y: 1)
    }
}

struct LargeDownloadOverlay: View {
    let progress: Double
    var size: CGFloat = 64

    private var lineWidth: CGFloat { size / 13 }
    private var padding: CGFloat  { size / 7  }
    private var fontSize: CGFloat { max(9, size / 5) }

    var body: some View {
        ZStack {
            Circle()
                .fill(Color.black.opacity(0.65))
            Circle()
                .stroke(Color.white.opacity(0.2), lineWidth: lineWidth)
                .padding(padding)
            Circle()
                .trim(from: 0, to: max(0.03, min(progress, 1)))
                .stroke(Color.accentColor, style: StrokeStyle(lineWidth: lineWidth, lineCap: .round))
                .rotationEffect(.degrees(-90))
                .padding(padding)
                .animation(.easeInOut(duration: 0.3), value: progress)
            Text("\(Int(progress * 100))%")
                .font(.system(size: fontSize, weight: .semibold).monospacedDigit())
                .foregroundStyle(.white)
        }
        .frame(width: size, height: size)
        .shadow(color: .black.opacity(0.45), radius: 6)
    }
}

func formatBytes(_ bytes: Int64) -> String {
    let mb = Double(bytes) / 1_048_576
    if mb >= 1024 { return String(format: "%.1f GB", mb / 1024) }
    if mb >= 1    { return String(format: "%.0f MB", mb) }
    return String(format: "%.0f KB", Double(bytes) / 1024)
}
