package logger

import (
	"fmt"
	"io"
	"os"
	"sync"
	"time"
)

type LogLevel int

const (
	LevelDebug LogLevel = iota
	LevelInfo
	LevelWarn
	LevelLog
	LevelLogTerminal
	LevelError
	LevelFatal
	LevelUnique
)

const (
	colorReset       = "\033[0m"
	colorDebug       = "\033[36m" // Cyan
	colorInfo        = "\033[32m" // Green
	colorLog         = "\033[34m"
	colorLogTerminal = "\033[35m"
	colorWarn        = "\033[33m" // Yellow
	colorError       = "\033[31m" // Red
	colorFatal       = "\033[35m" // Magenta
	colorTime        = "\033[90m" // Dark Gray
	colorPink        = "\033[95m"
)

var globalLevel = LevelDebug

// fileOutput, when set via SetFileOutput, receives an uncolored copy of
// every log line in addition to the colored line written to stdout.
var fileOutput io.Writer

// cron jobs log from different goroutines, keep each line whole in the file
var fileMu sync.Mutex

// errorHook, when set via SetErrorHook, receives every ERROR and FATAL line
var errorHook func(line string)

// SetErrorHook calls hook with every ERROR / FATAL line, e.g. to mail the errors. Pass nil to stop.
func SetErrorHook(hook func(line string)) {
	errorHook = hook
}

func SetLevel(level LogLevel) {
	globalLevel = level
}

// SetFileOutput directs a plain (non-ANSI) copy of every log line to w,
// on top of the colored stdout output. Pass nil to stop file logging.
func SetFileOutput(w io.Writer) {
	fileOutput = w
}

func writeLog(level LogLevel, colorCode, prefix, format string, args ...any) {
	if globalLevel > level {
		return
	}

	loc, _ := time.LoadLocation("Asia/Kolkata")
	timestamp := time.Now().In(loc).Format("2006-01-02 15:04:05.000")

	// Evaluate dynamic formatting options provided by the caller
	message := fmt.Sprintf(format, args...)

	// Construct the complete log line with distinct foreground token colors
	// Structure: [TIMESTAMP] [LEVEL] MESSAGE

	fmt.Fprintf(os.Stdout, "%s%s%s %s[%s]%s %s\n",
		colorTime, timestamp, colorReset,
		colorCode, prefix, colorReset,
		message,
	)

	if fileOutput != nil {
		fileMu.Lock()
		fmt.Fprintf(fileOutput, "%s [%s] %s\n", timestamp, prefix, message)
		fileMu.Unlock()
	}

	if errorHook != nil && level >= LevelError {
		errorHook(fmt.Sprintf("%s [%s] %s", timestamp, prefix, message))
	}

	// Intercept execution pathways if a panic-level operation is requested
	if level == LevelFatal {
		os.Exit(1)
	}

}

func Debug(format string, args ...any) {
	writeLog(LevelDebug, colorDebug, "DEBUG", format, args...)
}

// Info prints general informational notices with solid Green text decoration.
func Info(format string, args ...any) {
	writeLog(LevelInfo, colorInfo, "INFO ", format, args...)
}

func Log(format string, args ...any) {
	writeLog(LevelLog, colorLog, "LOGG", format, args...)
}

func LogTerminal(format string, args ...any) {
	writeLog(LevelLogTerminal, colorLogTerminal, "TERMINAL", format, args...)
}

// Warn prints transient anomalies or configuration failures using Yellow text decoration.
func Warn(format string, args ...any) {
	writeLog(LevelWarn, colorWarn, "WARN ", format, args...)
}

// Error prints structural program defects or exceptions using bright Red text decoration.
func Error(format string, args ...any) {
	writeLog(LevelError, colorError, "ERROR", format, args...)
}

// Fatal logs a catastrophic exception using Magenta decoration and forces a program exit.
func Fatal(format string, args ...any) {
	writeLog(LevelFatal, colorFatal, "FATAL", format, args...)
	os.Exit(1)
}

func UniqueLog(format string, args ...any) {
	writeLog(LevelUnique, colorPink, "UNIQUE", format, args...)
}
