import UIKit

enum MediaBubbleCornerMaskFactory {
    private static let mainRadius: CGFloat = 16
    private static let mergedRadius: CGFloat = 8
    // Telegram: photo corners use the SAME radius as bubble (16pt).
    // The 2pt gap is purely spatial (photoFrame offset), NOT radius reduction.
    // Non-concentric circles with same radius + 2pt offset create a natural
    // slightly wider gap at corners (~2.83pt at 45°) which matches Telegram.
    private static let inset: CGFloat = 0

    /// Full bubble mask INCLUDING tail shape — used for photo-only messages
    /// where the photo fills the entire bubble area.
    static func fullBubbleMask(
        bounds: CGRect,
        mergeType: BubbleMergeType,
        outgoing: Bool
    ) -> CAShapeLayer {
        let path = BubbleGeometryEngine.makeCGPath(
            in: bounds, mergeType: mergeType, outgoing: outgoing
        )
        let mask = CAShapeLayer()
        mask.frame = bounds
        mask.path = path
        return mask
    }

    static func containerMask(
        bounds: CGRect,
        mergeType: BubbleMergeType,
        outgoing: Bool
    ) -> CAShapeLayer {
        let radii = mediaCornerRadii(mergeType: mergeType, outgoing: outgoing)
        return makeMaskLayer(
            in: bounds,
            topLeft: radii.topLeft,
            topRight: radii.topRight,
            bottomLeft: radii.bottomLeft,
            bottomRight: radii.bottomRight
        )
    }

    static func tileMask(
        tileFrame: CGRect,
        containerBounds: CGRect,
        mergeType: BubbleMergeType,
        outgoing: Bool
    ) -> CAShapeLayer? {
        let eps: CGFloat = 0.5
        let touchesLeft = abs(tileFrame.minX - containerBounds.minX) <= eps
        let touchesRight = abs(tileFrame.maxX - containerBounds.maxX) <= eps
        let touchesTop = abs(tileFrame.minY - containerBounds.minY) <= eps
        let touchesBottom = abs(tileFrame.maxY - containerBounds.maxY) <= eps
        guard touchesLeft || touchesRight || touchesTop || touchesBottom else {
            return nil
        }

        let containerRadii = mediaCornerRadii(mergeType: mergeType, outgoing: outgoing)
        let tl = (touchesTop && touchesLeft) ? containerRadii.topLeft : 0
        let tr = (touchesTop && touchesRight) ? containerRadii.topRight : 0
        let bl = (touchesBottom && touchesLeft) ? containerRadii.bottomLeft : 0
        let br = (touchesBottom && touchesRight) ? containerRadii.bottomRight : 0

        let local = CGRect(origin: .zero, size: tileFrame.size)
        return makeMaskLayer(
            in: local,
            topLeft: tl,
            topRight: tr,
            bottomLeft: bl,
            bottomRight: br
        )
    }

    private static func mediaCornerRadii(
        mergeType: BubbleMergeType,
        outgoing: Bool
    ) -> (topLeft: CGFloat, topRight: CGFloat, bottomLeft: CGFloat, bottomRight: CGFloat) {
        let metrics = BubbleMetrics(
            mainRadius: mainRadius,
            auxiliaryRadius: mergedRadius,
            tailProtrusion: 6,
            defaultSpacing: 0,
            mergedSpacing: 0,
            textInsets: .zero,
            mediaStatusInsets: .zero
        )
        let base = BubbleGeometryEngine.cornerRadii(
            mergeType: mergeType,
            outgoing: outgoing,
            metrics: metrics
        )

        // Telegram: photo corners use the SAME radii as the bubble body.
        // No reduction at tail corner — the bubble's raster image behind
        // the 2pt gap handles the tail shape visually.
        return (
            topLeft: base.topLeft,
            topRight: base.topRight,
            bottomLeft: base.bottomLeft,
            bottomRight: base.bottomRight
        )
    }

    private static func makeMaskLayer(
        in rect: CGRect,
        topLeft: CGFloat,
        topRight: CGFloat,
        bottomLeft: CGFloat,
        bottomRight: CGFloat
    ) -> CAShapeLayer {
        let path = roundedPath(
            in: rect,
            topLeft: topLeft,
            topRight: topRight,
            bottomLeft: bottomLeft,
            bottomRight: bottomRight
        )
        let mask = CAShapeLayer()
        mask.frame = rect
        mask.path = path.cgPath
        return mask
    }

    private static func roundedPath(
        in rect: CGRect,
        topLeft: CGFloat,
        topRight: CGFloat,
        bottomLeft: CGFloat,
        bottomRight: CGFloat
    ) -> UIBezierPath {
        let maxRadius = min(rect.width, rect.height) / 2
        let cTL = min(topLeft, maxRadius)
        let cTR = min(topRight, maxRadius)
        let cBL = min(bottomLeft, maxRadius)
        let cBR = min(bottomRight, maxRadius)

        let path = UIBezierPath()
        path.move(to: CGPoint(x: rect.minX + cTL, y: rect.minY))
        path.addLine(to: CGPoint(x: rect.maxX - cTR, y: rect.minY))
        path.addArc(
            withCenter: CGPoint(x: rect.maxX - cTR, y: rect.minY + cTR),
            radius: cTR,
            startAngle: -.pi / 2,
            endAngle: 0,
            clockwise: true
        )
        path.addLine(to: CGPoint(x: rect.maxX, y: rect.maxY - cBR))
        path.addArc(
            withCenter: CGPoint(x: rect.maxX - cBR, y: rect.maxY - cBR),
            radius: cBR,
            startAngle: 0,
            endAngle: .pi / 2,
            clockwise: true
        )
        path.addLine(to: CGPoint(x: rect.minX + cBL, y: rect.maxY))
        path.addArc(
            withCenter: CGPoint(x: rect.minX + cBL, y: rect.maxY - cBL),
            radius: cBL,
            startAngle: .pi / 2,
            endAngle: .pi,
            clockwise: true
        )
        path.addLine(to: CGPoint(x: rect.minX, y: rect.minY + cTL))
        path.addArc(
            withCenter: CGPoint(x: rect.minX + cTL, y: rect.minY + cTL),
            radius: cTL,
            startAngle: .pi,
            endAngle: -.pi / 2,
            clockwise: true
        )
        path.close()
        return path
    }
}
