// SPDX-FileCopyrightText: 2026 John Burwell and contributors
// SPDX-License-Identifier: AGPL-3.0-or-later

import AppKit
import Foundation

enum MinecraftPackageThumbnailRenderer {
    nonisolated static func makeThumbnail(
        for inspection: MinecraftPackageInspector.InspectionResult,
        size: CGSize,
        scale: CGFloat = 1
    ) -> CGImage? {
        let pixelSize = CGSize(
            width: max(size.width * scale, 1),
            height: max(size.height * scale, 1)
        )

        guard
            let bitmap = NSBitmapImageRep(
                bitmapDataPlanes: nil,
                pixelsWide: max(Int(pixelSize.width.rounded(.up)), 1),
                pixelsHigh: max(Int(pixelSize.height.rounded(.up)), 1),
                bitsPerSample: 8,
                samplesPerPixel: 4,
                hasAlpha: true,
                isPlanar: false,
                colorSpaceName: .deviceRGB,
                bytesPerRow: 0,
                bitsPerPixel: 0
            )
        else {
            return nil
        }

        bitmap.size = pixelSize

        NSGraphicsContext.saveGraphicsState()
        defer { NSGraphicsContext.restoreGraphicsState() }

        guard let context = NSGraphicsContext(bitmapImageRep: bitmap) else {
            return nil
        }

        NSGraphicsContext.current = context
        let rect = CGRect(origin: .zero, size: pixelSize)

        if let iconURL = inspection.iconURL,
           let iconImage = NSImage(contentsOf: iconURL) {
            drawSquareThumbnail(iconImage, in: rect)
        } else {
            drawFallbackThumbnail(for: inspection.contentType, in: rect)
        }

        context.flushGraphics()
        return bitmap.cgImage
    }

    nonisolated private static func drawSquareThumbnail(_ image: NSImage, in rect: CGRect) {
        NSColor.clear.setFill()
        rect.fill()

        let cornerRadius = min(rect.width, rect.height) * 0.10
        let clipRect = rect.insetBy(dx: max(rect.width * 0.02, 1), dy: max(rect.height * 0.02, 1))
        let clipPath = NSBezierPath(
            roundedRect: clipRect,
            xRadius: cornerRadius,
            yRadius: cornerRadius
        )
        clipPath.addClip()

        let imageSize = image.size
        guard imageSize.width > 0, imageSize.height > 0 else {
            image.draw(in: clipRect)
            return
        }

        let scale = max(clipRect.width / imageSize.width, clipRect.height / imageSize.height)
        let drawSize = CGSize(width: imageSize.width * scale, height: imageSize.height * scale)
        let drawRect = CGRect(
            x: clipRect.midX - drawSize.width / 2,
            y: clipRect.midY - drawSize.height / 2,
            width: drawSize.width,
            height: drawSize.height
        )

        image.draw(in: drawRect)

        NSColor.black.withAlphaComponent(0.10).setStroke()
        let borderPath = NSBezierPath(
            roundedRect: clipRect.insetBy(dx: 0.5, dy: 0.5),
            xRadius: max(cornerRadius - 0.5, 0),
            yRadius: max(cornerRadius - 0.5, 0)
        )
        borderPath.lineWidth = max(1, rect.width * 0.01)
        borderPath.stroke()
    }

    nonisolated private static func drawFallbackThumbnail(
        for contentType: MinecraftContentType,
        in rect: CGRect
    ) {
        let path = NSBezierPath(
            roundedRect: rect,
            xRadius: rect.width * 0.10,
            yRadius: rect.width * 0.10
        )

        let gradient = NSGradient(colors: backgroundColors(for: contentType))
        gradient?.draw(in: path, angle: -45)

        let badgeRect = rect.insetBy(dx: rect.width * 0.18, dy: rect.height * 0.18)
        drawVoxelBadge(in: badgeRect)

        NSColor.black.withAlphaComponent(0.10).setStroke()
        path.lineWidth = max(1, rect.width * 0.01)
        path.stroke()
    }

    nonisolated private static func drawVoxelBadge(in rect: CGRect) {
        let topFace = NSBezierPath()
        topFace.move(to: CGPoint(x: rect.midX, y: rect.maxY))
        topFace.line(to: CGPoint(x: rect.maxX, y: rect.maxY - rect.height * 0.18))
        topFace.line(to: CGPoint(x: rect.midX, y: rect.maxY - rect.height * 0.36))
        topFace.line(to: CGPoint(x: rect.minX, y: rect.maxY - rect.height * 0.18))
        topFace.close()
        NSColor(calibratedRed: 0.78, green: 0.94, blue: 0.63, alpha: 1).setFill()
        topFace.fill()

        let leftFace = NSBezierPath()
        leftFace.move(to: CGPoint(x: rect.minX, y: rect.maxY - rect.height * 0.18))
        leftFace.line(to: CGPoint(x: rect.midX, y: rect.maxY - rect.height * 0.36))
        leftFace.line(to: CGPoint(x: rect.midX, y: rect.minY))
        leftFace.line(to: CGPoint(x: rect.minX, y: rect.minY + rect.height * 0.18))
        leftFace.close()
        NSColor(calibratedRed: 0.34, green: 0.61, blue: 0.24, alpha: 1).setFill()
        leftFace.fill()

        let rightFace = NSBezierPath()
        rightFace.move(to: CGPoint(x: rect.maxX, y: rect.maxY - rect.height * 0.18))
        rightFace.line(to: CGPoint(x: rect.midX, y: rect.maxY - rect.height * 0.36))
        rightFace.line(to: CGPoint(x: rect.midX, y: rect.minY))
        rightFace.line(to: CGPoint(x: rect.maxX, y: rect.minY + rect.height * 0.18))
        rightFace.close()
        NSColor(calibratedRed: 0.14, green: 0.34, blue: 0.13, alpha: 1).setFill()
        rightFace.fill()

        NSColor.black.withAlphaComponent(0.18).setStroke()
        [topFace, leftFace, rightFace].forEach {
            $0.lineWidth = max(1, rect.width * 0.02)
            $0.stroke()
        }
    }

    nonisolated private static func backgroundColors(for contentType: MinecraftContentType) -> [NSColor] {
        switch contentType {
        case .world:
            return [
                NSColor(calibratedRed: 0.18, green: 0.48, blue: 0.25, alpha: 1),
                NSColor(calibratedRed: 0.47, green: 0.75, blue: 0.31, alpha: 1)
            ]
        case .behaviorPack:
            return [
                NSColor(calibratedRed: 0.58, green: 0.29, blue: 0.15, alpha: 1),
                NSColor(calibratedRed: 0.86, green: 0.60, blue: 0.22, alpha: 1)
            ]
        case .resourcePack:
            return [
                NSColor(calibratedRed: 0.12, green: 0.38, blue: 0.66, alpha: 1),
                NSColor(calibratedRed: 0.31, green: 0.68, blue: 0.85, alpha: 1)
            ]
        case .skinPack:
            return [
                NSColor(calibratedRed: 0.53, green: 0.19, blue: 0.43, alpha: 1),
                NSColor(calibratedRed: 0.84, green: 0.39, blue: 0.62, alpha: 1)
            ]
        case .worldTemplate:
            return [
                NSColor(calibratedRed: 0.23, green: 0.23, blue: 0.54, alpha: 1),
                NSColor(calibratedRed: 0.53, green: 0.52, blue: 0.89, alpha: 1)
            ]
        }
    }
}
