import SwiftUI
import UIKit
import Photos

// MARK: - Data Types

/// Per-image metadata for the gallery viewer.
/// Android parity: `ViewableImage` in `ImageViewerScreen.kt`.
struct ViewableImageInfo: Equatable, Identifiable {
    let attachmentId: String
    let senderName: String
    let timestamp: Date
    let caption: String

    var id: String { attachmentId }
}

/// State for the image gallery viewer.
struct ImageViewerState: Equatable {
    let images: [ViewableImageInfo]
    let initialIndex: Int
}

// MARK: - ImageViewerPresenter

/// UIHostingController subclass that hides the status bar.
/// Uses `AnyView` instead of generic `Content` to avoid a Swift compiler crash
/// in the SIL inliner (SR-XXXXX / rdar://XXXXX).
private final class StatusBarHiddenHostingController: UIHostingController<AnyView> {
    override var prefersStatusBarHidden: Bool { true }
    override var preferredStatusBarUpdateAnimation: UIStatusBarAnimation { .fade }
}

/// Presents the image gallery viewer using UIKit `overFullScreen` presentation
/// — no bottom-sheet slide-up. Appears instantly; the viewer itself fades in.
/// Telegram parity: the viewer appears as a fade overlay covering nav bar and tab bar.
@MainActor
final class ImageViewerPresenter {

    static let shared = ImageViewerPresenter()
    private weak var presentedController: UIViewController?

    func present(state: ImageViewerState) {
        guard presentedController == nil else { return }

        let viewer = ImageGalleryViewer(state: state, onDismiss: { [weak self] in
            self?.dismiss()
        })

        let hostingController = StatusBarHiddenHostingController(rootView: AnyView(viewer))
        hostingController.modalPresentationStyle = .overFullScreen
        hostingController.view.backgroundColor = .clear

        guard let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
              let root = windowScene.keyWindow?.rootViewController
        else { return }

        var presenter = root
        while let presented = presenter.presentedViewController {
            presenter = presented
        }
        presenter.present(hostingController, animated: false)
        presentedController = hostingController
    }

    func dismiss() {
        presentedController?.dismiss(animated: false)
        presentedController = nil
    }
}

// MARK: - ImageGalleryViewer

/// Telegram-style multi-photo gallery viewer with horizontal paging.
/// Android parity: `ImageViewerScreen.kt` — top bar with sender/date,
/// bottom caption bar, edge-tap navigation, velocity dismiss, share/save.
struct ImageGalleryViewer: View {

    let state: ImageViewerState
    let onDismiss: () -> Void

    @State private var currentPage: Int
    @State private var showControls = true
    @State private var currentZoomScale: CGFloat = 1.0
    @State private var backgroundOpacity: Double = 1.0
    @State private var isDismissing = false
    /// Entry/exit animation progress (0 = hidden, 1 = fully visible).
    @State private var presentationAlpha: Double = 0

    private static let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "d MMMM, HH:mm"
        return formatter
    }()

    init(state: ImageViewerState, onDismiss: @escaping () -> Void) {
        self.state = state
        self.onDismiss = onDismiss
        self._currentPage = State(initialValue: state.initialIndex)
    }

    private var currentInfo: ViewableImageInfo? {
        state.images.indices.contains(currentPage) ? state.images[currentPage] : nil
    }

    var body: some View {
        ZStack {
            // Background — fades during drag-to-dismiss and entry/exit
            Color.black
                .opacity(backgroundOpacity * presentationAlpha)
                .ignoresSafeArea()

            // Pager
            TabView(selection: $currentPage) {
                ForEach(Array(state.images.enumerated()), id: \.element.attachmentId) { index, info in
                    ZoomableImagePage(
                        attachmentId: info.attachmentId,
                        onDismiss: { smoothDismiss() },
                        onDismissProgress: { progress in
                            backgroundOpacity = 1.0 - Double(progress) * 0.7
                        },
                        onDismissCancel: {
                            withAnimation(.easeOut(duration: 0.25)) {
                                backgroundOpacity = 1.0
                            }
                        },
                        showControls: $showControls,
                        currentScale: $currentZoomScale,
                        onEdgeTap: { direction in
                            navigateEdgeTap(direction: direction)
                        }
                    )
                    .tag(index)
                }
            }
            .tabViewStyle(.page(indexDisplayMode: .never))
            // Block TabView page swipe when zoomed or dismissing,
            // but ONLY the scroll — NOT all user interaction.
            // .disabled() kills ALL gestures (taps, pinch, etc.) which prevents
            // double-tap zoom out. .scrollDisabled() only blocks the page swipe.
            .scrollDisabled(currentZoomScale > 1.05 || isDismissing)
            .opacity(presentationAlpha)

            // Controls overlay
            controlsOverlay
                .opacity(presentationAlpha)
        }
        .statusBarHidden(true)
        .onAppear {
            prefetchAdjacentImages(around: state.initialIndex)
            // Android: 200ms entry animation (TelegramEasing)
            withAnimation(.easeOut(duration: 0.2)) {
                presentationAlpha = 1.0
            }
        }
        .onChange(of: currentPage) { _, newPage in
            prefetchAdjacentImages(around: newPage)
        }
    }

    // MARK: - Controls Overlay

    @ViewBuilder
    private var controlsOverlay: some View {
        VStack(spacing: 0) {
            // Android parity: slide + fade, 200ms, FastOutSlowInEasing, 24pt slide distance.
            if showControls && !isDismissing {
                topBar
                    .transition(.move(edge: .top).combined(with: .opacity))
            }
            Spacer()
            if showControls && !isDismissing {
                bottomBar
                    .transition(.move(edge: .bottom).combined(with: .opacity))
            }
        }
        .animation(.easeOut(duration: 0.2), value: showControls)
    }

    // MARK: - Top Bar (Android: sender name + date, back arrow)

    private var topBar: some View {
        HStack(spacing: 8) {
            // Back button (Android: arrow back on left)
            Button { smoothDismiss() } label: {
                Image(systemName: "chevron.left")
                    .font(.system(size: 20, weight: .medium))
                    .foregroundStyle(.white)
                    .frame(width: 44, height: 44)
            }

            // Sender name + date
            if let info = currentInfo {
                VStack(alignment: .leading, spacing: 1) {
                    Text(info.senderName)
                        .font(.system(size: 16, weight: .semibold))
                        .foregroundStyle(.white)
                        .lineLimit(1)

                    Text(Self.dateFormatter.string(from: info.timestamp))
                        .font(.system(size: 13))
                        .foregroundStyle(.white.opacity(0.7))
                }
            }

            Spacer()

            // Page counter (if multiple images)
            if state.images.count > 1 {
                Text("\(currentPage + 1) / \(state.images.count)")
                    .font(.system(size: 15, weight: .medium))
                    .foregroundStyle(.white.opacity(0.8))
                    .padding(.trailing, 8)
            }
        }
        .padding(.horizontal, 4)
        .padding(.vertical, 8)
        // Extend dark background up into the notch / Dynamic Island safe area
        .background(Color.black.opacity(0.5).ignoresSafeArea(edges: .top))
    }

    // MARK: - Bottom Bar (Caption + Share/Save)

    private var bottomBar: some View {
        VStack(spacing: 0) {
            // Caption text (Android: AppleEmojiText, 15sp, 4 lines max)
            if let caption = currentInfo?.caption, !caption.isEmpty {
                Text(caption)
                    .font(.system(size: 15))
                    .foregroundStyle(.white)
                    .lineLimit(4)
                    .frame(maxWidth: .infinity, alignment: .leading)
                    .padding(.horizontal, 16)
                    .padding(.vertical, 12)
                    .background(Color.black.opacity(0.5))
            }

            // Action buttons
            HStack(spacing: 32) {
                Button { shareCurrentImage() } label: {
                    Image(systemName: "square.and.arrow.up")
                        .font(.system(size: 20, weight: .medium))
                        .foregroundStyle(.white)
                        .frame(width: 44, height: 44)
                }

                Spacer()

                Button { saveCurrentImage() } label: {
                    Image(systemName: "square.and.arrow.down")
                        .font(.system(size: 20, weight: .medium))
                        .foregroundStyle(.white)
                        .frame(width: 44, height: 44)
                }
            }
            .padding(.horizontal, 24)
            .padding(.bottom, 8)
            // Extend dark background down into the home indicator safe area
            .background(Color.black.opacity(0.5).ignoresSafeArea(edges: .bottom))
        }
    }

    // MARK: - Edge Tap Navigation

    private func navigateEdgeTap(direction: Int) {
        let target = currentPage + direction
        guard target >= 0, target < state.images.count else { return }
        // Android: instant page switch with short fade (120ms)
        currentPage = target
    }

    // MARK: - Smooth Dismiss (Android: 200ms fade-out)

    private func smoothDismiss() {
        guard !isDismissing else { return }
        isDismissing = true

        withAnimation(.easeOut(duration: 0.2)) {
            presentationAlpha = 0
        }

        DispatchQueue.main.asyncAfter(deadline: .now() + 0.22) {
            onDismiss()
        }
    }

    // MARK: - Actions

    private func shareCurrentImage() {
        guard let info = currentInfo,
              let image = AttachmentCache.shared.loadImage(forAttachmentId: info.attachmentId)
        else { return }

        let activityVC = UIActivityViewController(activityItems: [image], applicationActivities: nil)
        if let windowScene = UIApplication.shared.connectedScenes.first as? UIWindowScene,
           let root = windowScene.keyWindow?.rootViewController {
            var presenter = root
            while let presented = presenter.presentedViewController {
                presenter = presented
            }
            activityVC.popoverPresentationController?.sourceView = presenter.view
            activityVC.popoverPresentationController?.sourceRect = CGRect(
                x: presenter.view.bounds.midX, y: presenter.view.bounds.maxY - 50,
                width: 0, height: 0
            )
            presenter.present(activityVC, animated: true)
        }
    }

    private func saveCurrentImage() {
        guard let info = currentInfo,
              let image = AttachmentCache.shared.loadImage(forAttachmentId: info.attachmentId)
        else { return }

        PHPhotoLibrary.requestAuthorization(for: .addOnly) { status in
            guard status == .authorized || status == .limited else { return }
            UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
        }
    }

    // MARK: - Prefetch

    private func prefetchAdjacentImages(around index: Int) {
        // Android: prefetches ±2 images from current page
        for offset in [-2, -1, 1, 2] {
            let i = index + offset
            guard i >= 0, i < state.images.count else { continue }
            let attachmentId = state.images[i].attachmentId
            guard AttachmentCache.shared.cachedImage(forAttachmentId: attachmentId) == nil else { continue }
            Task.detached(priority: .utility) {
                await ImageLoadLimiter.shared.acquire()
                _ = AttachmentCache.shared.loadImage(forAttachmentId: attachmentId)
                await ImageLoadLimiter.shared.release()
            }
        }
    }

}
