//
//  CanvasView.swift
//  CheapRetouch
//
//  Main editing canvas with pinch-to-zoom, pan, mask overlay, and before/after comparison.
//

import SwiftUI
import UIKit

struct CanvasView: View {
    @Bindable var viewModel: EditorViewModel

    @State private var scale: CGFloat = 1.0
    @State private var lastScale: CGFloat = 1.0
    @State private var offset: CGSize = .zero
    @State private var lastOffset: CGSize = .zero
    @State private var isShowingOriginal = false
    @State private var viewSize: CGSize = .zero

    private let minScale: CGFloat = 1.0
    private let maxScale: CGFloat = 10.0

    var body: some View {
        GeometryReader { geometry in
            ZStack {
                Color.black

                if let cgImage = isShowingOriginal ? viewModel.originalImage : viewModel.displayImage {
                    // Main image
                    Image(decorative: cgImage, scale: 1.0)
                        .resizable()
                        .aspectRatio(contentMode: .fit)
                        .scaleEffect(scale)
                        .offset(offset)

                    // Mask overlay - uses cyan for high contrast (colorblind-friendly)
                    if let mask = viewModel.maskPreview {
                        Image(decorative: mask, scale: 1.0)
                            .resizable()
                            .aspectRatio(contentMode: .fit)
                            .scaleEffect(scale)
                            .offset(offset)
                            .blendMode(.multiply)
                            .colorMultiply(viewModel.useHighContrastMask ? .cyan.opacity(0.7) : .red.opacity(0.5))
                            .accessibilityLabel("Selected area preview")
                    }

                    // Brush canvas overlay
                    if viewModel.selectedTool == .brush && !viewModel.showingMaskConfirmation {
                        BrushCanvasView(
                            viewModel: viewModel,
                            imageSize: viewModel.imageSize,
                            displayedImageFrame: displayedImageFrame(in: geometry.size)
                        )
                    }

                }
            }
            .contentShape(Rectangle())
            .gesture(tapGesture(in: geometry))
            .gesture(magnificationGesture(in: geometry))
            .simultaneousGesture(combinedDragGesture(in: geometry))
            .simultaneousGesture(longPressGesture)
            .onTapGesture(count: 2) {
                doubleTapZoom()
            }
            .onAppear {
                viewSize = geometry.size
            }
            .onChange(of: geometry.size) { _, newSize in
                viewSize = newSize
            }
            .onChange(of: viewModel.triggerResetZoom) { _, _ in
                // Reset zoom and offset when a new image is loaded
                scale = 1.0
                lastScale = 1.0
                offset = .zero
                lastOffset = .zero
            }
        }
        .clipped()
    }

    // MARK: - Computed Properties

    private func displayedImageFrame(in viewSize: CGSize) -> CGRect {
        let imageSize = viewModel.imageSize
        guard imageSize.width > 0, imageSize.height > 0 else {
            return .zero
        }

        let imageAspect = imageSize.width / imageSize.height
        let viewAspect = viewSize.width / viewSize.height

        let displayedSize: CGSize
        if imageAspect > viewAspect {
            displayedSize = CGSize(
                width: viewSize.width,
                height: viewSize.width / imageAspect
            )
        } else {
            displayedSize = CGSize(
                width: viewSize.height * imageAspect,
                height: viewSize.height
            )
        }

        let scaledSize = CGSize(
            width: displayedSize.width * scale,
            height: displayedSize.height * scale
        )

        let origin = CGPoint(
            x: (viewSize.width - scaledSize.width) / 2 + offset.width,
            y: (viewSize.height - scaledSize.height) / 2 + offset.height
        )

        return CGRect(origin: origin, size: scaledSize)
    }

    // MARK: - Gestures

    private func tapGesture(in geometry: GeometryProxy) -> some Gesture {
        SpatialTapGesture()
            .onEnded { value in
                DebugLogger.action("CanvasView tap at \(value.location)")
                DebugLogger.state("isProcessing: \(viewModel.isProcessing), showingMaskConfirmation: \(viewModel.showingMaskConfirmation), tool: \(viewModel.selectedTool)")

                guard !viewModel.isProcessing else {
                    DebugLogger.log("Tap ignored - isProcessing")
                    return
                }
                guard !viewModel.showingMaskConfirmation else {
                    DebugLogger.log("Tap ignored - showingMaskConfirmation")
                    return
                }
                guard viewModel.selectedTool != .brush else {
                    DebugLogger.log("Tap ignored - brush tool selected")
                    return
                }
                guard viewModel.selectedTool != .move else {
                    DebugLogger.log("Tap ignored - move tool selected")
                    return
                }

                let imagePoint = convertViewPointToImagePoint(value.location, in: geometry.size)
                DebugLogger.log("Converted to image point: \(imagePoint)")
                Task {
                    await viewModel.handleTap(at: imagePoint)
                }
            }
    }

    private func combinedDragGesture(in geometry: GeometryProxy) -> some Gesture {
        DragGesture(minimumDistance: 1)
            .onChanged { value in
                // Only allow panning when move tool is selected
                guard viewModel.selectedTool == .move else { return }

                // Pan mode: only when zoomed in
                if scale > 1.0 {
                    offset = CGSize(
                        width: lastOffset.width + value.translation.width,
                        height: lastOffset.height + value.translation.height
                    )
                }
            }
            .onEnded { _ in
                // Only allow panning when move tool is selected
                guard viewModel.selectedTool == .move else { return }

                lastOffset = offset
                withAnimation(.spring(duration: 0.3)) {
                    clampOffset(in: geometry.size)
                }
            }
    }

    private func magnificationGesture(in geometry: GeometryProxy) -> some Gesture {
        MagnificationGesture()
            .onChanged { value in
                // Only allow zooming when move tool is selected
                guard viewModel.selectedTool == .move else { return }

                let newScale = lastScale * value
                scale = min(max(newScale, minScale), maxScale)
            }
            .onEnded { _ in
                // Only allow zooming when move tool is selected
                guard viewModel.selectedTool == .move else { return }

                lastScale = scale
                withAnimation(.spring(duration: 0.3)) {
                    clampOffset(in: geometry.size)
                }
            }
    }

    private var longPressGesture: some Gesture {
        LongPressGesture(minimumDuration: 0.3)
            .onEnded { _ in
                // Toggle to show original briefly
                isShowingOriginal = true
                Task {
                    try? await Task.sleep(for: .seconds(0.5))
                    isShowingOriginal = false
                }
            }
    }

    private func doubleTapZoom() {
        // Only allow double-tap zoom when move tool is selected
        guard viewModel.selectedTool == .move else { return }

        withAnimation(.spring(duration: 0.3)) {
            if scale > 1.0 {
                scale = 1.0
                offset = .zero
                lastScale = 1.0
                lastOffset = .zero
            } else {
                scale = 2.5
                lastScale = 2.5
            }
        }
    }

    // MARK: - Coordinate Conversion

    private func convertViewPointToImagePoint(_ viewPoint: CGPoint, in viewSize: CGSize) -> CGPoint {
        let imageSize = viewModel.imageSize
        guard imageSize.width > 0, imageSize.height > 0 else { return viewPoint }

        // Calculate the displayed image frame
        let imageAspect = imageSize.width / imageSize.height
        let viewAspect = viewSize.width / viewSize.height

        let displayedSize: CGSize
        if imageAspect > viewAspect {
            displayedSize = CGSize(
                width: viewSize.width,
                height: viewSize.width / imageAspect
            )
        } else {
            displayedSize = CGSize(
                width: viewSize.height * imageAspect,
                height: viewSize.height
            )
        }

        // Account for scale and offset
        let scaledSize = CGSize(
            width: displayedSize.width * scale,
            height: displayedSize.height * scale
        )

        let scaledOrigin = CGPoint(
            x: (viewSize.width - scaledSize.width) / 2 + offset.width,
            y: (viewSize.height - scaledSize.height) / 2 + offset.height
        )

        // Convert view point to image coordinates
        let relativeX = (viewPoint.x - scaledOrigin.x) / scaledSize.width
        let relativeY = (viewPoint.y - scaledOrigin.y) / scaledSize.height

        return CGPoint(
            x: relativeX * imageSize.width,
            y: relativeY * imageSize.height
        )
    }

    // MARK: - Helpers

    private func clampOffset(in size: CGSize) {
        guard scale > 1.0 else {
            offset = .zero
            lastOffset = .zero
            return
        }

        let imageSize = viewModel.imageSize
        guard imageSize.width > 0, imageSize.height > 0 else { return }

        let imageAspect = imageSize.width / imageSize.height
        let viewAspect = size.width / size.height

        let imageDisplaySize: CGSize
        if imageAspect > viewAspect {
            imageDisplaySize = CGSize(
                width: size.width,
                height: size.width / imageAspect
            )
        } else {
            imageDisplaySize = CGSize(
                width: size.height * imageAspect,
                height: size.height
            )
        }

        let scaledSize = CGSize(
            width: imageDisplaySize.width * scale,
            height: imageDisplaySize.height * scale
        )

        let maxOffsetX = max(0, (scaledSize.width - size.width) / 2)
        let maxOffsetY = max(0, (scaledSize.height - size.height) / 2)

        offset.width = min(max(offset.width, -maxOffsetX), maxOffsetX)
        offset.height = min(max(offset.height, -maxOffsetY), maxOffsetY)
        lastOffset = offset
    }
}

#Preview {
    let viewModel = EditorViewModel()
    return CanvasView(viewModel: viewModel)
}
