//
//  MediaLibraryViews.swift
//  suixinkan
//
//  Created by Codex on 2026/6/23.
//

import PhotosUI
import SwiftUI

/// 媒体库页面，展示素材或样片列表、筛选、统计和上下架入口。
struct MediaLibraryView: View {
    let kind: MediaLibraryKind

    @Environment(\.assetsAPI) private var assetsAPI
    @EnvironmentObject private var router: RouterPath
    @EnvironmentObject private var toastCenter: ToastCenter
    @Environment(\.globalLoading) private var globalLoading

    @StateObject private var viewModel: MediaLibraryViewModel
    @State private var selectedItem: MediaLibraryItem?

    /// 初始化媒体库页面。
    init(kind: MediaLibraryKind = .material) {
        self.kind = kind
        _viewModel = StateObject(wrappedValue: MediaLibraryViewModel(kind: kind))
    }

    var body: some View {
        ScrollView {
            LazyVStack(spacing: AppMetrics.Spacing.medium) {
                filterSection
                statsSection
                contentSection
            }
            .padding(.horizontal, AppMetrics.Spacing.pageHorizontal)
            .padding(.vertical, AppMetrics.Spacing.medium)
        }
        .background(Color(hex: 0xF5F7FA))
        .navigationTitle(kind.title)
        .navigationBarTitleDisplayMode(.inline)
        .toolbar {
            ToolbarItem(placement: .topBarTrailing) {
                Button {
                    router.navigate(to: .home(kind == .sample ? .sampleUpload : .materialUpload))
                } label: {
                    Image(systemName: "plus")
                }
                .accessibilityLabel(kind.uploadTitle)
            }
        }
        .refreshable { await reload(showLoading: false) }
        .task {
            guard viewModel.items.isEmpty else { return }
            await reload(showLoading: true)
        }
        .appNavigationDestination(item: $selectedItem) { item in
            MediaLibraryDetailView(item: item, listViewModel: viewModel)
        }
    }

    /// 筛选区域，包含关键词和审核状态。
    private var filterSection: some View {
        VStack(spacing: AppMetrics.Spacing.small) {
            HStack(spacing: AppMetrics.Spacing.small) {
                Image(systemName: "magnifyingglass")
                    .foregroundStyle(AppDesign.placeholder)
                TextField(kind.searchPlaceholder, text: $viewModel.keyword)
                    .submitLabel(.search)
                    .onSubmit { Task { await reload(showLoading: true) } }
                Button("搜索") {
                    Task { await reload(showLoading: true) }
                }
                .font(.system(size: AppMetrics.FontSize.subheadline, weight: .semibold))
                .foregroundStyle(AppDesign.primary)
            }
            .appInputFieldStyle(cornerRadius: AppMetrics.CornerRadius.input, minHeight: AppMetrics.ControlSize.inputHeight)

            Picker("审核状态", selection: $viewModel.selectedAuditFilter) {
                ForEach(MediaLibraryAuditFilter.allCases) { filter in
                    Text(filter.title).tag(filter)
                }
            }
            .pickerStyle(.segmented)
            .onChange(of: viewModel.selectedAuditFilter) { _ in
                Task { await reload(showLoading: true) }
            }
        }
    }

    /// 统计区域，展示媒体库订单概览。
    @ViewBuilder
    private var statsSection: some View {
        if let info = viewModel.orderInfo {
            HStack(spacing: AppMetrics.Spacing.small) {
                MediaStatCard(title: "订单数", value: "\(info.totalNum)")
                MediaStatCard(title: "均价", value: String(format: "%.2f", info.avgOrderAmount))
                MediaStatCard(title: "退款", value: String(format: "%.2f", info.refundTotal))
            }
        }
    }

    /// 列表内容区域。
    @ViewBuilder
    private var contentSection: some View {
        if viewModel.items.isEmpty {
            AssetsEmptyState(title: kind.emptyTitle, message: viewModel.errorMessage ?? "当前筛选条件下没有\(kind == .sample ? "样片" : "素材")。")
        } else {
            ForEach(viewModel.items) { item in
                MediaLibraryCard(item: item) {
                    selectedItem = item
                } toggleAction: {
                    Task { await toggleListing(item) }
                }
                .onAppear {
                    guard item.id == viewModel.items.last?.id else { return }
                    Task { await viewModel.loadMore(api: assetsAPI) }
                }
            }

            if viewModel.isLoadingMore {
                ProgressView()
                    .padding(.vertical, AppMetrics.Spacing.small)
            }
        }
    }

    /// 重新加载媒体库列表。
    private func reload(showLoading: Bool) async {
        await globalLoading.withOptionalLoading(showLoading) {
            await viewModel.reload(api: assetsAPI)
        }
        if let message = viewModel.errorMessage {
            toastCenter.show(message)
        }
    }

    /// 切换媒体库上下架状态。
    private func toggleListing(_ item: MediaLibraryItem) async {
        let success = await globalLoading.withLoading {
            await viewModel.toggleListing(item, api: assetsAPI)
        }
        toastCenter.show(success ? "状态已更新" : (viewModel.errorMessage ?? "操作失败"))
    }
}

/// 媒体库统计卡片。
private struct MediaStatCard: View {
    let title: String
    let value: String

    var body: some View {
        VStack(alignment: .leading, spacing: AppMetrics.Spacing.xxSmall) {
            Text(title)
                .font(.system(size: AppMetrics.FontSize.caption))
                .foregroundStyle(AppDesign.textSecondary)
            Text(value)
                .font(.system(size: AppMetrics.FontSize.title3, weight: .semibold))
                .foregroundStyle(AppDesign.textPrimary)
        }
        .frame(maxWidth: .infinity, alignment: .leading)
        .padding(AppMetrics.Spacing.medium)
        .background(.white, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.card))
    }
}

/// 媒体库列表卡片。
private struct MediaLibraryCard: View {
    let item: MediaLibraryItem
    let openAction: () -> Void
    let toggleAction: () -> Void

    var body: some View {
        Button(action: openAction) {
            HStack(spacing: AppMetrics.Spacing.medium) {
                RemoteImage(urlString: item.coverUrl, contentMode: .fill) {
                    Image(systemName: "photo")
                        .font(.system(size: 28, weight: .semibold))
                        .foregroundStyle(AppDesign.primary)
                        .frame(maxWidth: .infinity, maxHeight: .infinity)
                        .background(AppDesign.primarySoft)
                }
                .frame(width: 74, height: 74)
                .clipShape(RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.input))

                VStack(alignment: .leading, spacing: AppMetrics.Spacing.xxSmall) {
                    Text(item.name)
                        .font(.system(size: AppMetrics.FontSize.body, weight: .semibold))
                        .foregroundStyle(AppDesign.textPrimary)
                        .lineLimit(2)
                    Text(item.scenicSpotName.assetsNonEmpty ?? item.projectName.assetsNonEmpty ?? "未关联打卡点")
                        .font(.system(size: AppMetrics.FontSize.caption))
                        .foregroundStyle(AppDesign.textSecondary)
                        .lineLimit(1)
                    HStack(spacing: AppMetrics.Spacing.xSmall) {
                        MediaStatusBadge(text: item.status.auditTitle, color: item.status.auditColor)
                        MediaStatusBadge(text: item.listing.title, color: item.listing == .online ? AppDesign.success : AppDesign.placeholder)
                    }
                }

                Spacer()

                Button(action: toggleAction) {
                    Image(systemName: item.listing == .online ? "arrow.down.circle" : "arrow.up.circle")
                        .frame(width: AppMetrics.ControlSize.iconTapArea, height: AppMetrics.ControlSize.iconTapArea)
                }
                .buttonStyle(.plain)
                .foregroundStyle(item.isApproved ? AppDesign.primary : AppDesign.placeholder)
                .disabled(!item.isApproved)
            }
            .padding(AppMetrics.Spacing.medium)
            .background(.white, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.card))
        }
        .buttonStyle(.plain)
    }
}

/// 媒体库状态标签。
private struct MediaStatusBadge: View {
    let text: String
    let color: Color

    var body: some View {
        Text(text)
            .font(.system(size: AppMetrics.FontSize.caption, weight: .semibold))
            .foregroundStyle(color)
            .padding(.horizontal, AppMetrics.Spacing.xSmall)
            .padding(.vertical, AppMetrics.Spacing.xxSmall)
            .background(color.opacity(0.1), in: Capsule())
    }
}

/// 媒体库详情页面，展示基础信息、统计、媒体文件和操作按钮。
struct MediaLibraryDetailView: View {
    let item: MediaLibraryItem
    let listViewModel: MediaLibraryViewModel

    @Environment(\.assetsAPI) private var assetsAPI
    @EnvironmentObject private var toastCenter: ToastCenter
    @Environment(\.dismiss) private var dismiss
    @Environment(\.globalLoading) private var globalLoading

    @State private var isEditing = false

    var body: some View {
        ScrollView {
            VStack(spacing: AppMetrics.Spacing.medium) {
                headerSection
                mediaSection
                actionSection
            }
            .padding(.horizontal, AppMetrics.Spacing.pageHorizontal)
            .padding(.vertical, AppMetrics.Spacing.medium)
        }
        .background(Color(hex: 0xF5F7FA))
        .navigationTitle(listViewModel.kind.detailTitle)
        .navigationBarTitleDisplayMode(.inline)
        .task {
            await globalLoading.withOptionalLoading(listViewModel.selectedDetail?.id != item.id) {
                await listViewModel.loadDetail(id: item.id, api: assetsAPI)
            }
        }
        .sheet(isPresented: $isEditing) {
            MediaLibraryUploadView(kind: listViewModel.kind, editingDetail: listViewModel.selectedDetail)
        }
    }

    /// 媒体库详情头部信息。
    private var headerSection: some View {
        VStack(alignment: .leading, spacing: AppMetrics.Spacing.medium) {
            RemoteImage(urlString: currentCoverURL, contentMode: .fill) {
                Image(systemName: "photo")
                    .font(.system(size: 40, weight: .semibold))
                    .foregroundStyle(AppDesign.primary)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background(AppDesign.primarySoft)
            }
            .frame(height: 180)
            .clipShape(RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.card))

            VStack(alignment: .leading, spacing: AppMetrics.Spacing.xSmall) {
                Text(currentName)
                    .font(.system(size: AppMetrics.FontSize.title2, weight: .semibold))
                    .foregroundStyle(AppDesign.textPrimary)
                Text(listViewModel.selectedDetail?.description.assetsNonEmpty ?? "暂无描述")
                    .font(.system(size: AppMetrics.FontSize.subheadline))
                    .foregroundStyle(AppDesign.textSecondary)
                HStack {
                    MediaStatusBadge(text: currentStatus.auditTitle, color: currentStatus.auditColor)
                    MediaStatusBadge(text: currentListing.title, color: currentListing == .online ? AppDesign.success : AppDesign.placeholder)
                }
                if listViewModel.kind == .sample {
                    sampleSummaryBadges
                }
                detailMetaRows
            }
        }
        .padding(AppMetrics.Spacing.medium)
        .background(.white, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.card))
    }

    /// 媒体库详情元信息。
    private var detailMetaRows: some View {
        VStack(alignment: .leading, spacing: AppMetrics.Spacing.xxSmall) {
            if let detail = listViewModel.selectedDetail {
                MediaDetailMetaRow(title: "项目", value: detail.projectName.assetsNonEmpty ?? "--")
                MediaDetailMetaRow(title: "景区", value: detail.scenicName.assetsNonEmpty ?? "--")
                MediaDetailMetaRow(title: "上传人", value: detail.uploaderName.assetsNonEmpty ?? "--")
                MediaDetailMetaRow(title: "创建时间", value: detail.createdAt.assetsNonEmpty ?? "--")
            } else {
                MediaDetailMetaRow(title: "关联", value: item.projectName.assetsNonEmpty ?? item.scenicSpotName.assetsNonEmpty ?? "--")
                MediaDetailMetaRow(title: "创建时间", value: item.createdAt.assetsNonEmpty ?? "--")
            }
        }
    }

    /// 样片媒体数量摘要。
    private var sampleSummaryBadges: some View {
        let media = listViewModel.selectedDetail?.mediaList ?? []
        let videoCount = media.filter(\.isVideo).count
        let imageCount = media.count - videoCount
        return HStack(spacing: AppMetrics.Spacing.xSmall) {
            MediaStatusBadge(text: "图片 \(imageCount)", color: AppDesign.success)
            MediaStatusBadge(text: "视频 \(videoCount)", color: AppDesign.warning)
            MediaStatusBadge(text: "总计 \(media.count)", color: AppDesign.primary)
        }
    }

    /// 媒体库媒体文件区域。
    @ViewBuilder
    private var mediaSection: some View {
        let media = listViewModel.selectedDetail?.mediaList ?? []
        if media.isEmpty {
            AssetsEmptyState(title: "暂无媒体文件", message: "详情接口暂未返回\(listViewModel.kind == .sample ? "样片" : "素材")文件。")
        } else {
            LazyVGrid(columns: [GridItem(.adaptive(minimum: 110), spacing: AppMetrics.Spacing.small)], spacing: AppMetrics.Spacing.small) {
                ForEach(media) { file in
                    VStack(alignment: .leading, spacing: AppMetrics.Spacing.xxSmall) {
                        RemoteImage(urlString: file.thumbnailUrl.assetsNonEmpty ?? file.showUrl.assetsNonEmpty ?? file.ossUrl, contentMode: .fill) {
                            Image(systemName: file.isVideo ? "play.rectangle.fill" : "photo")
                                .font(.system(size: 28, weight: .semibold))
                                .foregroundStyle(AppDesign.primary)
                                .frame(maxWidth: .infinity, maxHeight: .infinity)
                                .background(AppDesign.primarySoft)
                        }
                        .frame(height: 96)
                        .clipShape(RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.input))
                        Text(file.originalName)
                            .font(.system(size: AppMetrics.FontSize.caption))
                            .foregroundStyle(AppDesign.textSecondary)
                            .lineLimit(1)
                    }
                    .padding(AppMetrics.Spacing.xSmall)
                    .background(.white, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.input))
                }
            }
        }
    }

    /// 操作按钮区域。
    private var actionSection: some View {
        VStack(spacing: AppMetrics.Spacing.small) {
            if listViewModel.kind == .material {
                Button("编辑素材") {
                    isEditing = true
                }
                .frame(maxWidth: .infinity, minHeight: AppMetrics.ControlSize.primaryButtonHeight)
                .background(AppDesign.primary, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.button))
                .foregroundStyle(.white)
            }

            HStack(spacing: AppMetrics.Spacing.small) {
                Button(currentListing == .online ? "下架" : "上架") {
                    Task { await toggleListing() }
                }
                .frame(maxWidth: .infinity, minHeight: AppMetrics.ControlSize.sheetButtonHeight)
                .background(Color.white, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.button))

                Button("删除", role: .destructive) {
                    Task { await delete() }
                }
                .frame(maxWidth: .infinity, minHeight: AppMetrics.ControlSize.sheetButtonHeight)
                .background(Color.white, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.button))
            }
        }
        .font(.system(size: AppMetrics.FontSize.body, weight: .semibold))
    }

    private var currentName: String { listViewModel.selectedDetail?.name ?? item.name }
    private var currentCoverURL: String { listViewModel.selectedDetail?.coverUrl ?? item.coverUrl }
    private var currentStatus: Int { listViewModel.selectedDetail?.status ?? item.status }
    private var currentListing: MediaListingStatus { listViewModel.selectedDetail?.listing ?? item.listing }

    /// 切换详情媒体库上下架状态。
    private func toggleListing() async {
        guard let detail = listViewModel.selectedDetail else { return }
        let success = await globalLoading.withLoading {
            await listViewModel.toggleListing(detail: detail, api: assetsAPI)
        }
        toastCenter.show(success ? "状态已更新" : (listViewModel.errorMessage ?? "操作失败"))
    }

    /// 删除媒体库项目并返回列表。
    private func delete() async {
        let success = await globalLoading.withLoading {
            await listViewModel.delete(id: item.id, api: assetsAPI)
        }
        toastCenter.show(success ? "已删除" : (listViewModel.errorMessage ?? "删除失败"))
        if success { dismiss() }
    }
}

/// 媒体库上传或编辑页面，负责选择封面、媒体文件、打卡点和样片项目后提交。
struct MediaLibraryUploadView: View {
    let kind: MediaLibraryKind
    let editingDetail: MediaLibraryDetail?

    @EnvironmentObject private var accountContext: AccountContext
    @EnvironmentObject private var scenicSpotContext: ScenicSpotContext
    @Environment(\.assetsAPI) private var assetsAPI
    @Environment(\.ossUploadService) private var uploadService
    @EnvironmentObject private var toastCenter: ToastCenter
    @Environment(\.dismiss) private var dismiss
    @Environment(\.globalLoading) private var globalLoading

    @StateObject private var viewModel: MediaLibraryEditorViewModel
    @State private var coverSelection: PhotosPickerItem?
    @State private var mediaSelection: [PhotosPickerItem] = []

    /// 初始化媒体库上传或编辑页面。
    init(kind: MediaLibraryKind = .material, editingDetail: MediaLibraryDetail? = nil) {
        self.kind = kind
        self.editingDetail = editingDetail
        _viewModel = StateObject(wrappedValue: MediaLibraryEditorViewModel(detail: editingDetail))
    }

    var body: some View {
        ScrollView {
            VStack(spacing: AppMetrics.Spacing.medium) {
                formSection
                spotSection
                projectSection
                coverSection
                mediaSection
                submitButton
            }
            .padding(.horizontal, AppMetrics.Spacing.pageHorizontal)
            .padding(.vertical, AppMetrics.Spacing.medium)
        }
        .background(Color(hex: 0xF5F7FA))
        .navigationTitle(viewModel.isEditing ? "编辑素材" : kind.uploadTitle)
        .navigationBarTitleDisplayMode(.inline)
        .task {
            guard kind == .sample, viewModel.projects.isEmpty else { return }
            await viewModel.loadProjects(scenicId: accountContext.currentScenic?.id, api: assetsAPI)
            if let message = viewModel.errorMessage {
                toastCenter.show(message)
            }
        }
        .onChange(of: coverSelection) { item in
            Task { await loadCover(item) }
        }
        .onChange(of: mediaSelection) { items in
            Task { await loadMedia(items) }
        }
    }

    /// 表单基础信息区域。
    private var formSection: some View {
        VStack(spacing: AppMetrics.Spacing.small) {
            TextField(kind.namePlaceholder, text: $viewModel.name)
                .appInputFieldStyle(cornerRadius: AppMetrics.CornerRadius.input, minHeight: AppMetrics.ControlSize.inputHeight)

            if kind == .material {
                TextField("标签，用逗号分隔", text: $viewModel.tagsText)
                    .appInputFieldStyle(cornerRadius: AppMetrics.CornerRadius.input, minHeight: AppMetrics.ControlSize.inputHeight)
            }

            TextField("描述", text: $viewModel.description, axis: .vertical)
                .lineLimit(3...5)
                .appInputFieldStyle(cornerRadius: AppMetrics.CornerRadius.input, minHeight: 88)
        }
    }

    /// 样片关联项目选择区域。
    @ViewBuilder
    private var projectSection: some View {
        if kind == .sample {
            VStack(alignment: .leading, spacing: AppMetrics.Spacing.small) {
                Text("关联项目")
                    .font(.system(size: AppMetrics.FontSize.body, weight: .semibold))
                if viewModel.projects.isEmpty {
                    AssetsEmptyState(title: "暂无可选项目", message: viewModel.isLoadingProjects ? "正在加载项目..." : "当前景区暂未加载到可关联项目。")
                } else {
                    Picker("关联项目", selection: $viewModel.selectedProjectId) {
                        Text("请选择").tag(nil as Int?)
                        ForEach(viewModel.projects) { project in
                            Text(project.name.assetsNonEmpty ?? "项目\(project.id)").tag(Optional(project.id))
                        }
                    }
                    .pickerStyle(.menu)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding(AppMetrics.Spacing.medium)
                    .background(.white, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.card))
                }
            }
        }
    }

    /// 打卡点选择区域。
    private var spotSection: some View {
        VStack(alignment: .leading, spacing: AppMetrics.Spacing.small) {
            Text("打卡点")
                .font(.system(size: AppMetrics.FontSize.body, weight: .semibold))
            if scenicSpotContext.spots.isEmpty {
                AssetsEmptyState(title: "暂无打卡点", message: "当前景区暂未加载到可选打卡点。")
            } else {
                Picker("打卡点", selection: $viewModel.selectedSpotId) {
                    Text("请选择").tag(nil as Int?)
                    ForEach(scenicSpotContext.spots) { spot in
                        Text(spot.name).tag(Optional(spot.id))
                    }
                }
                .pickerStyle(.menu)
                .frame(maxWidth: .infinity, alignment: .leading)
                .padding(AppMetrics.Spacing.medium)
                .background(.white, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.card))
            }
        }
    }

    /// 封面选择区域。
    private var coverSection: some View {
        VStack(alignment: .leading, spacing: AppMetrics.Spacing.small) {
            Text("封面")
                .font(.system(size: AppMetrics.FontSize.body, weight: .semibold))
            PhotosPicker(selection: $coverSelection, matching: .images) {
                ZStack {
                    RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.card)
                        .fill(Color.white)
                    if let coverFile = viewModel.coverFile, let image = UIImage(data: coverFile.data) {
                        Image(uiImage: image)
                            .resizable()
                            .scaledToFill()
                    } else if let url = editingDetail?.coverUrl, !url.isEmpty {
                        RemoteImage(urlString: url, contentMode: .fill) {
                            Image(systemName: "photo")
                                .font(.system(size: 34, weight: .semibold))
                                .foregroundStyle(AppDesign.primary)
                        }
                    } else {
                        Label("选择封面", systemImage: "photo.badge.plus")
                            .font(.system(size: AppMetrics.FontSize.body, weight: .semibold))
                            .foregroundStyle(AppDesign.primary)
                    }
                }
                .frame(height: 170)
                .clipShape(RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.card))
            }
        }
    }

    /// 媒体文件选择区域。
    private var mediaSection: some View {
        VStack(alignment: .leading, spacing: AppMetrics.Spacing.small) {
            HStack {
                Text("媒体文件")
                    .font(.system(size: AppMetrics.FontSize.body, weight: .semibold))
                Spacer()
                PhotosPicker(selection: $mediaSelection, maxSelectionCount: 20, matching: .any(of: [.images, .videos])) {
                    Label("添加", systemImage: "plus")
                }
            }

            if viewModel.mediaFiles.isEmpty {
                AssetsEmptyState(title: "未选择\(kind == .sample ? "样片" : "素材")文件", message: "可选择图片或视频，提交前会先上传到 OSS。")
            } else {
                LazyVGrid(columns: [GridItem(.adaptive(minimum: 96), spacing: AppMetrics.Spacing.small)], spacing: AppMetrics.Spacing.small) {
                    ForEach(viewModel.mediaFiles) { file in
                        ZStack(alignment: .topTrailing) {
                            MediaLocalPreview(file: file)
                            Button {
                                viewModel.removeMediaFile(id: file.id)
                            } label: {
                                Image(systemName: "xmark.circle.fill")
                                    .foregroundStyle(.white, Color.black.opacity(0.5))
                            }
                            .padding(AppMetrics.Spacing.xxSmall)
                        }
                    }
                }
            }
        }
    }

    /// 提交按钮。
    private var submitButton: some View {
        Button {
            Task { await submit() }
        } label: {
            Text(viewModel.isSubmitting ? "提交中..." : "提交")
                .frame(maxWidth: .infinity, minHeight: AppMetrics.ControlSize.primaryButtonHeight)
        }
        .font(.system(size: AppMetrics.FontSize.body, weight: .semibold))
        .foregroundStyle(.white)
        .background(AppDesign.primary, in: RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.button))
        .disabled(viewModel.isSubmitting)
    }

    /// 加载封面选择结果。
    private func loadCover(_ item: PhotosPickerItem?) async {
        guard let item else { return }
        let files = await AssetPickerLoader.loadMediaFiles(from: [item])
        if let file = files.first {
            viewModel.setCover(file)
        }
    }

    /// 加载媒体选择结果。
    private func loadMedia(_ items: [PhotosPickerItem]) async {
        guard !items.isEmpty else { return }
        defer { mediaSelection = [] }
        let files = await AssetPickerLoader.loadMediaFiles(from: items)
        viewModel.addMediaFiles(files)
    }

    /// 提交媒体库表单。
    private func submit() async {
        let success = await globalLoading.withLoading {
            await viewModel.submit(
                kind: kind,
                scenicId: accountContext.currentScenic?.id,
                api: assetsAPI,
                uploadService: uploadService
            )
        }
        toastCenter.show(success ? "\(kind == .sample ? "样片" : "素材")已提交" : (viewModel.errorMessage ?? "提交失败"))
        if success { dismiss() }
    }
}

/// 媒体库详情元信息行。
private struct MediaDetailMetaRow: View {
    let title: String
    let value: String

    var body: some View {
        HStack(spacing: AppMetrics.Spacing.small) {
            Text(title)
                .font(.system(size: AppMetrics.FontSize.caption))
                .foregroundStyle(AppDesign.textSecondary)
            Text(value)
                .font(.system(size: AppMetrics.FontSize.caption))
                .foregroundStyle(AppDesign.textPrimary)
                .lineLimit(1)
            Spacer(minLength: 0)
        }
    }
}

/// 本地素材文件预览卡。
private struct MediaLocalPreview: View {
    let file: MediaLocalUploadFile

    var body: some View {
        ZStack {
            if file.fileType == 2, let image = UIImage(data: file.data) {
                Image(uiImage: image)
                    .resizable()
                    .scaledToFill()
            } else {
                Image(systemName: "play.rectangle.fill")
                    .font(.system(size: 30, weight: .semibold))
                    .foregroundStyle(AppDesign.primary)
                    .frame(maxWidth: .infinity, maxHeight: .infinity)
                    .background(AppDesign.primarySoft)
            }
        }
        .frame(height: 96)
        .clipShape(RoundedRectangle(cornerRadius: AppMetrics.CornerRadius.input))
    }
}

private extension Int {
    /// 返回素材审核状态文案。
    var auditTitle: String {
        switch self {
        case 1:
            "已通过"
        case 2:
            "未通过"
        default:
            "待审核"
        }
    }

    /// 返回素材审核状态颜色。
    var auditColor: Color {
        switch self {
        case 1:
            AppDesign.success
        case 2:
            .red
        default:
            AppDesign.warning
        }
    }
}
