#include <stdint.h>
#include <stddef.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <magic.h>

// libmagic internal function
extern int file_printf(magic_t ms, const char *format, ...);

// Unified cleanup
static inline void cleanup_all(magic_t ms, char *fmt) {
    if (ms) magic_close(ms);
    free(fmt);
}

// Construct format string from Data: max length 100, '\0' and '\n' are replaced with '%'
static inline char *build_format_string(const uint8_t *data, size_t size, size_t *out_fmt_len) {
    // Expectation: The upper layer has already ensured size >= 2
    const size_t fmt_len = (size > 100) ? 100 : (size - 1);
    char *fmt = (char *)malloc(fmt_len + 1);
    if (!fmt) return NULL;

    memcpy(fmt, data, fmt_len);
    fmt[fmt_len] = '\0';

    for (size_t i = 0; i < fmt_len; i++) {
        if (fmt[i] == '\0' || fmt[i] == '\n') fmt[i] = '%';
    }

    if (out_fmt_len) *out_fmt_len = fmt_len;
    return fmt;
}

static inline void drive_libmagic_with_format(magic_t ms,
                                             const char *fmt,
                                             const uint8_t *buf,
                                             size_t buf_len) {
    // First, enter the file_buffer code path through magic_buffer
    const char *result = magic_buffer(ms, buf, buf_len);
    if (!result) return;

    // Directly call file_printf(fmt)
    file_printf(ms, fmt);

    // Set ms->o.buf with a valid string "test"
    if (file_printf(ms, "test") == -1) return;

    // Trigger the file_checkfmt path again with the fuzzed fmt
    file_printf(ms, fmt);
}

int LLVMFuzzerTestOneInput(const uint8_t *Data, size_t Size) {
    if (Size < 2) return 0;

    magic_t ms = magic_open(MAGIC_NONE);
    if (!ms) return 0;

    if (magic_load(ms, NULL) == -1) {
        cleanup_all(ms, NULL);
        return 0;
    }

    // Construct format string (up to 100 bytes)
    size_t fmt_len = 0;
    char *fmt = build_format_string(Data, Size, &fmt_len);
    if (!fmt) {
        cleanup_all(ms, NULL);
        return 0;
    }

    // The rest of the data is used as input for magic_buffer
    const uint8_t *buf = Data + fmt_len;
    const size_t   buf_len = Size - fmt_len;

    drive_libmagic_with_format(ms, fmt, buf, buf_len);

    cleanup_all(ms, fmt);
    return 0;
}