////
// Shell utilities and helper functions.

import { statSync } from "fs"
import { basename } from "path"
import { stat } from "node:fs/promises"

import { NOSE_ICON } from "./config"

// End the process with an instructive error if a directory doesn't exist.
export function expectDir(path: string) {
    if (!isDir(path)) {
        console.error(NOSE_ICON)
        console.error(`No ${basename(path)} directory detected. Please run:`)
        console.error("\tmkdir -p", path)
        process.exit(1)
    }
}

// Is the given `path` a file?
export function isFile(path: string): boolean {
    try {
        const stats = statSync(path)
        return stats.isFile()
    } catch {
        return false
    }
}

// Is the given `path` a directory?
export function isDir(path: string): boolean {
    try {
        const stats = statSync(path)
        return stats.isDirectory()
    } catch {
        return false
    }
}

export async function mtime(path: string): Promise<Date> {
    const { mtime } = await stat(path)
    return mtime
}

// is the given file binary?
export async function isBinaryFile(path: string): Promise<boolean> {
    // Create a stream to read just the beginning
    const file = Bun.file(path)
    const stream = file.stream()
    const reader = stream.getReader()

    try {
        // Read first chunk (typically 16KB, which is more than enough to detect binary)
        const { value } = await reader.read()
        if (!value) return false

        // Check first 512 bytes or less
        const bytes = new Uint8Array(value)
        const checkLength = Math.min(bytes.length, 512)
        for (let i = 0; i < checkLength; i++) {
            const byte = bytes[i]!
            if (byte === 0) return true // null byte
            if (byte < 32 && ![9, 10, 13].includes(byte)) return true // control char
        }

        return false
    } finally {
        reader.releaseLock()
        stream.cancel()
    }
}

// Convert /Users/$USER or /home/$USER to ~ for simplicity
export function tilde(path: string): string {
    return path.replace(new RegExp(`/(Users|home)/${process.env.USER}`), "~")
}

const transpiler = new Bun.Transpiler({ loader: 'tsx' })
const transpileCache: Record<string, string> = {}

// Transpile the frontend *.ts file at `path` to JavaScript.
export async function transpile(path: string): Promise<string> {
    const { mtime } = await stat(path)
    const key = `${path}?${mtime}`

    let cached = transpileCache[key]
    if (!cached) {
        const code = await Bun.file(path).text()
        cached = transpiler.transformSync(code)
        cached = cached.replaceAll(/\bjsxDEV_?\w*\(/g, "jsx(")
        cached = cached.replaceAll(/\bFragment_?\w*,/g, "Fragment,")

        transpileCache[key] = cached
    }

    return cached
}
