import SwiftUI

// MARK: - FullScreenImageViewer

/// Full-screen image viewer with pinch zoom, double-tap zoom, and swipe-to-dismiss.
///
/// Android parity: `ImageViewerScreen.kt` — zoom (1x–5x), double-tap (2.5x),
/// vertical swipe dismiss, background fade, tap to toggle controls.
struct FullScreenImageViewer: View {

    let image: UIImage
    let onDismiss: () -> Void

    /// Current zoom scale (1.0 = fit, up to maxScale).
    @State private var scale: CGFloat = 1.0
    @State private var lastScale: CGFloat = 1.0

    /// Pan offset when zoomed.
    @State private var offset: CGSize = .zero
    @State private var lastOffset: CGSize = .zero

    /// Vertical drag offset for dismiss gesture (only when not zoomed).
    @State private var dismissOffset: CGFloat = 0

    /// Whether the UI controls (close button) are visible.
    @State private var showControls = true

    private let minScale: CGFloat = 1.0
    private let maxScale: CGFloat = 5.0
    private let doubleTapScale: CGFloat = 2.5
    private let dismissThreshold: CGFloat = 150

    var body: some View {
        ZStack {
            // Background: fades as user drags to dismiss
            Color.black
                .opacity(backgroundOpacity)
                .ignoresSafeArea()

            // Zoomable image (visual only — no gestures here)
            Image(uiImage: image)
                .resizable()
                .scaledToFit()
                .scaleEffect(scale)
                .offset(x: offset.width, y: offset.height + dismissOffset)
                .allowsHitTesting(false)

            // Close button (above gesture layer so it stays tappable)
            if showControls {
                VStack {
                    HStack {
                        Spacer()
                        Button {
                            onDismiss()
                        } label: {
                            Image(systemName: "xmark")
                                .font(.system(size: 17, weight: .semibold))
                                .foregroundStyle(.white)
                                .frame(width: 36, height: 36)
                                .background(Color.white.opacity(0.2))
                                .clipShape(Circle())
                        }
                        .padding(.trailing, 16)
                        .padding(.top, 8)
                    }
                    Spacer()
                }
                .transition(.opacity)
            }
        }
        // Gestures on the full-screen ZStack — not on the Image.
        // scaleEffect is visual-only and doesn't expand the Image's hit-test area,
        // so when zoomed to 2.5x, taps outside the original frame were lost.
        .contentShape(Rectangle())
        .onTapGesture(count: 2) {
            doubleTap()
        }
        .onTapGesture(count: 1) {
            withAnimation(.easeInOut(duration: 0.2)) {
                showControls.toggle()
            }
        }
        .simultaneousGesture(pinchGesture)
        .simultaneousGesture(dragGesture)
    }

    // MARK: - Background Opacity

    private var backgroundOpacity: Double {
        let progress = min(abs(dismissOffset) / 300, 1.0)
        return 1.0 - progress * 0.6
    }

    // MARK: - Double Tap Zoom

    private func doubleTap() {
        withAnimation(.spring(response: 0.35, dampingFraction: 0.8)) {
            if scale > 1.05 {
                scale = 1.0
                lastScale = 1.0
                offset = .zero
                lastOffset = .zero
            } else {
                scale = doubleTapScale
                lastScale = doubleTapScale
                offset = .zero
                lastOffset = .zero
            }
        }
    }

    // MARK: - Pinch Gesture

    private var pinchGesture: some Gesture {
        MagnificationGesture()
            .onChanged { value in
                let newScale = lastScale * value
                scale = min(max(newScale, minScale * 0.5), maxScale)
            }
            .onEnded { _ in
                withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
                    if scale < minScale { scale = minScale }
                    lastScale = scale
                    if scale <= 1.0 {
                        offset = .zero
                        lastOffset = .zero
                    }
                }
            }
    }

    // MARK: - Drag Gesture

    private var dragGesture: some Gesture {
        DragGesture()
            .onChanged { value in
                if scale > 1.05 {
                    offset = CGSize(
                        width: lastOffset.width + value.translation.width,
                        height: lastOffset.height + value.translation.height
                    )
                } else {
                    dismissOffset = value.translation.height
                }
            }
            .onEnded { _ in
                if scale > 1.05 {
                    lastOffset = offset
                } else {
                    if abs(dismissOffset) > dismissThreshold {
                        onDismiss()
                    } else {
                        withAnimation(.spring(response: 0.3, dampingFraction: 0.8)) {
                            dismissOffset = 0
                        }
                    }
                }
            }
    }
}

// MARK: - FullScreenImageFromCache

/// Wrapper that loads an image from `AttachmentCache` by attachment ID and
/// presents it in `FullScreenImageViewer`. Handles cache-miss gracefully.
///
/// Used as `fullScreenCover` content — the attachment ID is a stable value
/// passed as a parameter, avoiding @State capture issues with UIImage.
struct FullScreenImageFromCache: View {
    let attachmentId: String
    let onDismiss: () -> Void
    @State private var image: UIImage?
    @State private var isLoading = true

    var body: some View {
        if let image {
            FullScreenImageViewer(image: image, onDismiss: onDismiss)
        } else {
            // Cache miss/loading state — show placeholder with close button.
            ZStack {
                Color.black.ignoresSafeArea()
                if isLoading {
                    VStack(spacing: 16) {
                        ProgressView()
                            .tint(.white)
                        Text("Loading...")
                            .font(.system(size: 15))
                            .foregroundStyle(.white.opacity(0.5))
                    }
                } else {
                    VStack(spacing: 16) {
                        Image(systemName: "photo")
                            .font(.system(size: 48))
                            .foregroundStyle(.white.opacity(0.3))
                        Text("Image not available")
                            .font(.system(size: 15))
                            .foregroundStyle(.white.opacity(0.5))
                    }
                }
                VStack {
                    HStack {
                        Spacer()
                        Button {
                            onDismiss()
                        } label: {
                            Image(systemName: "xmark")
                                .font(.system(size: 17, weight: .semibold))
                                .foregroundStyle(.white)
                                .frame(width: 36, height: 36)
                                .background(Color.white.opacity(0.2))
                                .clipShape(Circle())
                        }
                        .padding(.trailing, 16)
                        .padding(.top, 8)
                    }
                    Spacer()
                }
            }
            .task(id: attachmentId) {
                if let cached = AttachmentCache.shared.cachedImage(forAttachmentId: attachmentId) {
                    image = cached
                    isLoading = false
                    return
                }
                await ImageLoadLimiter.shared.acquire()
                let loaded = await Task.detached(priority: .userInitiated) {
                    AttachmentCache.shared.loadImage(forAttachmentId: attachmentId)
                }.value
                await ImageLoadLimiter.shared.release()
                guard !Task.isCancelled else { return }
                image = loaded
                isLoading = false
            }
        }
    }
}
