import SwiftUI
import UIKit

// MARK: - ZoomableImagePage

/// Single page in the image gallery viewer.
/// Uses screen bounds for image sizing (Telegram parity) — GeometryReader is unreliable
/// inside TabView `.page` style, especially during hero animation transitions.
/// GeometryReader is kept only for centering via `.position()`.
struct ZoomableImagePage: View {

    let attachmentId: String
    let onDismiss: () -> Void
    @Binding var showControls: Bool
    @Binding var currentScale: CGFloat
    let onEdgeTap: ((Int) -> Void)?

    @State private var image: UIImage?

    @State private var zoomScale: CGFloat = 1.0
    @State private var zoomOffset: CGSize = .zero
    @GestureState private var pinchScale: CGFloat = 1.0

    /// Fixed screen bounds for fittedSize calculation — not dependent on GeometryReader.
    /// Telegram uses the same approach: sizes are computed against screen dimensions,
    /// UIScrollView handles the rest.
    private let screenSize = UIScreen.main.bounds.size

    var body: some View {
        let effectiveScale = zoomScale * pinchScale

        GeometryReader { geo in
            let centerX = geo.size.width / 2
            let centerY = geo.size.height / 2

            if let image {
                let fitted = fittedSize(image.size, in: screenSize)

                Image(uiImage: image)
                    .resizable()
                    .frame(width: fitted.width, height: fitted.height)
                    .scaleEffect(effectiveScale)
                    .offset(
                        x: effectiveScale > 1.05 ? zoomOffset.width : 0,
                        y: effectiveScale > 1.05 ? zoomOffset.height : 0
                    )
                    .position(x: centerX, y: centerY)
            } else {
                placeholder
                    .position(x: centerX, y: centerY)
            }
        }
        .contentShape(Rectangle())
        .onTapGesture(count: 2) {
            withAnimation(.spring(response: 0.35, dampingFraction: 0.9)) {
                if zoomScale > 1.1 {
                    zoomScale = 1.0
                    zoomOffset = .zero
                } else {
                    zoomScale = 2.5
                }
                currentScale = zoomScale
            }
        }
        .onTapGesture { location in
            let width = UIScreen.main.bounds.width
            let edgeZone = width * 0.20
            if location.x < edgeZone {
                onEdgeTap?(-1)
            } else if location.x > width - edgeZone {
                onEdgeTap?(1)
            } else {
                showControls.toggle()
            }
        }
        .simultaneousGesture(
            MagnifyGesture()
                .updating($pinchScale) { value, state, _ in
                    state = value.magnification
                }
                .onEnded { value in
                    let newScale = zoomScale * value.magnification
                    withAnimation(.spring(response: 0.35, dampingFraction: 0.9)) {
                        zoomScale = min(max(newScale, 1.0), 5.0)
                        if zoomScale <= 1.05 {
                            zoomScale = 1.0
                            zoomOffset = .zero
                        }
                        currentScale = zoomScale
                    }
                }
        )
        .simultaneousGesture(
            zoomScale > 1.05 ?
            DragGesture()
                .onChanged { value in zoomOffset = value.translation }
                .onEnded { _ in }
            : nil
        )
        .task {
            if let cached = AttachmentCache.shared.cachedImage(forAttachmentId: attachmentId) {
                image = cached
                return
            }
            await ImageLoadLimiter.shared.acquire()
            let loaded = await Task.detached(priority: .utility) {
                AttachmentCache.shared.loadImage(forAttachmentId: attachmentId)
            }.value
            await ImageLoadLimiter.shared.release()
            if !Task.isCancelled {
                image = loaded
            }
        }
    }

    // MARK: - Aspect-fit calculation (Telegram parity)

    /// Calculates the image frame to fill the view while maintaining aspect ratio.
    /// Telegram: `contentSize.fitted(boundsSize)` in ZoomableContentGalleryItemNode.
    private func fittedSize(_ imageSize: CGSize, in viewSize: CGSize) -> CGSize {
        guard imageSize.width > 0, imageSize.height > 0,
              viewSize.width > 0, viewSize.height > 0 else {
            return viewSize
        }
        let scale = min(viewSize.width / imageSize.width,
                        viewSize.height / imageSize.height)
        return CGSize(width: imageSize.width * scale,
                      height: imageSize.height * scale)
    }

    // MARK: - Placeholder

    private var placeholder: some View {
        VStack(spacing: 16) {
            ProgressView()
                .tint(.white)
            Text("Loading...")
                .font(.system(size: 14))
                .foregroundStyle(.white.opacity(0.5))
        }
    }
}
