//
//  SampleDetailViewController.swift
//  suixinkan
//

import AVFoundation
import Kingfisher
import SnapKit
import UIKit

/// 样片详情页，对齐 Android `SampleDetailScreen`。
final class SampleDetailViewController: SampleBaseViewController {
    private let viewModel: SampleDetailViewModel
    private let api: any SampleManagementServing

    private let scrollView = UIScrollView()
    private let contentStack = UIStackView()
    private let carouselView = SampleMediaCarouselView()
    private let sampleInfoSection = SampleDetailSampleInfoSection()
    private let uploaderSection = SampleDetailUploaderSection()
    private let scenicSection = SampleDetailImageInfoSection(title: "打卡地点")
    private let projectSection = SampleDetailImageInfoSection(title: "样片绑定套餐")
    private let reviewSection = SampleDetailReviewSection()
    private let bottomSpacer = UIView()

    /// 初始化样片详情页。
    init(
        sampleId: Int,
        api: (any SampleManagementServing)? = nil
    ) {
        viewModel = SampleDetailViewModel(sampleId: sampleId)
        self.api = api ?? NetworkServices.shared.sampleManagementAPI
        super.init(nibName: nil, bundle: nil)
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func setupNavigationBar() {
        super.setupNavigationBar()
        setSampleTitle("样片详情")
    }

    override func setupUI() {
        super.setupUI()
        view.backgroundColor = SampleManagementUITokens.pageBackground
        contentStack.axis = .vertical
        contentStack.spacing = 0
        scrollView.backgroundColor = SampleManagementUITokens.pageBackground

        view.addSubview(scrollView)
        scrollView.addSubview(contentStack)
        contentStack.addArrangedSubview(carouselView)
        contentStack.addArrangedSubview(sampleInfoSection)
        contentStack.addArrangedSubview(uploaderSection)
        contentStack.addArrangedSubview(scenicSection)
        contentStack.addArrangedSubview(projectSection)
        contentStack.addArrangedSubview(reviewSection)
        contentStack.addArrangedSubview(bottomSpacer)
        contentStack.setCustomSpacing(12, after: sampleInfoSection)
        contentStack.setCustomSpacing(12, after: uploaderSection)
        contentStack.setCustomSpacing(12, after: scenicSection)
        contentStack.setCustomSpacing(12, after: projectSection)
        bottomSpacer.snp.makeConstraints { make in make.height.equalTo(16) }
    }

    override func setupConstraints() {
        scrollView.snp.makeConstraints { make in
            make.top.equalTo(view.safeAreaLayoutGuide)
            make.leading.trailing.bottom.equalTo(view.safeAreaLayoutGuide)
        }
        contentStack.snp.makeConstraints { make in
            make.edges.equalToSuperview()
            make.width.equalTo(scrollView)
        }
        carouselView.snp.makeConstraints { make in
            make.height.equalTo(carouselView.snp.width).multipliedBy(9.0 / 16.0)
        }
    }

    override func bindActions() {
        viewModel.onStateChange = { [weak self] in
            Task { @MainActor in self?.applyViewModel() }
        }
        viewModel.onShowMessage = { [weak self] message in
            Task { @MainActor in self?.showToast(message) }
        }
    }

    override func viewDidLoad() {
        super.viewDidLoad()
        Task { await viewModel.load(api: api) }
    }

    override func viewWillDisappear(_ animated: Bool) {
        super.viewWillDisappear(animated)
        carouselView.pause()
    }

    private func applyViewModel() {
        if viewModel.isLoading && viewModel.detail == nil {
            showLoading()
        } else {
            hideLoading()
        }
        guard let detail = viewModel.detail else { return }
        carouselView.apply(media: detail.mediaList ?? [], fallbackType: detail.type ?? 1)
        sampleInfoSection.apply(detail: detail)
        uploaderSection.apply(detail: detail)
        scenicSection.applyScenic(detail: detail)
        projectSection.applyProject(detail: detail)
        reviewSection.apply(detail: detail)
    }
}

/// 样片详情媒体轮播，支持图片自动切换与视频播完切换。
private final class SampleMediaCarouselView: UIView, UIScrollViewDelegate {
    private let scrollView = UIScrollView()
    private let indicatorStack = UIStackView()
    private let placeholderLabel = UILabel()
    private var media: [SampleDetailMediaItem] = []
    private var renderedMediaIndices: [Int] = []
    private var currentRenderedPage = 0
    private var fallbackType = 1
    private var pageViews: [UIView] = []
    private var players: [Int: AVPlayer] = [:]
    private var playerViews: [Int: SamplePlayerLayerView] = [:]
    private var timer: Timer?

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = UIColor(hex: 0xF5F5F5)
        scrollView.isPagingEnabled = true
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.delegate = self
        scrollView.bounces = false
        indicatorStack.axis = .horizontal
        indicatorStack.spacing = 6
        indicatorStack.alignment = .center
        placeholderLabel.text = "暂无媒体"
        placeholderLabel.font = .systemFont(ofSize: 14)
        placeholderLabel.textColor = AppColor.textSecondary
        placeholderLabel.textAlignment = .center

        addSubview(scrollView)
        addSubview(indicatorStack)
        addSubview(placeholderLabel)
        scrollView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        indicatorStack.snp.makeConstraints { make in
            make.centerX.equalToSuperview()
            make.bottom.equalToSuperview().inset(12)
            make.height.equalTo(8)
        }
        placeholderLabel.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    override func layoutSubviews() {
        super.layoutSubviews()
        layoutPages()
    }

    /// 应用媒体列表。
    func apply(media: [SampleDetailMediaItem], fallbackType: Int) {
        stopTimer()
        NotificationCenter.default.removeObserver(self)
        players.values.forEach { $0.pause() }
        players.removeAll()
        playerViews.removeAll()
        pageViews.removeAll()
        self.media = media
        self.fallbackType = fallbackType
        renderedMediaIndices = media.count > 1
            ? [media.count - 1] + Array(media.indices) + [0]
            : Array(media.indices)
        currentRenderedPage = media.count > 1 ? 1 : 0
        scrollView.subviews.forEach { $0.removeFromSuperview() }
        rebuildIndicators(currentIndex: 0)
        indicatorStack.isHidden = media.count <= 1
        placeholderLabel.isHidden = !media.isEmpty
        scrollView.isHidden = media.isEmpty
        guard !media.isEmpty else { return }

        for (renderedIndex, mediaIndex) in renderedMediaIndices.enumerated() {
            let item = media[mediaIndex]
            let page = UIView()
            page.backgroundColor = UIColor(hex: 0xF5F5F5)
            page.tag = renderedIndex
            scrollView.addSubview(page)
            pageViews.append(page)
            if item.resolvedType(fallback: fallbackType) == 2 {
                addVideo(urlText: item.displayURL, to: page, index: renderedIndex)
            } else {
                addImage(urlText: item.displayURL, to: page)
            }
        }
        setNeedsLayout()
        layoutIfNeeded()
        playCurrentPageIfNeeded()
    }

    /// 暂停当前轮播。
    func pause() {
        stopTimer()
        players.values.forEach { $0.pause() }
    }

    func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
        updateCurrentPage()
    }

    func scrollViewDidEndScrollingAnimation(_ scrollView: UIScrollView) {
        updateCurrentPage()
    }

    func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
        stopTimer()
    }

    private func addImage(urlText: String, to page: UIView) {
        let imageView = UIImageView()
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        page.addSubview(imageView)
        imageView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        if let url = URL(string: urlText), !urlText.isEmpty {
            imageView.kf.setImage(with: url, placeholder: UIImage(named: "sample_img_loading")) { result in
                if case .failure = result {
                    imageView.image = UIImage(named: "sample_img_error")
                }
            }
        } else {
            imageView.image = UIImage(named: "sample_img_error")
        }
    }

    private func addVideo(urlText: String, to page: UIView, index: Int) {
        guard let url = URL(string: urlText), !urlText.isEmpty else {
            addImage(urlText: "", to: page)
            return
        }
        let player = AVPlayer(url: url)
        players[index] = player
        let playerView = SamplePlayerLayerView()
        playerView.configure(player: player)
        playerViews[index] = playerView
        page.addSubview(playerView)
        playerView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        NotificationCenter.default.addObserver(
            self,
            selector: #selector(videoDidEnd(_:)),
            name: .AVPlayerItemDidPlayToEndTime,
            object: player.currentItem
        )
    }

    private func layoutPages() {
        let size = bounds.size
        guard size.width > 0, size.height > 0 else { return }
        for (index, page) in pageViews.enumerated() {
            page.frame = CGRect(x: CGFloat(index) * size.width, y: 0, width: size.width, height: size.height)
        }
        scrollView.contentSize = CGSize(width: size.width * CGFloat(renderedMediaIndices.count), height: size.height)
        scrollView.contentOffset = CGPoint(x: CGFloat(currentRenderedPage) * size.width, y: 0)
    }

    private func updateCurrentPage() {
        guard bounds.width > 0, !renderedMediaIndices.isEmpty else { return }
        let page = Int(round(scrollView.contentOffset.x / bounds.width))
        currentRenderedPage = max(0, min(page, renderedMediaIndices.count - 1))
        if media.count > 1, currentRenderedPage == 0 {
            currentRenderedPage = media.count
            scrollView.setContentOffset(CGPoint(x: CGFloat(currentRenderedPage) * bounds.width, y: 0), animated: false)
        } else if media.count > 1, currentRenderedPage == renderedMediaIndices.count - 1 {
            currentRenderedPage = 1
            scrollView.setContentOffset(CGPoint(x: bounds.width, y: 0), animated: false)
        }
        rebuildIndicators(currentIndex: currentMediaIndex)
        playCurrentPageIfNeeded()
    }

    private func playCurrentPageIfNeeded() {
        stopTimer()
        players.forEach { index, player in
            if index == currentRenderedPage {
                player.seek(to: .zero)
                player.play()
                playerViews[index]?.setPlaying(true)
            } else {
                player.pause()
                playerViews[index]?.setPlaying(false)
            }
        }
        guard !media.isEmpty else { return }
        if media[currentMediaIndex].resolvedType(fallback: fallbackType) == 1 {
            startImageTimer()
        }
    }

    private func startImageTimer() {
        guard media.count > 1 else { return }
        timer = Timer.scheduledTimer(withTimeInterval: 3, repeats: false) { [weak self] _ in
            self?.advancePage()
        }
    }

    private func stopTimer() {
        timer?.invalidate()
        timer = nil
    }

    @objc private func videoDidEnd(_ notification: Notification) {
        guard let item = notification.object as? AVPlayerItem,
              players[currentRenderedPage]?.currentItem === item else { return }
        advancePage()
    }

    private func advancePage() {
        guard media.count > 1, bounds.width > 0 else { return }
        let nextPage = min(currentRenderedPage + 1, renderedMediaIndices.count - 1)
        currentRenderedPage = nextPage
        scrollView.setContentOffset(CGPoint(x: CGFloat(nextPage) * bounds.width, y: 0), animated: true)
        if !scrollView.isDragging && !scrollView.isDecelerating {
            DispatchQueue.main.asyncAfter(deadline: .now() + 0.25) { [weak self] in
                self?.playCurrentPageIfNeeded()
            }
        }
    }

    private var currentMediaIndex: Int {
        guard renderedMediaIndices.indices.contains(currentRenderedPage) else { return 0 }
        return renderedMediaIndices[currentRenderedPage]
    }

    private func rebuildIndicators(currentIndex: Int) {
        indicatorStack.arrangedSubviews.forEach { $0.removeFromSuperview() }
        guard media.count > 1 else { return }
        for index in media.indices {
            let dot = UIView()
            let size: CGFloat = index == currentIndex ? 8 : 6
            dot.backgroundColor = index == currentIndex ? .white : UIColor.white.withAlphaComponent(0.5)
            dot.layer.cornerRadius = size / 2
            dot.snp.makeConstraints { make in make.size.equalTo(size) }
            indicatorStack.addArrangedSubview(dot)
        }
    }
}

/// AVPlayerLayer 容器，提供点击显示且三秒自动隐藏的播放控制。
private final class SamplePlayerLayerView: UIView {
    private let controlButton = UIButton(type: .system)
    private weak var player: AVPlayer?
    private var hideControlWorkItem: DispatchWorkItem?

    override class var layerClass: AnyClass { AVPlayerLayer.self }
    var playerLayer: AVPlayerLayer { layer as! AVPlayerLayer }

    override init(frame: CGRect) {
        super.init(frame: frame)
        controlButton.tintColor = .white
        controlButton.backgroundColor = UIColor.black.withAlphaComponent(0.45)
        controlButton.layer.cornerRadius = 22
        controlButton.isHidden = true
        controlButton.accessibilityLabel = "播放或暂停视频"
        addSubview(controlButton)
        controlButton.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(44)
        }
        addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(showControls)))
        controlButton.addTarget(self, action: #selector(togglePlayback), for: .touchUpInside)
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    /// 绑定播放器。
    func configure(player: AVPlayer) {
        self.player = player
        playerLayer.player = player
        updateControlImage()
    }

    /// 同步轮播切页后的播放状态。
    func setPlaying(_ playing: Bool) {
        if !playing { controlButton.isHidden = true }
        updateControlImage()
    }

    @objc private func showControls() {
        controlButton.isHidden = false
        updateControlImage()
        hideControlWorkItem?.cancel()
        let workItem = DispatchWorkItem { [weak self] in self?.controlButton.isHidden = true }
        hideControlWorkItem = workItem
        DispatchQueue.main.asyncAfter(deadline: .now() + 3, execute: workItem)
    }

    @objc private func togglePlayback() {
        guard let player else { return }
        if player.rate == 0 {
            player.play()
        } else {
            player.pause()
        }
        showControls()
    }

    private func updateControlImage() {
        let name = player?.rate == 0 ? "play.fill" : "pause.fill"
        controlButton.setImage(UIImage(systemName: name), for: .normal)
    }
}

/// 样片详情名称与上架状态区。
private final class SampleDetailSampleInfoSection: UIView {
    private let titleLabel = UILabel()
    private let listingBadge = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .white
        titleLabel.font = .systemFont(ofSize: 20, weight: .bold)
        titleLabel.textColor = UIColor(hex: 0x2E3746)
        titleLabel.numberOfLines = 0
        listingBadge.font = .systemFont(ofSize: 12)
        listingBadge.layer.cornerRadius = 4
        listingBadge.clipsToBounds = true
        listingBadge.textAlignment = .center
        addSubview(titleLabel)
        addSubview(listingBadge)
        titleLabel.snp.makeConstraints { make in
            make.top.bottom.equalToSuperview().inset(16)
            make.leading.equalToSuperview().offset(16)
            make.trailing.lessThanOrEqualTo(listingBadge.snp.leading).offset(-12)
        }
        listingBadge.snp.makeConstraints { make in
            make.trailing.equalToSuperview().inset(16)
            make.centerY.equalTo(titleLabel)
            make.height.equalTo(24)
            make.width.greaterThanOrEqualTo(54)
        }
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func apply(detail: SampleDetail) {
        titleLabel.text = detail.name ?? ""
        let listed = detail.listingStatus == 1
        listingBadge.text = listed ? "已上架" : "未上架"
        let color = listed ? UIColor(hex: 0x22C55E) : AppColor.textSecondary
        listingBadge.textColor = color
        listingBadge.backgroundColor = color.withAlphaComponent(0.1)
    }
}

/// 样片上传人信息区。
private final class SampleDetailUploaderSection: UIView {
    private let titleLabel = UILabel()
    private let avatarView = UIImageView()
    private let nameLabel = UILabel()
    private let tagStack = UIStackView()
    private let descriptionLabel = UILabel()

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .white
        titleLabel.text = "上传人"
        titleLabel.font = .systemFont(ofSize: 14, weight: .bold)
        titleLabel.textColor = .black
        avatarView.contentMode = .scaleAspectFill
        avatarView.clipsToBounds = true
        avatarView.layer.cornerRadius = 24
        avatarView.backgroundColor = UIColor(hex: 0xF5F5F5)
        nameLabel.font = .systemFont(ofSize: 14, weight: .medium)
        nameLabel.textColor = UIColor(hex: 0x002255)
        descriptionLabel.font = .systemFont(ofSize: 14)
        descriptionLabel.textColor = UIColor(hex: 0x7B8EAA)
        descriptionLabel.numberOfLines = 0
        tagStack.axis = .horizontal
        tagStack.spacing = 8

        addSubview(titleLabel)
        addSubview(avatarView)
        addSubview(nameLabel)
        addSubview(tagStack)
        addSubview(descriptionLabel)

        titleLabel.snp.makeConstraints { make in
            make.top.leading.trailing.equalToSuperview().inset(16)
        }
        avatarView.snp.makeConstraints { make in
            make.top.equalTo(titleLabel.snp.bottom).offset(12)
            make.leading.equalToSuperview().offset(16)
            make.size.equalTo(48)
            make.bottom.lessThanOrEqualToSuperview().inset(16)
        }
        nameLabel.snp.makeConstraints { make in
            make.top.equalTo(avatarView)
            make.leading.equalTo(avatarView.snp.trailing).offset(12)
        }
        tagStack.snp.makeConstraints { make in
            make.leading.equalTo(nameLabel.snp.trailing).offset(8)
            make.centerY.equalTo(nameLabel)
            make.trailing.lessThanOrEqualToSuperview().inset(16)
        }
        descriptionLabel.snp.makeConstraints { make in
            make.top.equalTo(nameLabel.snp.bottom).offset(4)
            make.leading.equalTo(nameLabel)
            make.trailing.equalToSuperview().inset(16)
            make.bottom.equalToSuperview().inset(16)
        }
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func apply(detail: SampleDetail) {
        nameLabel.text = detail.uploaderName ?? ""
        descriptionLabel.text = detail.uploaderDescription ?? ""
        tagStack.arrangedSubviews.forEach { $0.removeFromSuperview() }
        if detail.platformCertification {
            tagStack.addArrangedSubview(makeTag("平台认证"))
        }
        if detail.scenicCertification {
            tagStack.addArrangedSubview(makeTag("景区认证"))
        }
        if let urlText = detail.uploaderAvatar, let url = URL(string: urlText), !urlText.isEmpty {
            avatarView.kf.setImage(with: url, placeholder: UIImage(systemName: "person.crop.circle.fill"))
        } else {
            avatarView.image = UIImage(systemName: "person.crop.circle.fill")
            avatarView.tintColor = AppColor.textTertiary
        }
    }

    private func makeTag(_ title: String) -> UILabel {
        let label = UILabel()
        label.text = title
        label.textColor = .white
        label.font = .systemFont(ofSize: 8)
        label.textAlignment = .center
        label.backgroundColor = AppColor.primary
        label.layer.cornerRadius = 4
        label.clipsToBounds = true
        label.snp.makeConstraints { make in
            make.height.equalTo(16)
            make.width.greaterThanOrEqualTo(44)
        }
        return label
    }
}

/// 样片详情图片信息区，复用打卡地点与绑定套餐布局。
private final class SampleDetailImageInfoSection: UIView {
    private let sectionTitleLabel = UILabel()
    private let imageView = UIImageView()
    private let titleLabel = UILabel()
    private let firstLineLabel = UILabel()
    private let secondLineLabel = UILabel()

    init(title: String) {
        super.init(frame: .zero)
        backgroundColor = .white
        sectionTitleLabel.text = title
        sectionTitleLabel.font = .systemFont(ofSize: 14, weight: .bold)
        sectionTitleLabel.textColor = .black
        imageView.backgroundColor = UIColor(hex: 0xF5F5F5)
        imageView.contentMode = .scaleAspectFill
        imageView.clipsToBounds = true
        imageView.layer.cornerRadius = 12
        titleLabel.font = .systemFont(ofSize: title == "打卡地点" ? 14 : 16, weight: .bold)
        titleLabel.textColor = .black
        [firstLineLabel, secondLineLabel].forEach { label in
            label.font = .systemFont(ofSize: 12)
            label.textColor = UIColor(hex: 0x4B5563)
            label.numberOfLines = 1
        }

        addSubview(sectionTitleLabel)
        addSubview(imageView)
        addSubview(titleLabel)
        addSubview(firstLineLabel)
        addSubview(secondLineLabel)
        sectionTitleLabel.snp.makeConstraints { make in
            make.top.leading.trailing.equalToSuperview().inset(16)
        }
        imageView.snp.makeConstraints { make in
            make.top.equalTo(sectionTitleLabel.snp.bottom).offset(12)
            make.leading.equalToSuperview().offset(16)
            make.height.equalTo(100)
            make.width.equalTo(179)
            make.bottom.equalToSuperview().inset(16)
        }
        titleLabel.snp.makeConstraints { make in
            make.top.equalTo(imageView).offset(2)
            make.leading.equalTo(imageView.snp.trailing).offset(12)
            make.trailing.equalToSuperview().inset(16)
        }
        firstLineLabel.snp.makeConstraints { make in
            make.top.equalTo(titleLabel.snp.bottom).offset(12)
            make.leading.trailing.equalTo(titleLabel)
        }
        secondLineLabel.snp.makeConstraints { make in
            make.top.equalTo(firstLineLabel.snp.bottom).offset(7)
            make.leading.trailing.equalTo(titleLabel)
        }
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func applyScenic(detail: SampleDetail) {
        titleLabel.text = detail.scenicName ?? ""
        firstLineLabel.attributedText = coloredValue(prefix: "无人机: ", value: "\(detail.scenicPlanesNum)架")
        secondLineLabel.attributedText = coloredValue(prefix: "摄影师: ", value: "\(detail.scenicPhotographerNum)人")
        let shouldShowCounts = detail.scenicPlanesNum > 0 || detail.scenicPhotographerNum > 0
        firstLineLabel.isHidden = !shouldShowCounts
        secondLineLabel.isHidden = !shouldShowCounts
        setImage(detail.scenicCoverImg)
    }

    func applyProject(detail: SampleDetail) {
        titleLabel.text = detail.projectName ?? ""
        firstLineLabel.isHidden = detail.projectPrice == nil
        secondLineLabel.isHidden = detail.projectPriceDeposit == nil
        if let price = detail.projectPrice {
            firstLineLabel.attributedText = coloredValue(prefix: "价格: ", value: "\(price)元")
        } else {
            firstLineLabel.text = ""
        }
        if let deposit = detail.projectPriceDeposit {
            secondLineLabel.attributedText = coloredValue(prefix: "定金: ", value: "\(deposit)元")
        } else {
            secondLineLabel.text = ""
        }
        setImage(detail.projectCover)
    }

    private func setImage(_ urlText: String?) {
        if let urlText, let url = URL(string: urlText), !urlText.isEmpty {
            imageView.kf.setImage(with: url, placeholder: UIImage(named: "sample_img_loading")) { [weak imageView] result in
                if case .failure = result {
                    imageView?.image = UIImage(named: "sample_img_error")
                }
            }
        } else {
            imageView.image = UIImage(named: "sample_img_error")
        }
    }

    private func coloredValue(prefix: String, value: String) -> NSAttributedString {
        let text = NSMutableAttributedString(
            string: prefix,
            attributes: [.foregroundColor: UIColor(hex: 0x4B5563), .font: UIFont.systemFont(ofSize: 12)]
        )
        text.append(
            NSAttributedString(
                string: value,
                attributes: [.foregroundColor: AppColor.primary, .font: UIFont.systemFont(ofSize: 12)]
            )
        )
        return text
    }
}

/// 样片审核信息区。
private final class SampleDetailReviewSection: UIView {
    private let stack = UIStackView()
    private let titleLabel = UILabel()
    private let reviewerRow = SampleDetailInfoRow()
    private let reviewTimeRow = SampleDetailInfoRow()
    private let statusRow = SampleDetailStatusRow()
    private let noteRow = SampleDetailInfoRow()

    override init(frame: CGRect) {
        super.init(frame: frame)
        backgroundColor = .white
        stack.axis = .vertical
        stack.spacing = 0
        titleLabel.text = "审核信息"
        titleLabel.font = .systemFont(ofSize: 14, weight: .bold)
        titleLabel.textColor = .black
        addSubview(stack)
        stack.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(16)
        }
        stack.addArrangedSubview(titleLabel)
        [reviewerRow, reviewTimeRow, statusRow, noteRow].forEach { row in
            stack.addArrangedSubview(row)
        }
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func apply(detail: SampleDetail) {
        reviewerRow.apply(label: "审核人", value: detail.reviewer?.nonEmpty ?? "--")
        reviewTimeRow.apply(label: "审核时间", value: detail.reviewTime?.nonEmpty ?? "--")
        statusRow.apply(status: detail.status)
        noteRow.apply(label: "审核备注", value: detail.reviewNotes?.nonEmpty ?? "--", valueColor: UIColor(hex: 0x4B5563), valueFont: .systemFont(ofSize: 12))
        noteRow.setSeparatorHidden(true)
    }
}

/// 样片详情普通信息行。
private final class SampleDetailInfoRow: UIView {
    private let label = UILabel()
    private let valueLabel = UILabel()
    private let separator = UIView()

    override init(frame: CGRect) {
        super.init(frame: frame)
        label.font = .systemFont(ofSize: 14)
        label.textColor = UIColor(hex: 0x2E3746)
        valueLabel.font = .systemFont(ofSize: 14, weight: .medium)
        valueLabel.textColor = .black
        valueLabel.textAlignment = .right
        valueLabel.numberOfLines = 1
        separator.backgroundColor = UIColor(hex: 0xEEEEEE)
        addSubview(label)
        addSubview(valueLabel)
        addSubview(separator)
        snp.makeConstraints { make in
            make.height.equalTo(44)
        }
        label.snp.makeConstraints { make in
            make.leading.centerY.equalToSuperview()
        }
        valueLabel.snp.makeConstraints { make in
            make.trailing.centerY.equalToSuperview()
            make.leading.greaterThanOrEqualTo(label.snp.trailing).offset(12)
        }
        separator.snp.makeConstraints { make in
            make.leading.trailing.bottom.equalToSuperview()
            make.height.equalTo(1)
        }
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func apply(label: String, value: String, valueColor: UIColor = .black, valueFont: UIFont = .systemFont(ofSize: 14, weight: .medium)) {
        self.label.text = label
        valueLabel.text = value
        valueLabel.textColor = valueColor
        valueLabel.font = valueFont
    }

    /// 控制行底部分隔线显示。
    func setSeparatorHidden(_ hidden: Bool) {
        separator.isHidden = hidden
    }
}

/// 样片详情审核状态行。
private final class SampleDetailStatusRow: UIView {
    private let label = UILabel()
    private let badge = SampleStatusBadgeView()
    private let separator = UIView()

    override init(frame: CGRect) {
        super.init(frame: frame)
        label.text = "审核状态"
        label.font = .systemFont(ofSize: 14)
        label.textColor = UIColor(hex: 0x2E3746)
        separator.backgroundColor = UIColor(hex: 0xEEEEEE)
        addSubview(label)
        addSubview(badge)
        addSubview(separator)
        snp.makeConstraints { make in
            make.height.equalTo(44)
        }
        label.snp.makeConstraints { make in
            make.leading.centerY.equalToSuperview()
        }
        badge.snp.makeConstraints { make in
            make.trailing.centerY.equalToSuperview()
            make.height.equalTo(24)
        }
        separator.snp.makeConstraints { make in
            make.leading.trailing.bottom.equalToSuperview()
            make.height.equalTo(1)
        }
    }

    @available(*, unavailable)
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    func apply(status: Int) {
        badge.apply(status: status)
    }
}

private extension String {
    var nonEmpty: String? { isEmpty ? nil : self }
}
