import UIKit

// MARK: - GalleryZoomPage

/// Single zoomable image page using native UIScrollView zoom delegate.
/// Hardware-accelerated pinch zoom with zero gesture conflicts.
/// Centering: frame-based positioning (Telegram ZoomableContentGalleryItemNode parity).
final class GalleryZoomPage: UIView, UIScrollViewDelegate {

    enum LoadState {
        case idle
        case loading
        case loaded
        case failed
    }

    var image: UIImage? {
        didSet { configureImage() }
    }

    var onSingleTap: (() -> Void)?
    var onEdgeTap: ((Int) -> Void)?
    var onZoomChanged: ((Bool) -> Void)?

    /// True when zoom scale > minimum (used by dismiss controller to disable pan).
    var isZoomed: Bool { scrollView.zoomScale > scrollView.minimumZoomScale + 0.01 }

    private(set) var loadState: LoadState = .idle

    private let scrollView = UIScrollView()
    private let imageView = UIImageView()
    private var ignoreZoom = false

    private let loadingIndicator: UIActivityIndicatorView = {
        let indicator = UIActivityIndicatorView(style: .large)
        indicator.color = UIColor { $0.userInterfaceStyle == .dark ? .white : .gray }
        indicator.hidesWhenStopped = true
        return indicator
    }()

    private let placeholderView: UIImageView = {
        let config = UIImage.SymbolConfiguration(pointSize: 40, weight: .thin)
        let img = UIImage(systemName: "photo.fill", withConfiguration: config)
        let iv = UIImageView(image: img)
        iv.tintColor = UIColor { traits in
            traits.userInterfaceStyle == .dark
                ? UIColor.white.withAlphaComponent(0.3)
                : UIColor.black.withAlphaComponent(0.2)
        }
        iv.contentMode = .center
        iv.isHidden = true
        return iv
    }()

    // MARK: - Init

    override init(frame: CGRect) {
        super.init(frame: frame)
        setupScrollView()
        setupGestures()
        setupLoadingViews()
    }

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

    // MARK: - Setup

    private func setupScrollView() {
        scrollView.delegate = self
        scrollView.showsVerticalScrollIndicator = false
        scrollView.showsHorizontalScrollIndicator = false
        scrollView.contentInsetAdjustmentBehavior = .never
        scrollView.decelerationRate = .fast
        scrollView.bouncesZoom = true
        addSubview(scrollView)

        imageView.contentMode = .scaleToFill
        imageView.clipsToBounds = true
        scrollView.addSubview(imageView)
    }

    private func setupLoadingViews() {
        addSubview(loadingIndicator)
        addSubview(placeholderView)
    }

    private func setupGestures() {
        let doubleTap = UITapGestureRecognizer(target: self, action: #selector(handleDoubleTap(_:)))
        doubleTap.numberOfTapsRequired = 2
        scrollView.addGestureRecognizer(doubleTap)

        let singleTap = UITapGestureRecognizer(target: self, action: #selector(handleSingleTap(_:)))
        singleTap.numberOfTapsRequired = 1
        singleTap.require(toFail: doubleTap)
        scrollView.addGestureRecognizer(singleTap)
    }

    // MARK: - Layout

    override func layoutSubviews() {
        super.layoutSubviews()
        let newFrame = bounds
        loadingIndicator.center = CGPoint(x: bounds.midX, y: bounds.midY)
        placeholderView.center = CGPoint(x: bounds.midX, y: bounds.midY)
        guard scrollView.frame.size != newFrame.size else { return }
        scrollView.frame = newFrame
        if image != nil {
            updateZoomScale()
        }
    }

    // MARK: - Image Configuration

    func setLoadState(_ state: LoadState) {
        loadState = state
        switch state {
        case .loading:
            loadingIndicator.startAnimating()
            placeholderView.isHidden = true
        case .failed:
            loadingIndicator.stopAnimating()
            placeholderView.isHidden = false
        case .loaded, .idle:
            loadingIndicator.stopAnimating()
            placeholderView.isHidden = true
        }
    }

    private func configureImage() {
        // Full state reset — prevents zoom accumulation from recycled pages
        ignoreZoom = true
        scrollView.minimumZoomScale = 1.0
        scrollView.maximumZoomScale = 1.0
        scrollView.zoomScale = 1.0
        scrollView.contentOffset = .zero
        scrollView.contentInset = .zero
        scrollView.isScrollEnabled = false

        guard let image else {
            imageView.image = nil
            imageView.frame = .zero
            scrollView.contentSize = .zero
            ignoreZoom = false
            return
        }

        setLoadState(.loaded)
        imageView.image = image
        imageView.frame = CGRect(origin: .zero, size: image.size)
        scrollView.contentSize = image.size
        ignoreZoom = false
        updateZoomScale()
    }

    private func updateZoomScale() {
        guard let image, bounds.width > 0, bounds.height > 0 else { return }
        let scaleW = bounds.width / image.size.width
        let scaleH = bounds.height / image.size.height
        let minScale = min(scaleW, scaleH)
        let maxScale = max(minScale * 3.0, 1.0)

        // Collapse check (Telegram parity)
        let effectiveMax = abs(maxScale - minScale) < 0.01 ? minScale : maxScale

        ignoreZoom = true
        scrollView.minimumZoomScale = minScale
        scrollView.maximumZoomScale = effectiveMax
        scrollView.zoomScale = minScale
        scrollView.contentInset = .zero
        scrollView.isScrollEnabled = false
        ignoreZoom = false
        centerContent()
    }

    /// Light reset: returns to min zoom without clearing the image.
    /// Used by `scrollViewDidEndDecelerating` on adjacent pages.
    func resetZoomKeepImage() {
        guard isZoomed else { return }
        ignoreZoom = true
        scrollView.setZoomScale(scrollView.minimumZoomScale, animated: false)
        scrollView.contentOffset = .zero
        scrollView.isScrollEnabled = false
        ignoreZoom = false
        centerContent()
    }

    /// Full state reset (called when page is recycled in pager).
    func resetZoom() {
        ignoreZoom = true
        scrollView.minimumZoomScale = 1.0
        scrollView.maximumZoomScale = 1.0
        scrollView.zoomScale = 1.0
        scrollView.contentOffset = .zero
        scrollView.contentInset = .zero
        scrollView.contentSize = .zero
        scrollView.isScrollEnabled = false
        imageView.image = nil
        imageView.frame = .zero
        ignoreZoom = false
        setLoadState(.idle)
    }

    // MARK: - Content Centering (Telegram frame-based, NOT contentInset)

    private func centerContent() {
        let boundsSize = scrollView.bounds.size
        var frame = imageView.frame

        if boundsSize.width > frame.size.width {
            frame.origin.x = (boundsSize.width - frame.size.width) / 2.0
        } else {
            frame.origin.x = 0.0
        }

        if boundsSize.height >= frame.size.height {
            frame.origin.y = (boundsSize.height - frame.size.height) / 2.0
        } else {
            frame.origin.y = 0.0
        }

        imageView.frame = frame
    }

    // MARK: - UIScrollViewDelegate

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

    func scrollViewDidZoom(_ scrollView: UIScrollView) {
        guard !ignoreZoom else { return }
        centerContent()
        let isAtMin = scrollView.zoomScale <= scrollView.minimumZoomScale + 0.01
        scrollView.isScrollEnabled = !isAtMin
        onZoomChanged?(!isAtMin)
    }

    // MARK: - Gestures

    @objc private func handleDoubleTap(_ gesture: UITapGestureRecognizer) {
        if scrollView.zoomScale > scrollView.minimumZoomScale + 0.01 {
            scrollView.setZoomScale(scrollView.minimumZoomScale, animated: true)
        } else {
            let point = gesture.location(in: imageView)
            let targetScale = min(scrollView.maximumZoomScale, scrollView.minimumZoomScale * 2.5)
            let w = scrollView.bounds.width / targetScale
            let h = scrollView.bounds.height / targetScale
            let rect = CGRect(x: point.x - w / 2, y: point.y - h / 2, width: w, height: h)
            scrollView.zoom(to: rect, animated: true)
        }
    }

    @objc private func handleSingleTap(_ gesture: UITapGestureRecognizer) {
        let location = gesture.location(in: self)
        let edgeZone = bounds.width * 0.20
        if location.x < edgeZone {
            onEdgeTap?(-1)
        } else if location.x > bounds.width - edgeZone {
            onEdgeTap?(1)
        } else {
            onSingleTap?()
        }
    }
}
