# ==============================================================================
# Toolchain & Executables
# ==============================================================================
LLVM_DIR      ?= /opt/homebrew/opt/llvm/bin
CC            := $(LLVM_DIR)/clang
LLVM_PROFDATA := $(LLVM_DIR)/llvm-profdata
LLVM_COV      := $(LLVM_DIR)/llvm-cov
CPPCHECK      := cppcheck

# ==============================================================================
# Flags & Directories
# ==============================================================================
CFLAGS        := -std=c23 -Wall -Wextra -Wpedantic -Wshadow \
                 -Wconversion -Wsign-conversion \
                 -g -O1 -fno-omit-frame-pointer
SAN_FLAGS     := -fsanitize=address,undefined
INCLUDES      := -Iinclude -Isrc/lib -Isrc/lib/char -Isrc/lib/utils

ASAN_ENV      := ASAN_OPTIONS="allocator_may_return_null=1:detect_stack_use_after_return=1" \
                 UBSAN_OPTIONS="halt_on_error=1:print_stacktrace=1"

CPPCHECK_FLAGS := --enable=all \
                  --inconclusive \
                  --error-exitcode=1 \
                  --check-level=exhaustive \
                  --suppress=missingIncludeSystem \
                  --suppress=unmatchedSuppression \
                  --suppress=unusedFunction \
                  --suppress=staticFunction \
                  --inline-suppr \
                  $(INCLUDES)

# Sources & Build Directories
SRCS          := src/lib/char/char.c src/lib/utils/utils.c
TEST_SRC      := tests/test_learn.c
FUZZ_SRC      := tests/fuzz_char.c

BIN_DIR       := tests/bin
CACHE_DIR     := tests/cache

TEST_BIN      := $(BIN_DIR)/test_learn_runner
TSAN_BIN      := $(BIN_DIR)/test_tsan_runner
FUZZ_BIN      := $(BIN_DIR)/fuzz_char_runner
COV_BIN       := $(BIN_DIR)/test_cov_runner

# ==============================================================================
# Targets & Workflow Rules
# ==============================================================================
.PHONY: all test sanitize check check-sources analyze cppcheck fuzz cov-build cov clean

all: test

$(BIN_DIR) $(CACHE_DIR):
	@mkdir -p $@

# --- Compilation Targets ---

$(TEST_BIN): $(SRCS) $(TEST_SRC) | $(BIN_DIR)
	$(CC) $(CFLAGS) $(SAN_FLAGS) $(INCLUDES) $^ -o $@

$(TSAN_BIN): $(SRCS) $(TEST_SRC) | $(BIN_DIR)
	$(CC) $(CFLAGS) -fsanitize=thread $(INCLUDES) $^ -o $@

$(FUZZ_BIN): $(SRCS) $(FUZZ_SRC) | $(BIN_DIR)
	$(CC) $(CFLAGS) -fsanitize=fuzzer,address,undefined $(INCLUDES) $^ -o $@

# --- Testing & Sanitizer Suites ---

test: $(TEST_BIN)
	@echo "=== Running Unit Tests (ASan + UBSan) ==="
	@$(ASAN_ENV) ./$(TEST_BIN)

sanitize: $(TEST_BIN) $(TSAN_BIN)
	@echo "\n=== [1/2] Running Address + UndefinedBehavior Sanitizers ==="
	@$(ASAN_ENV) ./$(TEST_BIN)
	@echo "\n=== [2/2] Running ThreadSanitizer ==="
	@./$(TSAN_BIN)
	@echo "\n>>> Sanitizers passed with zero defects."

# Unified master check: runtime sanitizers, static analyzer, and cppcheck
check: sanitize analyze cppcheck
	@echo "\n=============================================="
	@echo " ALL TESTS, SANITIZERS & STATIC CHECKS PASSED "
	@echo "=============================================="

fuzz: $(FUZZ_BIN)
	@echo "=== Starting Fuzzer (Press Ctrl+C to stop) ==="
	@$(ASAN_ENV) ./$(FUZZ_BIN) -max_total_time=60

# --- Coverage Profiling ---

cov-build: $(SRCS) $(TEST_SRC) | $(BIN_DIR)
	$(CC) $(CFLAGS) $(SAN_FLAGS) -fprofile-instr-generate -fcoverage-mapping $(INCLUDES) $^ -o $(COV_BIN)

cov: cov-build | $(CACHE_DIR)
	@echo "=== Recording Coverage Profile Data ==="
	@$(ASAN_ENV) LLVM_PROFILE_FILE="$(CACHE_DIR)/test_char.profdata_raw" ./$(COV_BIN)
	@$(LLVM_PROFDATA) merge -sparse $(CACHE_DIR)/test_char.profdata_raw -o $(CACHE_DIR)/test_char.profdata
	@echo "\n=== Line & Branch Coverage Report: src/lib/char/char.c ==="
	@$(LLVM_COV) report $(COV_BIN) -instr-profile=$(CACHE_DIR)/test_char.profdata src/lib/char/char.c
	@$(LLVM_COV) show $(COV_BIN) -instr-profile=$(CACHE_DIR)/test_char.profdata src/lib/char/char.c --format=html > coverage.html
	@echo "\nDetailed HTML report written to coverage.html."

# --- Static Analysis & Object Checking ---

check-sources: | $(BIN_DIR)
	@echo "=== Compiling Sources to Object Files (ASan + UBSan) ==="
	$(CC) $(CFLAGS) $(SAN_FLAGS) $(INCLUDES) -c src/lib/char/char.c -o $(BIN_DIR)/char.o
	$(CC) $(CFLAGS) $(SAN_FLAGS) $(INCLUDES) -c src/lib/utils/utils.c -o $(BIN_DIR)/utils.o
	@echo "Library sources compiled cleanly."

analyze:
	@echo "=== Running Clang Static Analyzer ==="
	@$(CC) $(CFLAGS) $(INCLUDES) \
		--analyze \
		-Xanalyzer -analyzer-output=text \
		-Xanalyzer -analyzer-checker=core,unix,deadcode,security \
		$(SRCS)
	@echo "Clang Static Analyzer complete: zero defects."

cppcheck:
	@echo "=== Running Cppcheck Static Analysis ==="
	@$(CPPCHECK) $(CPPCHECK_FLAGS) include/ src/
	@echo "Cppcheck passed with zero defects."

# --- Housekeeping ---

clean:
	rm -rf $(BIN_DIR)/* $(CACHE_DIR)/* coverage.html
