import SwiftUI
import Bedrock

/// Aspect ratio options for the photo cropper
enum CropAspectRatio: Identifiable, CaseIterable, Equatable {
    case original         // Use image's original aspect ratio
    case square           // 1:1 for profile photos
    case threeToTwo       // 3:2
    case fiveToThree      // 5:3
    case fourToThree      // 4:3
    case fiveToFour       // 5:4
    case sevenToFive      // 7:5
    case sixteenToNine    // 16:9
    case banner           // Wide ratio for cover/banner photos (roughly 2.3:1)
    
    var id: String { displayName }
    
    static var allCases: [CropAspectRatio] {
        [.original, .square, .threeToTwo, .fiveToThree, .fourToThree, .fiveToFour, .sevenToFive, .sixteenToNine]
    }
    
    var displayName: String {
        switch self {
        case .original: return String.localized("Original")
        case .square: return String.localized("Square")
        case .threeToTwo: return "3:2"
        case .fiveToThree: return "5:3"
        case .fourToThree: return "4:3"
        case .fiveToFour: return "5:4"
        case .sevenToFive: return "7:5"
        case .sixteenToNine: return "16:9"
        case .banner: return String.localized("Banner")
        }
    }
    
    /// Returns the ratio (width / height). For .original, pass the image size.
    func ratio(for imageSize: CGSize? = nil) -> CGFloat {
        switch self {
        case .original:
            guard let size = imageSize, size.height > 0 else { return 1.0 }
            return size.width / size.height
        case .square:
            return 1.0
        case .threeToTwo:
            return 3.0 / 2.0
        case .fiveToThree:
            return 5.0 / 3.0
        case .fourToThree:
            return 4.0 / 3.0
        case .fiveToFour:
            return 5.0 / 4.0
        case .sevenToFive:
            return 7.0 / 5.0
        case .sixteenToNine:
            return 16.0 / 9.0
        case .banner:
            return 2.3  // Width is 2.3x height
        }
    }
    
    /// Simple ratio for backwards compatibility (doesn't handle .original properly)
    var ratio: CGFloat {
        ratio(for: nil)
    }
}

/// A sheet that allows the user to crop an image.
/// Supports pinch-to-zoom and drag gestures for positioning.
/// Use `aspectRatio` to specify square, banner, or custom crop shapes.
/// Set `allowAspectRatioSelection` to true to show aspect ratio picker (for logos).
struct PhotoCropperSheet: View {
    @Environment(\.dismiss) private var dismiss
    
    let imageData: Data
    let initialAspectRatio: CropAspectRatio
    let allowAspectRatioSelection: Bool
    let onComplete: (Data?) -> Void  // nil = cancelled, Data = saved
    let shouldDismissOnComplete: Bool
    
    init(
        imageData: Data,
        aspectRatio: CropAspectRatio = .square,
        allowAspectRatioSelection: Bool = false,
        shouldDismissOnComplete: Bool = true,
        onComplete: @escaping (Data?) -> Void
    ) {
        self.imageData = imageData
        self.initialAspectRatio = aspectRatio
        self.allowAspectRatioSelection = allowAspectRatioSelection
        self.shouldDismissOnComplete = shouldDismissOnComplete
        self.onComplete = onComplete
        self._selectedAspectRatio = State(initialValue: aspectRatio)
    }
    
    @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 rotation: Angle = .zero
    @State private var lastRotation: Angle = .zero
    @State private var fixedRotation: Angle = .zero  // For 90° toolbar rotations
    @State private var containerSize: CGSize = .zero
    @State private var selectedAspectRatio: CropAspectRatio
    @State private var showingAspectRatioPicker = false
    
    // Horizontal padding for the crop area
    private let horizontalPadding: CGFloat = 20
    
    /// Current effective aspect ratio (accounting for image size for .original)
    private var effectiveRatio: CGFloat {
        selectedAspectRatio.ratio(for: uiImage?.size)
    }
    
    private var cropWidth: CGFloat {
        // Use almost full width for better cropping experience
        max(100, containerSize.width - (horizontalPadding * 2))
    }
    
    private var cropHeight: CGFloat {
        // Calculate height based on aspect ratio
        let height = cropWidth / effectiveRatio
        // Ensure it fits within the container
        if height > containerSize.height - 100 {
            return containerSize.height - 100
        }
        return height
    }
    
    private var adjustedCropWidth: CGFloat {
        // If height was constrained, recalculate width
        let height = cropWidth / effectiveRatio
        if height > containerSize.height - 100 {
            return cropHeight * effectiveRatio
        }
        return cropWidth
    }
    
    private var cropSize: CGSize {
        // Guard against zero/invalid sizes
        guard containerSize.width > 0, containerSize.height > 0 else {
            return CGSize(width: 280, height: 280 / effectiveRatio)
        }
        return CGSize(width: adjustedCropWidth, height: cropHeight)
    }
    
    private var uiImage: UIImage? {
        UIImage(data: imageData)
    }
    
    var body: some View {
        NavigationStack {
            GeometryReader { geometry in
                ZStack {
                    // Dark background
                    Color.black.ignoresSafeArea()
                    
                    // Image with gestures
                    if let uiImage {
                        Image(uiImage: uiImage)
                            .resizable()
                            .scaledToFill()
                            .rotationEffect(rotation + fixedRotation)
                            .scaleEffect(scale)
                            .offset(offset)
                            .gesture(dragGesture)
                            .gesture(combinedPinchRotateGesture)
                            .frame(width: geometry.size.width, height: geometry.size.height)
                            .clipped()
                    }
                    
                    // Overlay with crop area cutout
                    CropOverlay(cropSize: cropSize, containerSize: geometry.size)
                    
                    // Grid lines inside crop area
                    CropGridLines(cropSize: cropSize)
                        .position(x: geometry.size.width / 2, y: geometry.size.height / 2)
                }
                .onAppear {
                    containerSize = geometry.size
                }
                .onChange(of: geometry.size) { _, newSize in
                    containerSize = newSize
                }
            }
            .navigationBarTitleDisplayMode(.inline)
            .toolbarBackground(.hidden, for: .navigationBar)
            .toolbar {
                ToolbarItem(placement: .cancellationAction) {
                    Button(String.localized("Cancel")) {
                        onComplete(nil)
                        if shouldDismissOnComplete {
                            dismiss()
                        }
                    }
                    .foregroundStyle(.white)
                }
                ToolbarItem(placement: .principal) {
                    HStack(spacing: Design.Spacing.xLarge) {
                        // Rotate 90° left
                        Button {
                            rotate90Left()
                        } label: {
                            Image(systemName: "rotate.left")
                                .foregroundStyle(.white)
                        }
                        
                        // Reset all transforms
                        Button {
                            resetTransform()
                        } label: {
                            Image(systemName: "arrow.counterclockwise")
                                .foregroundStyle(.white)
                        }
                        
                        // Aspect ratio picker (only for logo workflow)
                        if allowAspectRatioSelection {
                            Button {
                                showingAspectRatioPicker = true
                            } label: {
                                Image(systemName: "aspectratio")
                                    .foregroundStyle(.white)
                            }
                        }
                        
                        // Rotate 90° right
                        Button {
                            rotate90Right()
                        } label: {
                            Image(systemName: "rotate.right")
                                .foregroundStyle(.white)
                        }
                    }
                }
                ToolbarItem(placement: .confirmationAction) {
                    Button(String.localized("Done")) {
                        cropAndSave()
                    }
                    .bold()
                    .foregroundStyle(.white)
                }
            }
        }
        .preferredColorScheme(.dark)
        .confirmationDialog(
            String.localized("Aspect Ratio"),
            isPresented: $showingAspectRatioPicker,
            titleVisibility: .visible
        ) {
            ForEach(CropAspectRatio.allCases) { ratio in
                Button(ratio.displayName) {
                    withAnimation(.spring(duration: Design.Animation.springDuration)) {
                        selectedAspectRatio = ratio
                    }
                }
            }
            Button(String.localized("Cancel"), role: .cancel) { }
        }
    }
    
    // MARK: - Gestures
    
    private var dragGesture: some Gesture {
        DragGesture()
            .onChanged { value in
                offset = CGSize(
                    width: lastOffset.width + value.translation.width,
                    height: lastOffset.height + value.translation.height
                )
            }
            .onEnded { _ in
                lastOffset = offset
            }
    }
    
    private var combinedPinchRotateGesture: some Gesture {
        SimultaneousGesture(
            MagnificationGesture(),
            RotationGesture()
        )
        .onChanged { value in
            // Handle pinch (magnification)
            if let magnification = value.first {
                let newScale = lastScale * magnification
                // Limit scale between 0.5x and 5x
                scale = min(max(newScale, 0.5), 5.0)
            }
            
            // Handle rotation
            if let rotationValue = value.second {
                rotation = lastRotation + rotationValue
            }
        }
        .onEnded { _ in
            lastScale = scale
            lastRotation = rotation
        }
    }
    
    // MARK: - Actions
    
    private func resetTransform() {
        withAnimation(.spring(duration: Design.Animation.springDuration)) {
            scale = 1.0
            lastScale = 1.0
            offset = .zero
            lastOffset = .zero
            rotation = .zero
            lastRotation = .zero
            fixedRotation = .zero
        }
    }
    
    private func rotate90Left() {
        withAnimation(.spring(duration: Design.Animation.springDuration)) {
            fixedRotation -= .degrees(90)
        }
    }
    
    private func rotate90Right() {
        withAnimation(.spring(duration: Design.Animation.springDuration)) {
            fixedRotation += .degrees(90)
        }
    }
    
    private func cropAndSave() {
        guard let uiImage else {
            onComplete(nil)
            if shouldDismissOnComplete {
                dismiss()
            }
            return
        }
        
        // First, normalize the image orientation so we're working with correctly oriented pixels
        let orientedImage = normalizeImageOrientation(uiImage)
        
        // Apply total rotation (gesture rotation + fixed rotation)
        let totalRotation = rotation + fixedRotation
        let normalizedImage = rotateImage(orientedImage, by: totalRotation)
        let imageSize = normalizedImage.size
        
        // Guard against zero container size
        guard containerSize.width > 0, containerSize.height > 0 else {
            onComplete(nil)
            if shouldDismissOnComplete {
                dismiss()
            }
            return
        }
        
        // The image is displayed with scaledToFill in the container
        // Calculate the display size of the image (before user scale)
        let containerAspect = containerSize.width / containerSize.height
        let imageAspect = imageSize.width / imageSize.height
        
        let baseDisplaySize: CGSize
        if imageAspect > containerAspect {
            // Image is wider - height fills container
            baseDisplaySize = CGSize(
                width: containerSize.height * imageAspect,
                height: containerSize.height
            )
        } else {
            // Image is taller - width fills container
            baseDisplaySize = CGSize(
                width: containerSize.width,
                height: containerSize.width / imageAspect
            )
        }
        
        // Apply the user's scale to get the actual display size
        let scaledDisplaySize = CGSize(
            width: baseDisplaySize.width * scale,
            height: baseDisplaySize.height * scale
        )
        
        // Calculate the ratio between image pixels and display points
        let displayToImageRatioX = imageSize.width / scaledDisplaySize.width
        let displayToImageRatioY = imageSize.height / scaledDisplaySize.height
        
        // The crop area is centered in the container
        // The image is also centered, but shifted by the user's offset
        
        // In display coordinates:
        // - Container center: (containerSize.width/2, containerSize.height/2)
        // - Image center after offset: (containerSize.width/2 + offset.width, containerSize.height/2 + offset.height)
        // - Crop area center: (containerSize.width/2, containerSize.height/2)
        
        // The crop area's center relative to the image's center (in display coords):
        // cropCenterInImage = cropCenter - imageCenter = -offset
        let cropCenterRelativeToImageCenterX = -offset.width
        let cropCenterRelativeToImageCenterY = -offset.height
        
        // Convert to image pixel coordinates
        // Image center in pixels is (imageSize.width/2, imageSize.height/2)
        let cropCenterInImageX = (imageSize.width / 2) + (cropCenterRelativeToImageCenterX * displayToImageRatioX)
        let cropCenterInImageY = (imageSize.height / 2) + (cropCenterRelativeToImageCenterY * displayToImageRatioY)
        
        // Crop size in image pixels (width and height can be different for banners)
        let cropWidthInImagePixels = cropSize.width * displayToImageRatioX
        let cropHeightInImagePixels = cropSize.height * displayToImageRatioY
        
        // Calculate crop rect origin (top-left corner)
        let cropOriginX = cropCenterInImageX - (cropWidthInImagePixels / 2)
        let cropOriginY = cropCenterInImageY - (cropHeightInImagePixels / 2)
        
        // Clamp to image bounds
        let clampedX = max(0, min(cropOriginX, imageSize.width - cropWidthInImagePixels))
        let clampedY = max(0, min(cropOriginY, imageSize.height - cropHeightInImagePixels))
        let clampedWidth = min(cropWidthInImagePixels, imageSize.width - clampedX)
        let clampedHeight = min(cropHeightInImagePixels, imageSize.height - clampedY)
        
        let cropRect = CGRect(
            x: clampedX,
            y: clampedY,
            width: clampedWidth,
            height: clampedHeight
        )
        
        // Crop the normalized image
        guard let cgImage = normalizedImage.cgImage,
              let croppedCGImage = cgImage.cropping(to: cropRect) else {
            onComplete(nil)
            if shouldDismissOnComplete {
                dismiss()
            }
            return
        }
        
        let croppedUIImage = UIImage(cgImage: croppedCGImage)
        
        // Resize to a consistent output size based on aspect ratio
        let outputSize: CGSize
        let baseWidth: CGFloat = 1024
        let currentRatio = effectiveRatio
        
        switch selectedAspectRatio {
        case .square:
            outputSize = CGSize(width: 512, height: 512)
        case .banner:
            outputSize = CGSize(width: baseWidth, height: baseWidth / 2.3)
        default:
            // For all other ratios, use base width and calculate height
            outputSize = CGSize(width: baseWidth, height: baseWidth / currentRatio)
        }
        
        let format = UIGraphicsImageRendererFormat()
        format.scale = 1
        
        let renderer = UIGraphicsImageRenderer(size: outputSize, format: format)
        let resizedImage = renderer.image { _ in
            croppedUIImage.draw(in: CGRect(origin: .zero, size: outputSize))
        }
        
        if let jpegData = resizedImage.jpegData(compressionQuality: 0.85) {
            onComplete(jpegData)
        } else {
            onComplete(nil)
        }
        
        if shouldDismissOnComplete {
            dismiss()
        }
    }
    
    /// Normalizes image orientation by redrawing with correct orientation applied
    private func normalizeImageOrientation(_ image: UIImage) -> UIImage {
        guard image.imageOrientation != .up else { return image }
        
        let format = UIGraphicsImageRendererFormat()
        format.scale = image.scale
        
        let renderer = UIGraphicsImageRenderer(size: image.size, format: format)
        return renderer.image { _ in
            image.draw(at: .zero)
        }
    }
    
    /// Rotates an image by the specified angle
    private func rotateImage(_ image: UIImage, by angle: Angle) -> UIImage {
        // If no rotation needed, return original
        guard abs(angle.radians) > 0.001 else { return image }
        
        let radians = CGFloat(angle.radians)
        
        // Calculate the size of the rotated image
        let rotatedRect = CGRect(origin: .zero, size: image.size)
            .applying(CGAffineTransform(rotationAngle: radians))
        let rotatedSize = CGSize(
            width: abs(rotatedRect.width),
            height: abs(rotatedRect.height)
        )
        
        let format = UIGraphicsImageRendererFormat()
        format.scale = 1  // Use 1:1 scale for output
        
        let renderer = UIGraphicsImageRenderer(size: rotatedSize, format: format)
        return renderer.image { context in
            let cgContext = context.cgContext
            
            // Move to center
            cgContext.translateBy(x: rotatedSize.width / 2, y: rotatedSize.height / 2)
            
            // Rotate
            cgContext.rotate(by: radians)
            
            // Draw image centered
            image.draw(in: CGRect(
                x: -image.size.width / 2,
                y: -image.size.height / 2,
                width: image.size.width,
                height: image.size.height
            ))
        }
    }
}

// MARK: - Crop Overlay

private struct CropOverlay: View {
    let cropSize: CGSize
    let containerSize: CGSize
    
    var body: some View {
        ZStack {
            // Semi-transparent overlay
            Rectangle()
                .fill(Color.black.opacity(Design.Opacity.accent))
            
            // Clear rectangle in center (can be square or banner)
            Rectangle()
                .fill(Color.clear)
                .frame(width: cropSize.width, height: cropSize.height)
                .blendMode(.destinationOut)
        }
        .compositingGroup()
        .allowsHitTesting(false)
        
        // Border around crop area
        Rectangle()
            .stroke(Color.white, lineWidth: Design.LineWidth.thin)
            .frame(width: cropSize.width, height: cropSize.height)
            .allowsHitTesting(false)
    }
}

// MARK: - Crop Grid Lines

private struct CropGridLines: View {
    let cropSize: CGSize
    
    var body: some View {
        ZStack {
            // Vertical lines (rule of thirds)
            HStack(spacing: cropSize.width / 3 - Design.LineWidth.thin) {
                ForEach(0..<2, id: \.self) { _ in
                    Rectangle()
                        .fill(Color.white.opacity(Design.Opacity.light))
                        .frame(width: Design.LineWidth.thin, height: cropSize.height)
                }
            }
            
            // Horizontal lines (rule of thirds)
            VStack(spacing: cropSize.height / 3 - Design.LineWidth.thin) {
                ForEach(0..<2, id: \.self) { _ in
                    Rectangle()
                        .fill(Color.white.opacity(Design.Opacity.light))
                        .frame(width: cropSize.width, height: Design.LineWidth.thin)
                }
            }
        }
        .frame(width: cropSize.width, height: cropSize.height)
        .allowsHitTesting(false)
    }
}

// MARK: - Preview

#Preview {
    // Create a sample image for preview
    let sampleImage = UIImage(systemName: "person.fill")!
    let sampleData = sampleImage.pngData()!
    
    return PhotoCropperSheet(imageData: sampleData) { data in
        print(data != nil ? "Saved" : "Cancelled")
    }
}
