//
//  BrowseImageZoomView.swift

//
//  Created by Leon on 2025/7/26.
//

import SnapKit
import UIKit

protocol BrowseImageZoomViewDelegate: AnyObject {
    func dismisAnimation(_ zoomImageView: UIImageView, toFrame: CGRect)
    func singleTapZoomView(_ zoomImageView: UIImageView) -> Bool
    func longPressZoomView(_ zoomView: BrowseImageZoomView) -> Bool
    func setVCViewBackgroundColor(_ alpha: CGFloat)
}

class BrowseImageZoomView: UIView, UIScrollViewDelegate, UIGestureRecognizerDelegate {
    private let kMaxScale: CGFloat = 3.0
    private let kMaxDropHeight: CGFloat = 60

    private var imageUrl: String?
    private var progress: PhotoBrowseProgressView?
    private var noticeButton: UIButton?
    private var stateLabel: UILabel?
    private var origFrame: CGRect = .zero

    var scrollView: UIScrollView!
    var imageView: UIImageView!
    weak var delegate: BrowseImageZoomViewDelegate?
    var displayIndex: Int = 0
    var imageModel: PhotoBrowserModel?

    init(imageModel: PhotoBrowserModel) {
        super.init(frame: .zero)
        self.imageModel = imageModel
        setupViews()
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

    private func setupViews() {
        backgroundColor = .clear

        scrollView = UIScrollView()
        scrollView.backgroundColor = UIColor.black.withAlphaComponent(1.0) // Assuming kBackgroundColor(1) is black
        scrollView.clipsToBounds = true
        scrollView.showsVerticalScrollIndicator = false
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.delegate = self
        if #available(iOS 11.0, *) {
            scrollView.contentInsetAdjustmentBehavior = .never
        }
        addSubview(scrollView)

        // Using SnapKit for scrollView layout
        scrollView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }

        imageView = UIImageView(frame: CGRect(
            x: 0,
            y: (UIScreen.main.bounds.height - UIScreen.main.bounds.width) / 2,
            width: UIScreen.main.bounds.width,
            height: UIScreen.main.bounds.width
        ))
        imageView.backgroundColor = .clear
        imageView.image = imageModel?.placeHolder
        scrollView.addSubview(imageView)

        addGestures()
        reloadImage(imageModel)
    }

    private func addGestures() {
        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(singleGestureAction(_:)))
        addGestureRecognizer(tapGesture)

        let doubleGesture = UITapGestureRecognizer(target: self, action: #selector(doubleGestureAction(_:)))
        doubleGesture.numberOfTapsRequired = 2
        addGestureRecognizer(doubleGesture)
        tapGesture.require(toFail: doubleGesture)

        let longPressGesture = UILongPressGestureRecognizer(target: self, action: #selector(longPressGestureAction(_:)))
        addGestureRecognizer(longPressGesture)

        let panGesture = UIPanGestureRecognizer(target: self, action: #selector(panGestureAction(_:)))
        panGesture.delegate = self
        addGestureRecognizer(panGesture)
    }

    private func resetErrorNoticeView() {
        stateLabel?.removeFromSuperview()
        stateLabel = nil
        noticeButton?.removeFromSuperview()
        noticeButton = nil
    }

    func reloadImage(_ imageModel: PhotoBrowserModel?) {
        guard let imageModel = imageModel else { return }
        self.imageModel = imageModel

        if let image = imageModel.image {
            imageView.image = image
            updateImageViewFrame()
        }
        if let url = imageModel.imageUrl {
            loadImageWithImageUrl(url, animated: imageModel.sourceRect.size.width > 0)
        }
    }

    private func loadImageWithImageUrl(_ imageUrl: String, animated:Bool = true) {
        self.imageUrl = imageUrl
//        guard let url = URL(string: imageUrl) else { return }
//        if let cacheImage = SDImageCache.shared.imageFromDiskCache(forKey: url.absoluteString) {
//            imageView.image = cacheImage
//            resetErrorNoticeView()
//            setNeedsLayout()
//            imageView.setNeedsDisplay()
//            updateImageViewFrame()
//            return
//        }

        progress = PhotoBrowseProgressView()
        progress?.isHidden = false
        addSubview(progress!)
        progress?.snp.makeConstraints { make in
            make.size.equalTo(CGSize(width: 50, height: 50))
            make.center.equalToSuperview()
        }

        imageView.loadImage(imageUrl) { receivedSize, totalSize in
            guard self.imageUrl == imageUrl, totalSize > 0 else { return } // let self = self,
            DispatchQueue.main.async {
                self.progress?.progress = CGFloat(receivedSize) / CGFloat(totalSize)
            }
        } completionBlock: { result in
            guard self.imageUrl == imageUrl else { return } // let self = self,
            
            DispatchQueue.main.async {
                self.progress?.removeFromSuperview()
                self.progress = nil

                switch result {
                case let .success(imageResult):
                    let duration = animated ? 0.25 : 0
                    UIView.animate(withDuration: duration) {
                        self.imageView.image = imageResult.image
                        self.resetErrorNoticeView()
                        self.setNeedsLayout()
                        self.imageView.setNeedsDisplay()
                        self.updateImageViewFrame()
                    }
                case let .failure(error):
                    dlog("图片加载失败: \(error.localizedDescription)")
                    let button = UIButton()
                    button.isUserInteractionEnabled = false
                    button.setImage(UIImage(named: "icon_notice_white"), for: .normal)
                    self.addSubview(button)
                    self.noticeButton = button
                    button.snp.makeConstraints { make in
                        make.size.equalTo(CGSize(width: 16, height: 16))
                        make.centerX.equalToSuperview()
                        make.centerY.equalToSuperview().offset(-36)
                    }

                    let errorLabel = UILabel()
                    errorLabel.backgroundColor = .clear
                    errorLabel.textColor = .white
                    errorLabel.textAlignment = .center
                    errorLabel.clipsToBounds = true
                    errorLabel.text = NSLocalizedString("image_load_failed", comment: "")
                    errorLabel.font = .systemFont(ofSize: 14)
                    errorLabel.numberOfLines = 2
                    errorLabel.sizeToFit()
                    self.addSubview(errorLabel)
                    errorLabel.snp.makeConstraints { make in
                        make.size.equalTo(CGSize(width: UIScreen.main.bounds.width - 80, height: 30))
                        make.center.equalToSuperview()
                    }
                    self.stateLabel = errorLabel
                }
            }
        }
        /*
         imageView.sd_setImage(with: url, placeholderImage: imageModel?.placeHolder, options: [.retryFailed, .lowPriority, .handleCookies]) { [weak self] receivedSize, expectedSize, _ in
             guard let self = self, self.imageUrl == imageUrl, expectedSize > 0 else { return }
             DispatchQueue.main.async {
                 self.progress?.progress = CGFloat(receivedSize) / CGFloat(expectedSize)
             }
         } completed: { [weak self] image, error, _, _ in
             guard let self = self, self.imageUrl == imageUrl else { return }
             self.progress?.removeFromSuperview()
             self.progress = nil

             if let error = error {
                 let button = UIButton()
                 button.isUserInteractionEnabled = false
                 button.setImage(UIImage(named: "icon_notice_white"), for: .normal)
                 self.addSubview(button)
                 self.noticeButton = button
                 button.snp.makeConstraints { make in
                     make.size.equalTo(CGSize(width: 16, height: 16))
                     make.centerX.equalToSuperview()
                     make.centerY.equalToSuperview().offset(-36)
                 }

                 let errorLabel = UILabel()
                 errorLabel.backgroundColor = .clear
                 errorLabel.textColor = .white
                 errorLabel.textAlignment = .center
                 errorLabel.clipsToBounds = true
                 errorLabel.text = NSLocalizedString("image_load_failed", comment: "")
                 errorLabel.font = .systemFont(ofSize: 14)
                 errorLabel.numberOfLines = 2
                 errorLabel.sizeToFit()
                 errorLabel.snp.makeConstraints { make in
                     make.size.equalTo(CGSize(width: UIScreen.main.bounds.width - 80, height: 30))
                     make.center.equalToSuperview()
                 }
                 self.addSubview(errorLabel)
                 self.stateLabel = errorLabel
             } else {
                 UIView.animate(withDuration: 0.25) {
                     self.imageView.image = image
                     self.resetErrorNoticeView()
                     self.setNeedsLayout()
                     self.imageView.setNeedsDisplay()
                     self.updateImageViewFrame()
                 }
             }
         }*/
    }

    private func updateImageViewFrame() {
        guard let image = imageView.image, image.size.height > 0, image.size.width > 0 else { return }

        let screenHeight = UIScreen.main.bounds.height
        let imageRatio = image.size.width / image.size.height
        let newWidth = frame.width
        let newHeight = frame.width / imageRatio
        let newX: CGFloat = 0
        let newY = newHeight > screenHeight ? 0 : (screenHeight - newHeight) / 2

        let newRect = CGRect(x: newX, y: newY, width: newWidth, height: newHeight)
        imageView.frame = newRect
        origFrame = newRect

        scrollView.contentSize = CGSize(width: newWidth, height: max(newHeight, screenHeight))
        scrollView.minimumZoomScale = 1.0
        scrollView.maximumZoomScale = max(screenHeight / newHeight, kMaxScale)
        scrollView.zoomScale = 1.0
    }

    @objc private func panGestureAction(_ recognizer: UIPanGestureRecognizer) {
        switch recognizer.state {
        case .ended:
            scrollView.isScrollEnabled = true
            if imageView.frame.origin.y - origFrame.origin.y > kMaxDropHeight {
                dismisAnimationWithGestureView(recognizer.view)
            } else {
                resetFrame()
            }
        case .cancelled:
            resetFrame()
        case .changed:
            let point = recognizer.translation(in: recognizer.view)
            updatePanFrame(point)
        default:
            break
        }
    }

    @objc private func singleGestureAction(_ recognizer: UITapGestureRecognizer) {
        if let stateLabel = stateLabel, !stateLabel.isHidden {
            retryAction()
            return
        }
        if delegate?.singleTapZoomView(imageView) ?? false {
            dismisAnimationWithGestureView(recognizer.view)
        }
    }

    @objc private func doubleGestureAction(_ recognizer: UITapGestureRecognizer) {
        if scrollView.zoomScale > scrollView.minimumZoomScale + 0.5 {
            scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
        } else {
            let tapPoint = recognizer.location(in: recognizer.view)
            scrollView.zoom(to: zoomRectForScale(2.0, withTapPoint: tapPoint), animated: true)
        }
    }

    @objc private func longPressGestureAction(_ recognizer: UILongPressGestureRecognizer) {
        if recognizer.state == .began {
            delegate?.longPressZoomView(self)
        }
    }

    private func retryAction() {
        resetErrorNoticeView()
        
        if let imageUrl = imageUrl, let imageModel = imageModel {
            loadImageWithImageUrl(imageUrl, animated: imageModel.sourceRect.size.width > 0)
        }
    }

    private func updatePanFrame(_ point: CGPoint) {
        var scale = 1.0
        if point.y >= 0 && point.y < 360 {
            scale = 1 - point.y / 410
            scrollView.backgroundColor = UIColor.black.withAlphaComponent(1 - point.y / 250.0)
            delegate?.setVCViewBackgroundColor(1 - point.y / 200.0)
        } else if point.y > 360 {
            scale = 1 - 360 / 410.0
        }

        let radiusWidth = imageView.frame.width - (imageModel?.sourceRect.width ?? 0)
        let newWidth = radiusWidth > 10 ? origFrame.width * scale : imageView.frame.width
        let newHeight = radiusWidth > 10 ? origFrame.height * scale : imageView.frame.height
        let offsetX = origFrame.width - newWidth

        imageView.frame = CGRect(
            x: origFrame.origin.x + point.x + offsetX / 2.0,
            y: origFrame.origin.y + point.y,
            width: newWidth,
            height: newHeight
        )
    }

    private func resetFrame() {
        isUserInteractionEnabled = false
        UIView.animate(withDuration: 0.2) { [weak self] in
            guard let self = self else { return }
            self.imageView.frame = self.origFrame
            self.scrollView.backgroundColor = UIColor.black.withAlphaComponent(1.0)
            self.delegate?.setVCViewBackgroundColor(1.0)
        } completion: { [weak self] _ in
            self?.isUserInteractionEnabled = true
        }
    }

    private func dismisAnimationWithGestureView(_ view: UIView?) {
        delegate?.dismisAnimation(imageView, toFrame: imageModel?.sourceRect ?? .zero)
        isHidden = true
    }

    private func zoomRectForScale(_ scale: CGFloat, withTapPoint point: CGPoint) -> CGRect {
        let touchX = point.x / scrollView.zoomScale
        let touchY = point.y / scrollView.zoomScale
        let x = touchX + scrollView.contentOffset.x - frame.width / (2 * scale)
        let y = touchY + scrollView.contentOffset.y - frame.height / (2 * scale)
        return CGRect(x: x, y: y, width: frame.width / scale, height: frame.height / scale)
    }

    // MARK: - UIScrollViewDelegate

    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        imageView.center = centerOfScrollViewContent(scrollView)
    }

    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }

    func scrollViewWillBeginZooming(_ scrollView: UIScrollView, with view: UIView?) {
        isUserInteractionEnabled = false
    }

    func scrollViewDidEndZooming(_ scrollView: UIScrollView, with view: UIView?, atScale scale: CGFloat) {
        isUserInteractionEnabled = true
    }

    private func centerOfScrollViewContent(_ scrollView: UIScrollView) -> CGPoint {
        let offsetX = scrollView.bounds.width > scrollView.contentSize.width ? (scrollView.bounds.width - scrollView.contentSize.width) * 0.5 : 0
        let offsetY = scrollView.bounds.height > scrollView.contentSize.height ? (scrollView.bounds.height - scrollView.contentSize.height) * 0.5 : 0
        return CGPoint(x: scrollView.contentSize.width * 0.5 + offsetX, y: scrollView.contentSize.height * 0.5 + offsetY)
    }

    func prepareForReuse() {
        displayIndex = 0
        resetErrorNoticeView()
        progress?.removeFromSuperview()
        progress = nil
        imageView.alpha = 1.0
        imageView.image = nil
        imageModel = nil
        delegate = nil
        updateImageViewFrame()
    }

    // MARK: - UIGestureRecognizerDelegate

    func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
        return scrollView.isScrollEnabled
    }

    override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        if let pan = gestureRecognizer as? UIPanGestureRecognizer {
            let velocity = pan.velocity(in: pan.view)
            if abs(velocity.x) < abs(velocity.y) && velocity.y > 0 && scrollView.contentOffset.y < 10 {
                scrollView.isScrollEnabled = false
                return true
            }
        }
        return false
    }
}
