//
//  WildReportRiskMapViewController.swift
//  suixinkan
//

import Kingfisher
import MapKit
import SnapKit
import UIKit

/// 附近风险地图页面，展示当前位置、疑似打野、处理中线索和内部摄影师标记。
final class WildReportRiskMapViewController: BaseViewController {
    private let viewModel: WildReportRiskMapViewModel
    private let api: any WildPhotographerReportServing
    private let locationProvider: any LocationProviding
    private let scenicIdProvider: () -> Int
    private let scenicNameProvider: () -> String

    private let rootStack = UIStackView()
    private let mapContainerView = UIView()
    private let mapView = MKMapView()
    private let scenicCapsule = UIView()
    private let scenicNameLabel = UILabel()
    private let legendBar = UIStackView()
    private let detailContainerView = UIView()
    private let scrollView = UIScrollView()
    private let contentStack = UIStackView()
    private var annotationMap: [String: WildReportMapMarker] = [:]
    private var isShowingRiskMapLoading = false

    init(
        api: any WildPhotographerReportServing = NetworkServices.shared.wildPhotographerReportAPI,
        locationProvider: any LocationProviding = LocationProvider.shared,
        scenicIdProvider: @escaping () -> Int = { AppStore.shared.currentScenicId },
        scenicNameProvider: @escaping () -> String = { AppStore.shared.currentScenicName }
    ) {
        self.viewModel = WildReportRiskMapViewModel()
        self.api = api
        self.locationProvider = locationProvider
        self.scenicIdProvider = scenicIdProvider
        self.scenicNameProvider = scenicNameProvider
        super.init(nibName: nil, bundle: nil)
    }

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

    override func viewDidLoad() {
        super.viewDidLoad()
        Task {
            await viewModel.loadInitial(
                api: api,
                locationProvider: locationProvider,
                scenicId: scenicIdProvider(),
                scenicName: scenicNameProvider()
            )
        }
    }

    override func viewDidDisappear(_ animated: Bool) {
        super.viewDidDisappear(animated)
        if isMovingFromParent || isBeingDismissed || navigationController?.isBeingDismissed == true {
            setRiskMapLoadingVisible(false)
        }
    }

    override func setupNavigationBar() {
        title = "附近风险地图"
    }

    override func setupUI() {
        view.backgroundColor = UIColor(hex: 0xF8FAFF)
        rootStack.axis = .vertical
        rootStack.spacing = 0
        view.addSubview(rootStack)

        mapContainerView.backgroundColor = UIColor(hex: 0xE8F3FF)
        mapContainerView.clipsToBounds = true
        rootStack.addArrangedSubview(mapContainerView)

        mapView.delegate = self
        mapView.showsCompass = false
        mapView.showsScale = true
        mapView.pointOfInterestFilter = .excludingAll
        mapContainerView.addSubview(mapView)

        configureScenicCapsule()
        configureLegendBar()
        configureDetailArea()
        refreshContent(animated: false)
    }

    override func setupConstraints() {
        rootStack.snp.makeConstraints { make in
            make.edges.equalTo(view.safeAreaLayoutGuide)
        }
        mapContainerView.snp.makeConstraints { make in
            make.height.equalTo(340)
        }
        mapView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        scenicCapsule.snp.makeConstraints { make in
            make.top.leading.equalToSuperview().inset(12)
            make.height.equalTo(34)
        }
        detailContainerView.snp.makeConstraints { make in
            make.height.greaterThanOrEqualTo(180)
        }
    }

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

    private func configureScenicCapsule() {
        scenicCapsule.backgroundColor = UIColor.white.withAlphaComponent(0.94)
        scenicCapsule.layer.cornerRadius = 17
        scenicCapsule.layer.shadowColor = UIColor.black.cgColor
        scenicCapsule.layer.shadowOpacity = 0.08
        scenicCapsule.layer.shadowRadius = 8
        scenicCapsule.layer.shadowOffset = CGSize(width: 0, height: 4)

        let icon = UIImageView(image: UIImage(systemName: "mountain.2.fill"))
        icon.tintColor = AppColor.primary
        icon.contentMode = .scaleAspectFit
        scenicNameLabel.font = .systemFont(ofSize: 13, weight: .semibold)
        scenicNameLabel.textColor = UIColor(hex: 0x195591)

        mapContainerView.addSubview(scenicCapsule)
        scenicCapsule.addSubview(icon)
        scenicCapsule.addSubview(scenicNameLabel)
        icon.snp.makeConstraints { make in
            make.leading.equalToSuperview().offset(12)
            make.centerY.equalToSuperview()
            make.size.equalTo(16)
        }
        scenicNameLabel.snp.makeConstraints { make in
            make.leading.equalTo(icon.snp.trailing).offset(6)
            make.trailing.equalToSuperview().inset(12)
            make.centerY.equalToSuperview()
        }
    }

    private func configureLegendBar() {
        legendBar.axis = .horizontal
        legendBar.alignment = .center
        legendBar.distribution = .fillEqually
        legendBar.spacing = 6
        legendBar.backgroundColor = .white
        legendBar.layoutMargins = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
        legendBar.isLayoutMarginsRelativeArrangement = true
        rootStack.addArrangedSubview(legendBar)
        rebuildLegend()

        let divider = UIView()
        divider.backgroundColor = AppColor.cardOutline
        rootStack.addArrangedSubview(divider)
        divider.snp.makeConstraints { make in
            make.height.equalTo(1 / UIScreen.main.scale)
        }
    }

    private func configureDetailArea() {
        detailContainerView.backgroundColor = UIColor(hex: 0xF8FAFF)
        rootStack.addArrangedSubview(detailContainerView)
        detailContainerView.addSubview(scrollView)
        scrollView.alwaysBounceVertical = true
        scrollView.addSubview(contentStack)
        contentStack.axis = .vertical
        contentStack.spacing = 14
        scrollView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        contentStack.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(UIEdgeInsets(top: 14, left: 18, bottom: 18, right: 18))
            make.width.equalTo(scrollView.snp.width).offset(-36)
        }
    }

    @MainActor
    private func refreshContent(animated: Bool) {
        setRiskMapLoadingVisible(viewModel.isLoading)
        scenicNameLabel.text = viewModel.scenicAreaName
        syncMapAnnotations()
        mapView.setRegion(viewModel.region, animated: animated)
        updateAnnotationSelection()
        rebuildLegend()
        rebuildPanel()
    }

    @MainActor
    private func setRiskMapLoadingVisible(_ isVisible: Bool) {
        guard isShowingRiskMapLoading != isVisible else { return }
        isShowingRiskMapLoading = isVisible
        isVisible ? showLoading() : hideLoading()
    }

    private func syncMapAnnotations() {
        let oldAnnotations = mapView.annotations.compactMap { $0 as? WildReportMapAnnotation }
        mapView.removeAnnotations(oldAnnotations)
        annotationMap.removeAll()
        let annotations = viewModel.markers.map { marker in
            annotationMap[marker.id] = marker
            return WildReportMapAnnotation(marker: marker)
        }
        mapView.addAnnotations(annotations)
    }

    private func updateAnnotationSelection() {
        for annotation in mapView.annotations {
            guard let annotation = annotation as? WildReportMapAnnotation,
                  let view = mapView.view(for: annotation) as? WildReportPulseMarkerAnnotationView
            else { continue }
            view.apply(marker: annotation.marker, selected: annotation.marker.id == viewModel.selectedMarkerID)
        }
    }

    private func rebuildLegend() {
        legendBar.arrangedSubviews.forEach { view in
            legendBar.removeArrangedSubview(view)
            view.removeFromSuperview()
        }
        [
            WildReportMapMarkerKind.selfLocation,
            .wildPhotographer,
            .processingClue,
            .peerPhotographer
        ].forEach { kind in
            legendBar.addArrangedSubview(WildReportRiskLegendView(kind: kind))
        }
    }

    @MainActor
    private func rebuildPanel() {
        contentStack.arrangedSubviews.forEach { view in
            contentStack.removeArrangedSubview(view)
            view.removeFromSuperview()
        }

        if let message = viewModel.errorMessage, !viewModel.isLoading {
            contentStack.addArrangedSubview(makeStateCard(icon: "exclamationmark.triangle.fill", text: message))
        }

        guard let marker = viewModel.selectedMarker,
              case .activeClue(let clue) = marker.detail
        else {
            contentStack.addArrangedSubview(makeStateCard(icon: "hand.tap.fill", text: "点击地图标记查看详情"))
            return
        }

        let card = WildReportCardView(spacing: 16, inset: 16)
        card.layer.cornerRadius = 16
        card.stack.addArrangedSubview(makeHeader(marker: marker, clue: clue))
        if viewModel.isLoadingDetail {
            card.stack.addArrangedSubview(makeInlineLoading(text: "正在加载标记详情..."))
        }
        if let nearest = clue.nearestAbnormalText {
            card.stack.addArrangedSubview(makeHighlight(text: nearest, color: AppColor.danger))
        }
        addInfoRows(for: clue, to: card.stack)
        addDescriptionBlocks(for: clue, to: card.stack)
        addMediaRow(for: clue, to: card.stack)
        addProcessingBlock(for: clue, to: card.stack)
        card.stack.addArrangedSubview(makeButtonRow(for: clue))
        contentStack.addArrangedSubview(card)
    }

    private func makeHeader(marker: WildReportMapMarker, clue: WildReportRadarActiveClue) -> UIView {
        let row = UIStackView()
        row.axis = .horizontal
        row.alignment = .center
        row.spacing = 12

        let iconWrap = UIView()
        iconWrap.backgroundColor = marker.kind.color.withAlphaComponent(0.14)
        iconWrap.layer.cornerRadius = 22
        let icon = UIImageView(image: UIImage(systemName: iconName(for: marker.kind)))
        icon.tintColor = marker.kind.color
        icon.contentMode = .scaleAspectFit
        iconWrap.addSubview(icon)
        icon.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(22)
        }
        iconWrap.snp.makeConstraints { make in
            make.size.equalTo(44)
        }

        let textStack = UIStackView()
        textStack.axis = .vertical
        textStack.spacing = 4
        let title = WildReportUI.label(clue.displayTitle, font: .systemFont(ofSize: 17, weight: .bold), color: AppColor.textPrimary, lines: 2)
        let metaText = [clue.distanceText ?? clue.distance, clue.updatedAt].compactMap { $0 }.joined(separator: " · ")
        let meta = WildReportUI.label(metaText.isEmpty ? marker.title : metaText, font: .systemFont(ofSize: 12), color: AppColor.textSecondary, lines: 1)
        textStack.addArrangedSubview(title)
        textStack.addArrangedSubview(meta)

        let badge = WildReportRiskStatusBadge(text: clue.statusText, color: marker.kind.color)
        row.addArrangedSubview(iconWrap)
        row.addArrangedSubview(textStack)
        row.addArrangedSubview(badge)
        textStack.setContentHuggingPriority(.defaultLow, for: .horizontal)
        badge.setContentHuggingPriority(.required, for: .horizontal)
        return row
    }

    private func addInfoRows(for clue: WildReportRadarActiveClue, to stack: UIStackView) {
        if let name = clue.name {
            stack.addArrangedSubview(WildReportInfoRowView(title: clue.type == .green ? "摄影师" : "点位", value: name))
        }
        if let store = clue.storeName {
            stack.addArrangedSubview(WildReportInfoRowView(title: "门店", value: store))
        }
        if let distance = clue.distanceText ?? clue.distance {
            stack.addArrangedSubview(WildReportInfoRowView(title: "距离", value: distance))
        }
        if let updatedAt = clue.updatedAt {
            stack.addArrangedSubview(WildReportInfoRowView(title: "时间", value: updatedAt))
        }
        if let reporter = clue.reporterName {
            stack.addArrangedSubview(WildReportInfoRowView(title: "举报人", value: reporter))
        }
        if let phone = clue.maskedReporterPhone ?? clue.reporterPhone {
            stack.addArrangedSubview(WildReportInfoRowView(title: "联系电话", value: phone))
        }
    }

    private func addDescriptionBlocks(for clue: WildReportRadarActiveClue, to stack: UIStackView) {
        if let summary = clue.summary {
            stack.addArrangedSubview(makeBodyLabel(summary))
        }
        if let detail = clue.detail, detail != clue.summary {
            stack.addArrangedSubview(makeBodyLabel(detail))
        }
        for content in clue.reportContents.prefix(3) {
            let time = content.time.isEmpty ? "" : "\(content.time)  "
            stack.addArrangedSubview(makeBodyLabel("\(time)\(content.content)"))
        }
    }

    private func addMediaRow(for clue: WildReportRadarActiveClue, to stack: UIStackView) {
        guard !clue.images.isEmpty else { return }
        let scroll = UIScrollView()
        scroll.showsHorizontalScrollIndicator = false
        let row = UIStackView()
        row.axis = .horizontal
        row.spacing = 10
        scroll.addSubview(row)
        row.snp.makeConstraints { make in
            make.edges.equalToSuperview()
            make.height.equalTo(72)
        }
        for url in clue.images.prefix(9) {
            let tile = WildReportRiskImageTile(url: url)
            tile.onTap = { [weak self] in self?.presentImagePreview(url: url) }
            row.addArrangedSubview(tile)
        }
        stack.addArrangedSubview(scroll)
        scroll.snp.makeConstraints { make in
            make.height.equalTo(72)
        }
    }

    private func addProcessingBlock(for clue: WildReportRadarActiveClue, to stack: UIStackView) {
        guard let record = clue.record else { return }
        let box = UIView()
        box.backgroundColor = UIColor(hex: 0xF8FAFF)
        box.layer.cornerRadius = 12
        box.layer.borderWidth = 1
        box.layer.borderColor = AppColor.cardOutline.cgColor
        let inner = UIStackView()
        inner.axis = .vertical
        inner.spacing = 8
        box.addSubview(inner)
        inner.snp.makeConstraints { make in
            make.edges.equalToSuperview().inset(12)
        }
        inner.addArrangedSubview(WildReportInfoRowView(title: "处理人", value: record.handlerName))
        inner.addArrangedSubview(makeBodyLabel(record.processResult))
        stack.addArrangedSubview(box)
    }

    private func makeButtonRow(for clue: WildReportRadarActiveClue) -> UIView {
        let row = UIStackView()
        row.axis = .horizontal
        row.spacing = 10
        row.distribution = .fillEqually

        let navButton = WildReportUI.iconButton(
            title: clue.type == .blue ? "刷新定位" : "导航到此",
            imageName: clue.type == .blue ? "location.fill" : "map.fill",
            style: .primary
        )
        navButton.addTarget(self, action: #selector(navigateTapped), for: .touchUpInside)
        let shareButton = WildReportUI.iconButton(title: "分享", imageName: "square.and.arrow.up", style: .secondary)
        shareButton.addTarget(self, action: #selector(shareTapped), for: .touchUpInside)
        [navButton, shareButton].forEach { button in
            button.snp.makeConstraints { make in make.height.equalTo(44) }
            row.addArrangedSubview(button)
        }
        return row
    }

    private func makeInlineLoading(text: String) -> UIView {
        let row = UIStackView()
        row.axis = .horizontal
        row.alignment = .center
        row.spacing = 8
        let indicator = UIActivityIndicatorView(style: .medium)
        indicator.startAnimating()
        row.addArrangedSubview(indicator)
        row.addArrangedSubview(WildReportUI.label(text, font: .systemFont(ofSize: 13), color: AppColor.textSecondary))
        return row
    }

    private func makeStateCard(icon: String, text: String) -> UIView {
        let card = WildReportCardView(spacing: 12, inset: 18)
        card.stack.alignment = .center
        let image = UIImageView(image: UIImage(systemName: icon))
        image.tintColor = AppColor.primary
        image.contentMode = .scaleAspectFit
        image.snp.makeConstraints { make in
            make.size.equalTo(34)
        }
        let label = WildReportUI.label(text, font: .systemFont(ofSize: 15, weight: .semibold), color: AppColor.textSecondary, lines: 0)
        label.textAlignment = .center
        card.stack.addArrangedSubview(image)
        card.stack.addArrangedSubview(label)
        return card
    }

    private func makeHighlight(text: String, color: UIColor) -> UIView {
        let label = WildReportUI.label(text, font: .systemFont(ofSize: 14, weight: .semibold), color: color, lines: 0)
        label.backgroundColor = color.withAlphaComponent(0.08)
        label.layer.cornerRadius = 10
        label.clipsToBounds = true
        label.textAlignment = .center
        label.snp.makeConstraints { make in
            make.height.greaterThanOrEqualTo(34)
        }
        return label
    }

    private func makeBodyLabel(_ text: String) -> UILabel {
        WildReportUI.label(text, font: .systemFont(ofSize: 14), color: AppColor.textSecondary, lines: 0)
    }

    private func iconName(for kind: WildReportMapMarkerKind) -> String {
        switch kind {
        case .selfLocation: return "location.fill"
        case .wildPhotographer: return "exclamationmark"
        case .processingClue: return "clock.fill"
        case .peerPhotographer: return "camera.fill"
        }
    }

    private func presentImagePreview(url: String) {
        presentImagePreview(imageURLs: [url], startIndex: 0)
    }

    @objc private func navigateTapped() {
        guard let marker = viewModel.selectedMarker,
              case .activeClue(let clue) = marker.detail
        else { return }
        if clue.type == .blue {
            Task {
                await viewModel.refreshLocation(
                    api: api,
                    locationProvider: locationProvider,
                    scenicId: scenicIdProvider(),
                    scenicName: scenicNameProvider()
                )
            }
            return
        }
        guard let target = viewModel.navigateToSelectedMarker() else { return }
        let item = MKMapItem(placemark: MKPlacemark(coordinate: target.coordinate))
        item.name = target.title
        item.openInMaps(launchOptions: [
            MKLaunchOptionsDirectionsModeKey: MKLaunchOptionsDirectionsModeDriving
        ])
    }

    @objc private func shareTapped() {
        viewModel.shareSelectedClue()
    }
}

extension WildReportRiskMapViewController: MKMapViewDelegate {
    func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
        guard let annotation = annotation as? WildReportMapAnnotation else { return nil }
        let identifier = "WildReportPulseMarkerAnnotationView"
        let view = (mapView.dequeueReusableAnnotationView(withIdentifier: identifier) as? WildReportPulseMarkerAnnotationView)
            ?? WildReportPulseMarkerAnnotationView(annotation: annotation, reuseIdentifier: identifier)
        view.annotation = annotation
        view.apply(marker: annotation.marker, selected: annotation.marker.id == viewModel.selectedMarkerID)
        return view
    }

    func mapView(_ mapView: MKMapView, didSelect annotation: MKAnnotation) {
        guard let annotation = annotation as? WildReportMapAnnotation else { return }
        Task {
            await viewModel.selectMarker(
                annotation.marker,
                api: api,
                scenicId: scenicIdProvider()
            )
        }
    }
}

/// 风险地图 MKAnnotation 包装。
final class WildReportMapAnnotation: NSObject, MKAnnotation {
    let marker: WildReportMapMarker
    var coordinate: CLLocationCoordinate2D { marker.coordinate }
    var title: String? { marker.title }

    init(marker: WildReportMapMarker) {
        self.marker = marker
        super.init()
    }
}

/// 风险地图自定义脉冲圆点标记。
private final class WildReportPulseMarkerAnnotationView: MKAnnotationView {
    private let pulseOuter = UIView()
    private let pulseInner = UIView()
    private let selectedRing = UIView()
    private let dot = UIView()
    private let label = UILabel()

    override init(annotation: MKAnnotation?, reuseIdentifier: String?) {
        super.init(annotation: annotation, reuseIdentifier: reuseIdentifier)
        frame = CGRect(x: 0, y: 0, width: 56, height: 56)
        centerOffset = CGPoint(x: 0, y: -12)
        canShowCallout = false
        setupUI()
    }

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

    func apply(marker: WildReportMapMarker, selected: Bool) {
        let color = marker.kind.color
        pulseOuter.layer.borderColor = color.withAlphaComponent(0.18).cgColor
        pulseInner.layer.borderColor = color.withAlphaComponent(0.28).cgColor
        selectedRing.layer.borderColor = color.cgColor
        selectedRing.isHidden = !selected
        dot.backgroundColor = color
        label.isHidden = marker.kind != .selfLocation
        label.text = "我"
    }

    private func setupUI() {
        backgroundColor = .clear
        [pulseOuter, pulseInner, selectedRing, dot, label].forEach(addSubview)
        [pulseOuter, pulseInner, selectedRing].forEach { view in
            view.backgroundColor = .clear
            view.layer.borderWidth = 2
            view.isUserInteractionEnabled = false
        }
        pulseOuter.layer.cornerRadius = 20
        pulseInner.layer.cornerRadius = 14
        selectedRing.layer.cornerRadius = 14
        dot.layer.cornerRadius = 7
        dot.layer.borderWidth = 2
        dot.layer.borderColor = UIColor.white.cgColor

        label.font = .systemFont(ofSize: 10, weight: .bold)
        label.textColor = .white
        label.textAlignment = .center

        pulseOuter.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(40)
        }
        pulseInner.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(28)
        }
        selectedRing.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(28)
        }
        dot.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.size.equalTo(14)
        }
        label.snp.makeConstraints { make in
            make.center.equalTo(dot)
            make.size.equalTo(14)
        }
        startPulse()
    }

    private func startPulse() {
        let animation = CABasicAnimation(keyPath: "opacity")
        animation.fromValue = 0.85
        animation.toValue = 0.2
        animation.duration = 1.2
        animation.autoreverses = true
        animation.repeatCount = .infinity
        pulseOuter.layer.add(animation, forKey: "opacityPulse")
    }
}

/// 风险地图图例项视图。
private final class WildReportRiskLegendView: UIView {
    init(kind: WildReportMapMarkerKind) {
        super.init(frame: .zero)
        let row = UIStackView()
        row.axis = .horizontal
        row.alignment = .center
        row.spacing = 5
        let dot = UIView()
        dot.backgroundColor = kind.color
        dot.layer.cornerRadius = 4
        dot.snp.makeConstraints { make in
            make.size.equalTo(8)
        }
        let label = WildReportUI.label(kind.title, font: .systemFont(ofSize: 11, weight: .medium), color: AppColor.textSecondary, lines: 1)
        label.adjustsFontSizeToFitWidth = true
        label.minimumScaleFactor = 0.82
        row.addArrangedSubview(dot)
        row.addArrangedSubview(label)
        addSubview(row)
        row.snp.makeConstraints { make in
            make.center.equalToSuperview()
            make.leading.greaterThanOrEqualToSuperview()
            make.trailing.lessThanOrEqualToSuperview()
        }
    }

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

/// 风险地图状态胶囊。
private final class WildReportRiskStatusBadge: UILabel {
    init(text: String, color: UIColor) {
        super.init(frame: .zero)
        self.text = text
        textColor = color
        font = .systemFont(ofSize: 12, weight: .semibold)
        textAlignment = .center
        backgroundColor = color.withAlphaComponent(0.12)
        layer.cornerRadius = 12
        clipsToBounds = true
        numberOfLines = 1
        adjustsFontSizeToFitWidth = true
        minimumScaleFactor = 0.8
        snp.makeConstraints { make in
            make.height.equalTo(24)
            make.width.greaterThanOrEqualTo(64)
            make.width.lessThanOrEqualTo(96)
        }
    }

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

/// 风险地图举报图片缩略图。
private final class WildReportRiskImageTile: UIView {
    private let imageView = UIImageView()
    var onTap: (() -> Void)?

    init(url: String) {
        super.init(frame: .zero)
        backgroundColor = AppColor.pageBackgroundSoft
        layer.cornerRadius = 10
        layer.masksToBounds = true
        imageView.contentMode = .scaleAspectFill
        addSubview(imageView)
        imageView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        if let imageURL = URL(string: url) {
            imageView.kf.setImage(with: imageURL, placeholder: UIImage(systemName: "photo.fill"))
        } else {
            imageView.image = UIImage(systemName: "photo.fill")
            imageView.tintColor = AppColor.textTertiary
        }
        addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(tapped)))
        snp.makeConstraints { make in
            make.size.equalTo(72)
        }
    }

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

    @objc private func tapped() {
        onTap?()
    }
}
