//
//  BrushCanvasView.swift
//  CheapRetouch
//
//  Canvas overlay for brush-based manual selection.
//

import SwiftUI
import UIKit

struct BrushCanvasView: View {
    @Bindable var viewModel: EditorViewModel
    let imageSize: CGSize
    let displayedImageFrame: CGRect

    @State private var currentStroke: [CGPoint] = []
    @State private var allStrokes: [[CGPoint]] = []
    @State private var isErasing = false
    @State private var currentTouchLocation: CGPoint?
    @State private var gradientImage: EdgeRefinement.GradientImage?

    var body: some View {
        Canvas { context, size in
            // Draw all completed strokes
            for stroke in allStrokes {
                drawStroke(stroke, in: &context, color: isErasing ? .black : .white)
            }

            // Draw current stroke
            if !currentStroke.isEmpty {
                drawStroke(currentStroke, in: &context, color: isErasing ? .black : .white)
            }

            // Draw brush preview circle at current touch location
            if let location = currentTouchLocation {
                let previewRect = CGRect(
                    x: location.x - viewModel.brushSize / 2,
                    y: location.y - viewModel.brushSize / 2,
                    width: viewModel.brushSize,
                    height: viewModel.brushSize
                )
                context.stroke(
                    Path(ellipseIn: previewRect),
                    with: .color(.white.opacity(0.8)),
                    lineWidth: 2
                )
            }
        }
        .gesture(drawingGesture)
        .overlay(alignment: .bottom) {
            brushControls
        }
        .onAppear {
            // Precompute gradient for edge refinement if enabled
            if viewModel.useEdgeRefinement, let image = viewModel.displayImage {
                computeGradientAsync(from: image)
            }
        }
        .onChange(of: viewModel.useEdgeRefinement) { _, newValue in
            if newValue, gradientImage == nil, let image = viewModel.displayImage {
                computeGradientAsync(from: image)
            }
        }
    }

    private func computeGradientAsync(from image: CGImage) {
        Task {
            let gradient = await Task.detached(priority: .userInitiated) {
                EdgeRefinement.computeGradient(from: image)
            }.value
            gradientImage = gradient
        }
    }

    private func drawStroke(_ points: [CGPoint], in context: inout GraphicsContext, color: Color) {
        guard points.count >= 2 else { return }

        var path = Path()
        path.move(to: points[0])

        if points.count == 2 {
            path.addLine(to: points[1])
        } else {
            for i in 1..<points.count {
                let mid = CGPoint(
                    x: (points[i-1].x + points[i].x) / 2,
                    y: (points[i-1].y + points[i].y) / 2
                )
                path.addQuadCurve(to: mid, control: points[i-1])
            }
            path.addLine(to: points[points.count - 1])
        }

        context.stroke(
            path,
            with: .color(color.opacity(0.7)),
            style: StrokeStyle(
                lineWidth: viewModel.brushSize,
                lineCap: .round,
                lineJoin: .round
            )
        )
    }

    private var drawingGesture: some Gesture {
        DragGesture(minimumDistance: 0)
            .onChanged { value in
                let point = value.location
                currentTouchLocation = point

                // Only add points within the image bounds
                if displayedImageFrame.contains(point) {
                    currentStroke.append(point)
                }
            }
            .onEnded { _ in
                currentTouchLocation = nil
                if !currentStroke.isEmpty {
                    allStrokes.append(currentStroke)
                    currentStroke = []
                }
            }
    }

    private var brushControls: some View {
        HStack(spacing: 16) {
            // Erase toggle
            Button {
                isErasing.toggle()
            } label: {
                Image(systemName: isErasing ? "eraser.fill" : "eraser")
                    .font(.title2)
                    .frame(width: 44, height: 44)
                    .background(isErasing ? Color.accentColor : Color.clear)
                    .clipShape(Circle())
            }
            .accessibilityLabel(isErasing ? "Eraser active" : "Switch to eraser")

            // Clear all
            Button {
                allStrokes.removeAll()
                currentStroke.removeAll()
            } label: {
                Image(systemName: "trash")
                    .font(.title2)
                    .frame(width: 44, height: 44)
            }
            .disabled(allStrokes.isEmpty)
            .accessibilityLabel("Clear all strokes")

            Spacer()

            // Done button
            Button {
                Task {
                    await applyBrushMask()
                }
            } label: {
                Text("Done")
                    .font(.headline)
                    .padding(.horizontal, 20)
                    .padding(.vertical, 10)
            }
            .buttonStyle(.borderedProminent)
            .disabled(allStrokes.isEmpty)
        }
        .padding()
        .background(.ultraThinMaterial)
    }

    private func applyBrushMask() async {
        DebugLogger.action("BrushCanvasView.applyBrushMask called")
        guard !allStrokes.isEmpty else {
            DebugLogger.log("No strokes to apply")
            return
        }

        DebugLogger.log("Stroke count: \(allStrokes.count), total points: \(allStrokes.reduce(0) { $0 + $1.count })")
        DebugLogger.log("Image size: \(imageSize), displayed frame: \(displayedImageFrame)")

        let scaleX = imageSize.width / displayedImageFrame.width
        let scaleY = imageSize.height / displayedImageFrame.height
        DebugLogger.log("Scale factors: X=\(scaleX), Y=\(scaleY)")

        // Convert all strokes to image coordinates
        var imageCoordStrokes: [[CGPoint]] = []
        for stroke in allStrokes {
            let imageStroke = stroke.map { point in
                CGPoint(
                    x: (point.x - displayedImageFrame.minX) * scaleX,
                    y: (point.y - displayedImageFrame.minY) * scaleY
                )
            }
            imageCoordStrokes.append(imageStroke)
        }

        // Apply edge refinement if enabled and gradient is available
        if viewModel.useEdgeRefinement, let gradient = gradientImage {
            imageCoordStrokes = imageCoordStrokes.map { stroke in
                EdgeRefinement.refineSelectionToEdges(
                    selection: stroke,
                    gradient: gradient,
                    searchRadius: Int(viewModel.brushSize / 2)
                )
            }
        }

        // Create mask image from strokes (use scale 1.0 to match actual image pixels)
        let format = UIGraphicsImageRendererFormat()
        format.scale = 1.0  // Don't use screen scale, use actual pixel size
        let renderer = UIGraphicsImageRenderer(size: imageSize, format: format)
        let maskImage = renderer.image { ctx in
            // Fill with black (not masked)
            UIColor.black.setFill()
            ctx.fill(CGRect(origin: .zero, size: imageSize))

            // Draw strokes in white (masked areas)
            UIColor.white.setStroke()

            for stroke in imageCoordStrokes {
                guard stroke.count >= 2 else { continue }

                let path = UIBezierPath()
                path.move(to: stroke[0])

                for i in 1..<stroke.count {
                    path.addLine(to: stroke[i])
                }

                path.lineWidth = viewModel.brushSize * scaleX
                path.lineCapStyle = .round
                path.lineJoinStyle = .round
                path.stroke()
            }
        }

        if let cgImage = maskImage.cgImage {
            DebugLogger.imageInfo("Created brush mask", image: cgImage)
            await viewModel.applyBrushMask(cgImage)
        } else {
            DebugLogger.error("Failed to create CGImage from brush mask")
        }

        // Clear strokes after applying
        allStrokes.removeAll()
        DebugLogger.log("Strokes cleared")
    }
}

// MARK: - Line Brush View for Wire Tool

struct LineBrushView: View {
    @Bindable var viewModel: EditorViewModel
    let imageSize: CGSize
    let displayedImageFrame: CGRect

    @State private var linePoints: [CGPoint] = []

    var body: some View {
        Canvas { context, size in
            guard linePoints.count >= 2 else { return }

            var path = Path()
            path.move(to: linePoints[0])

            for point in linePoints.dropFirst() {
                path.addLine(to: point)
            }

            context.stroke(
                path,
                with: .color(.white.opacity(0.7)),
                style: StrokeStyle(
                    lineWidth: viewModel.wireWidth,
                    lineCap: .round,
                    lineJoin: .round
                )
            )
        }
        .gesture(lineDrawingGesture)
        .overlay(alignment: .bottom) {
            lineControls
        }
    }

    private var lineDrawingGesture: some Gesture {
        DragGesture(minimumDistance: 0)
            .onChanged { value in
                let point = value.location
                if displayedImageFrame.contains(point) {
                    // For line brush, we sample less frequently for smoother lines
                    if linePoints.isEmpty || distance(from: linePoints.last!, to: point) > 5 {
                        linePoints.append(point)
                    }
                }
            }
            .onEnded { _ in
                // Line complete, ready to apply
            }
    }

    private var lineControls: some View {
        HStack(spacing: 16) {
            // Clear
            Button {
                linePoints.removeAll()
            } label: {
                Image(systemName: "trash")
                    .font(.title2)
                    .frame(width: 44, height: 44)
            }
            .disabled(linePoints.isEmpty)
            .accessibilityLabel("Clear line")

            Spacer()

            // Cancel
            Button {
                linePoints.removeAll()
                viewModel.selectedTool = .wire
            } label: {
                Text("Cancel")
                    .font(.headline)
                    .padding(.horizontal, 16)
                    .padding(.vertical, 10)
            }
            .buttonStyle(.bordered)

            // Done button
            Button {
                Task {
                    await applyLineMask()
                }
            } label: {
                Text("Remove Line")
                    .font(.headline)
                    .padding(.horizontal, 16)
                    .padding(.vertical, 10)
            }
            .buttonStyle(.borderedProminent)
            .disabled(linePoints.count < 2)
        }
        .padding()
        .background(.ultraThinMaterial)
    }

    private func applyLineMask() async {
        guard linePoints.count >= 2 else { return }

        let renderer = UIGraphicsImageRenderer(size: imageSize)
        let maskImage = renderer.image { ctx in
            UIColor.black.setFill()
            ctx.fill(CGRect(origin: .zero, size: imageSize))

            UIColor.white.setStroke()

            let scaleX = imageSize.width / displayedImageFrame.width
            let scaleY = imageSize.height / displayedImageFrame.height

            let path = UIBezierPath()
            let firstPoint = CGPoint(
                x: (linePoints[0].x - displayedImageFrame.minX) * scaleX,
                y: (linePoints[0].y - displayedImageFrame.minY) * scaleY
            )
            path.move(to: firstPoint)

            for point in linePoints.dropFirst() {
                let scaledPoint = CGPoint(
                    x: (point.x - displayedImageFrame.minX) * scaleX,
                    y: (point.y - displayedImageFrame.minY) * scaleY
                )
                path.addLine(to: scaledPoint)
            }

            path.lineWidth = viewModel.wireWidth * scaleX
            path.lineCapStyle = .round
            path.lineJoinStyle = .round
            path.stroke()
        }

        if let cgImage = maskImage.cgImage {
            await viewModel.applyBrushMask(cgImage)
        }

        linePoints.removeAll()
    }

    private func distance(from p1: CGPoint, to p2: CGPoint) -> CGFloat {
        sqrt(pow(p2.x - p1.x, 2) + pow(p2.y - p1.y, 2))
    }
}

#Preview {
    let viewModel = EditorViewModel()
    return BrushCanvasView(
        viewModel: viewModel,
        imageSize: CGSize(width: 1000, height: 1000),
        displayedImageFrame: CGRect(x: 0, y: 0, width: 300, height: 300)
    )
}
