import Localization
import SwiftUI

/*
 Similar idea to `Tooltip`, but since that design is outdated at this point,
 we're using a custom type for Hooks capture tooltips. These includes:
 - Adjust song start time
 - Adjust audio levels
 Future:
 - Trim video (if needed)

 Check out this component to see what it looks like: https://www.figma.com/design/Olk2ULF0Qgz2XwZvdMMz0j/Hooks-Feed?node-id=7396-69992&m=dev
 */
public struct HooksCaptureTooltip: View {
    let text: String
    let configuration: Configuration
    let onDismiss: () -> Void

    public init(text: String, configuration: Configuration, onDismiss: @escaping () -> Void) {
        self.text = text
        self.configuration = configuration
        self.onDismiss = onDismiss
    }

    public var body: some View {
        tooltipContent
            .onTapGesture {
                onDismiss()
            }
    }

    @ViewBuilder
    private var tooltipContent: some View {
        switch configuration.trianglePosition {
        case .up:
            VStack(spacing: 0) {
                triangle
                bubble
            }

        case .down:
            VStack(spacing: 0) {
                bubble
                triangle
            }

        case .left:
            HStack(spacing: 0) {
                triangle
                bubble
            }

        case .right:
            HStack(spacing: 0) {
                bubble
                triangle
            }
        }
    }

    private var triangle: some View {
        Triangle(direction: configuration.trianglePosition)
            .fill(configuration.backgroundColor)
            .frame(width: configuration.triangleSize, height: configuration.triangleSize / 2)
    }

    private var bubble: some View {
        Text(text)
            .font(.system(size: 12, weight: .medium))
            .foregroundColor(.SemanticV2.foregroundPrimary)
            .padding(.horizontal, 12)
            .padding(.vertical, 8)
            .background(
                RoundedRectangle(cornerRadius: configuration.cornerRadius)
                    .fill(configuration.backgroundColor)
            )
    }
}

public extension HooksCaptureTooltip {
    struct Configuration: Equatable {
        public let trianglePosition: TrianglePosition
        public let backgroundColor: Color
        public let cornerRadius: CGFloat
        public let triangleSize: CGFloat
        public let spacing: CGFloat
        public let persistenceKey: String

        public init(
            trianglePosition: TrianglePosition,
            backgroundColor: Color = Color.SemanticV2.backgroundTertiary,
            cornerRadius: CGFloat = 12,
            triangleSize: CGFloat = 12,
            spacing: CGFloat = 4,
            persistenceKey: String
        ) {
            self.trianglePosition = trianglePosition
            self.backgroundColor = backgroundColor
            self.cornerRadius = cornerRadius
            self.triangleSize = triangleSize
            self.spacing = spacing
            self.persistenceKey = persistenceKey
        }

        public static let adjustSongStartTime = Configuration(
            trianglePosition: .up,
            persistenceKey: .hasSeenHooksCaptureAdjustSongTooltip
        )
        public static let adjustAudioLevels = Configuration(
            trianglePosition: .right,
            persistenceKey: .hasSeenHooksCaptureAdjustAudioTooltip
        )

        public var displayText: String {
            switch self {
            case .adjustSongStartTime: return L10n.FeatureHooks.adjustSongStartTimeTooltip
            case .adjustAudioLevels: return L10n.FeatureHooks.adjustAudioLevelsTooltip
            default: return ""
            }
        }
    }

    enum TrianglePosition: Equatable {
        case up, down, left, right
    }
}

private struct Triangle: Shape {
    let direction: HooksCaptureTooltip.TrianglePosition

    func path(in rect: CGRect) -> Path {
        var path = Path()
        let cornerRadius: CGFloat = 1.5

        switch direction {
        case .up:
            // Triangle pointing up with rounded tip
            let tipPoint = CGPoint(x: rect.midX, y: rect.minY + cornerRadius)
            let leftPoint = CGPoint(x: rect.minX + cornerRadius, y: rect.maxY)
            let rightPoint = CGPoint(x: rect.maxX - cornerRadius, y: rect.maxY)

            path.move(to: CGPoint(x: rect.midX - cornerRadius, y: rect.minY + cornerRadius))
            path.addQuadCurve(to: CGPoint(x: rect.midX + cornerRadius, y: rect.minY + cornerRadius),
                              control: CGPoint(x: rect.midX, y: rect.minY))
            path.addLine(to: rightPoint)
            path.addLine(to: leftPoint)

        case .down:
            // Triangle pointing down with rounded tip
            let tipPoint = CGPoint(x: rect.midX, y: rect.maxY - cornerRadius)
            let leftPoint = CGPoint(x: rect.minX + cornerRadius, y: rect.minY)
            let rightPoint = CGPoint(x: rect.maxX - cornerRadius, y: rect.minY)

            path.move(to: leftPoint)
            path.addLine(to: rightPoint)
            path.addLine(to: CGPoint(x: rect.midX + cornerRadius, y: rect.maxY - cornerRadius))
            path.addQuadCurve(to: CGPoint(x: rect.midX - cornerRadius, y: rect.maxY - cornerRadius),
                              control: CGPoint(x: rect.midX, y: rect.maxY))

        case .left:
            // Triangle pointing left with rounded tip
            let tipPoint = CGPoint(x: rect.minX + cornerRadius, y: rect.midY)
            let topPoint = CGPoint(x: rect.maxX, y: rect.minY + cornerRadius)
            let bottomPoint = CGPoint(x: rect.maxX, y: rect.maxY - cornerRadius)

            path.move(to: topPoint)
            path.addLine(to: bottomPoint)
            path.addLine(to: CGPoint(x: rect.minX + cornerRadius, y: rect.midY + cornerRadius))
            path.addQuadCurve(to: CGPoint(x: rect.minX + cornerRadius, y: rect.midY - cornerRadius),
                              control: CGPoint(x: rect.minX, y: rect.midY))

        case .right:
            // Triangle pointing right with rounded tip
            let tipPoint = CGPoint(x: rect.maxX - cornerRadius, y: rect.midY)
            let topPoint = CGPoint(x: rect.minX, y: rect.minY + cornerRadius)
            let bottomPoint = CGPoint(x: rect.minX, y: rect.maxY - cornerRadius)

            path.move(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.midY - cornerRadius))
            path.addQuadCurve(to: CGPoint(x: rect.maxX - cornerRadius, y: rect.midY + cornerRadius),
                              control: CGPoint(x: rect.maxX, y: rect.midY))
            path.addLine(to: bottomPoint)
            path.addLine(to: topPoint)
        }

        path.closeSubpath()
        return path
    }
}

public struct HooksCaptureTooltipModifier: ViewModifier {
    let showTooltip: Bool
    let text: String
    let configuration: HooksCaptureTooltip.Configuration
    let onDismiss: () -> Void

    // Defaults in case tooltip size isn't available onAppear
    private let defaultHeightOffset: CGFloat = 40
    private let defaultWidthOffset: CGFloat = 40

    @State private var offset: CGSize = .zero

    @State private var tooltipSize: CGSize = .zero {
        didSet {
            switch configuration.trianglePosition {
            case .up:
                offset = CGSize(width: 0, height: defaultHeightOffset)
            case .down:
                offset = CGSize(width: 0, height: -tooltipSize.height - configuration.spacing - configuration.triangleSize / 2)
            case .left:
                offset = CGSize(width: -tooltipSize.width - configuration.spacing - configuration.triangleSize / 2, height: 0)
            case .right:
                offset = CGSize(width: tooltipSize.width + configuration.spacing + configuration.triangleSize / 2, height: 0)
            }
        }
    }

    private var overlayAlignment: Alignment {
        switch configuration.trianglePosition {
        case .up: return .bottom
        case .down: return .top
        case .left: return .trailing
        case .right: return .leading
        }
    }

    public init(showTooltip: Bool, text: String, configuration: HooksCaptureTooltip.Configuration, onDismiss: @escaping () -> Void) {
        self.showTooltip = showTooltip
        self.text = text
        self.configuration = configuration
        self.onDismiss = onDismiss
    }

    public func body(content: Content) -> some View {
        content
            .overlay(alignment: overlayAlignment) {
                if showTooltip {
                    HooksCaptureTooltip(text: text, configuration: configuration) {
                        withAnimation(.easeOut(duration: 0.3)) {
                            onDismiss()
                        }
                    }
                    .onGeometryChange(for: CGSize.self) { proxy in proxy.size } action: { size in
                        tooltipSize = size
                    }
                    .offset(offset)
                    .transition(
                        .asymmetric(
                            insertion: .scale(scale: 0.8, anchor: .center)
                                .combined(with: .opacity)
                                .animation(.easeOut(duration: 0.2)),
                            removal: .scale(scale: 0.95, anchor: .center)
                                .combined(with: .opacity)
                                .animation(.easeOut(duration: 0.2))
                        )
                    )
                    .zIndex(.infinity)
                }
            }
    }
}

public extension View {
    func hooksCaptureTooltip(
        show: Bool,
        text: String,
        configuration: HooksCaptureTooltip.Configuration,
        onDismiss: @escaping () -> Void
    ) -> some View {
        modifier(HooksCaptureTooltipModifier(showTooltip: show, text: text, configuration: configuration, onDismiss: onDismiss))
    }

    func maybeHooksCaptureTooltip(
        configuration: HooksCaptureTooltip.Configuration?,
        onDismiss: @escaping () -> Void
    ) -> some View {
        modifier(HooksCaptureTooltipModifier(
            showTooltip: configuration != nil,
            text: configuration?.displayText ?? "",
            configuration: configuration ?? .adjustSongStartTime,
            onDismiss: onDismiss
        ))
    }
}
