//
//  LNImagePreviewCell.swift
//  Lanu
//
//  Created by OneeChan on 2025/12/11.
//

import Foundation
import UIKit
import SnapKit


protocol LNImagePreviewCellDelegate: AnyObject {
    func onImagePreviewCellDragToDismiss(cell: LNImagePreviewCell)
}


class LNImagePreviewCell: UICollectionViewCell {
    private let scrollView = UIScrollView()
    private let imageView = UIImageView()
    private let indicator = UIActivityIndicatorView(style: .large)
    
    private var panGesture: UIPanGestureRecognizer?
    private var touchBeginPoint: CGPoint = .zero
    private var lastMove: CGPoint = .zero
    private let dragScaleMin = 0.4
    private let dragScaleOffsetY = UIScreen.main.bounds.height * 0.5
    private let dragAlphaOffsetY = 150.0
    
    private var curUrl: String?
    
    weak var delegate: LNImagePreviewCellDelegate?
    
    override init(frame: CGRect) {
        super.init(frame: frame)
        setupViews()
        setupGesture()
    }
    
    func update(url: String) {
        resetZoom()
        loadImage(from: url)
    }
    
    func resetZoom() {
        scrollView.setZoomScale(1.0, animated: false)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
}

extension LNImagePreviewCell {
    private func loadImage(from url: String) {
        curUrl = url
        
        let handler: (UIImage?) -> Void = { [weak self] image in
            guard let self else { return }
            guard curUrl == url else { return }
            indicator.stopAnimating()
            
            if let image, let viewController {
                if image.size.width / image.size.height > viewController.view.bounds.width / viewController.view.bounds.height {
                    imageView.snp.remakeConstraints { make in
                        make.edges.equalToSuperview()
                        make.width.equalToSuperview()
                        make.height.equalTo(self.imageView.snp.width).multipliedBy(image.size.height / image.size.width)
                    }
                } else {
                    imageView.snp.remakeConstraints { make in
                        make.edges.equalToSuperview()
                        make.height.equalToSuperview()
                        make.width.equalTo(self.imageView.snp.height).multipliedBy(image.size.width / image.size.height)
                    }
                }
            } else {
                imageView.snp.remakeConstraints { make in
                    make.edges.equalToSuperview()
                    make.width.height.equalTo(0)
                }
            }
            contentView.layoutIfNeeded()
            centerImage()
        }
        indicator.startAnimating()
        if url.hasPrefix("http") {
            imageView.sd_setImage(with: URL(string: url)) { image, _,_,_ in
                handler(image)
            }
        } else {
            imageView.sd_setImage(with: URL(fileURLWithPath: url)) { image,_,_,_ in
                handler(image)
            }
        }
    }
    
    private func centerImage() {
        let imageViewSize = imageView.frame.size
        let scrollViewSize = scrollView.bounds.size
        
        let verticalInset = (scrollViewSize.height - imageViewSize.height) / 2
        let horizontalInset = (scrollViewSize.width - imageViewSize.width) / 2
        
        scrollView.contentInset = UIEdgeInsets(
            top: max(verticalInset, 0),
            left: max(horizontalInset, 0),
            bottom: max(verticalInset, 0),
            right: max(horizontalInset, 0)
        )
    }
    
    private func zoomRect(for scale: CGFloat, center: CGPoint) -> CGRect {
        var zoomRect = CGRect.zero
        zoomRect.size.height = imageView.frame.size.height / scale
        zoomRect.size.width = imageView.frame.size.width / scale
        
        zoomRect.origin.x = center.x - (zoomRect.size.width / 2)
        zoomRect.origin.y = center.y - (zoomRect.size.height / 2)
        
        return zoomRect
    }
    
    @objc
    private func handleDoubleTap(_ gesture: UITapGestureRecognizer) {
        let currentScale = scrollView.zoomScale
        let targetScale = currentScale == 1.0 ? scrollView.maximumZoomScale : 1.0
        
        // 获取点击位置，缩放到点击点
        let point = gesture.location(in: imageView)
        let zoomRect = zoomRect(for: targetScale, center: point)
        
        scrollView.zoom(to: zoomRect, animated: true)
    }
    
    @objc
    private func handlePan(_ gesture: UIPanGestureRecognizer) {
        let position = gesture.translation(in: self)
        
        switch gesture.state {
        case .began:
            touchBeginPoint = position
            break
        case .changed:
            lastMove = gesture.velocity(in: self)
            var frame = contentView.frame
            frame.origin.y = position.y - touchBeginPoint.y
            frame.origin.x = position.x - touchBeginPoint.x
            contentView.frame = frame
            
            let progress = (frame.origin.y / dragAlphaOffsetY).bounded(min: 0, max: 1.0)
            superview?.backgroundColor = .black.withAlphaComponent(1 - progress)
            
            let scale = 1 - (frame.origin.y / dragScaleOffsetY).bounded(min: 0, max: 1.0) * (1 - dragScaleMin)
            imageView.transform = .init(scaleX: scale, y: scale)
            break
        default:
            if lastMove.y > 0 {
                var frame = contentView.frame
                frame.origin.y = bounds.height
                
                UIView.animate(withDuration: 0.25) { [weak self] in
                    guard let self else { return }
                    contentView.frame = frame
                    
                    superview?.backgroundColor = .clear
                } completion: { [weak self] _ in
                    guard let self else { return }
                    delegate?.onImagePreviewCellDragToDismiss(cell: self)
                }
            } else {
                UIView.animate(withDuration: 0.25) { [weak self] in
                    guard let self else { return }
                    contentView.frame = bounds
                    
                    superview?.backgroundColor = .black
                    imageView.transform = .identity
                }
            }
            break
        }
    }
}

extension LNImagePreviewCell: UIScrollViewDelegate {
    func viewForZooming(in scrollView: UIScrollView) -> UIView? {
        return imageView
    }
    
    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        centerImage()
        panGesture?.isEnabled = scrollView.zoomScale == 1.0
    }
}

extension LNImagePreviewCell: UIGestureRecognizerDelegate {
    override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
        guard let pan = gestureRecognizer as? UIPanGestureRecognizer else {
            return true
        }
        
        let velocity = pan.velocity(in: self)
        return abs(velocity.y) > abs(velocity.x)
    }
}

extension LNImagePreviewCell {
    private func setupViews() {
        scrollView.delegate = self
        scrollView.minimumZoomScale = 1.0
        scrollView.maximumZoomScale = UIScreen.main.bounds.height / UIScreen.main.bounds.width
        scrollView.showsVerticalScrollIndicator = false
        scrollView.showsHorizontalScrollIndicator = false
        contentView.addSubview(scrollView)
        scrollView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
        }
        
        imageView.contentMode = .scaleAspectFit
        imageView.backgroundColor = .clear
        scrollView.addSubview(imageView)
        imageView.snp.makeConstraints { make in
            make.edges.equalToSuperview()
            make.width.height.equalTo(0)
        }
        
        indicator.color = .white
        indicator.hidesWhenStopped = true
        contentView.addSubview(indicator)
        indicator.snp.makeConstraints { make in
            make.center.equalToSuperview()
        }
    }
    
    private func setupGesture() {
        let doubleTap = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap))
        doubleTap.numberOfTapsRequired = 2
        scrollView.addGestureRecognizer(doubleTap)
        
        let pan = UIPanGestureRecognizer(target: self, action: #selector(handlePan(_:)))
        pan.delegate = self
        pan.isEnabled = true
        pan.maximumNumberOfTouches = 1
        contentView.addGestureRecognizer(pan)
        panGesture = pan
        
        contentView.onTap { [weak self] in
            guard let self else { return }
            var frame = contentView.frame
            frame.origin.y = bounds.height
            
            UIView.animate(withDuration: 0.25) { [weak self] in
                guard let self else { return }
                contentView.frame = frame
                
                superview?.backgroundColor = .clear
            } completion: { [weak self] _ in
                guard let self else { return }
                delegate?.onImagePreviewCellDragToDismiss(cell: self)
            }
        }
    }
}
