package logging

import (
	"fmt"
	"log"
	"os"
	"runtime"
	"time"
)

var (
	file       *os.File
	history    []string
	logger     = log.New(os.Stderr, "[videotools] ", log.LstdFlags|log.Lmicroseconds)
	filePath   string
	historyMax = 500
	debugOn    = false
)

const (
	CatUI      Category = "[UI]"
	CatCLI     Category = "[CLI]"
	CatFFMPEG  Category = "[FFMPEG]"
	CatSystem  Category = "[SYS]"
	CatModule  Category = "[MODULE]"
	CatPlayer  Category = "[PLAYER]"
	CatEnhance Category = "[ENHANCE]"
)

// Categories represents a log category
type Category string

// Init initializes logging system with organized log folders
func Init() {
	// Create logs directory if it doesn't exist
	logsDir := "logs"
	if err := os.MkdirAll(logsDir, 0755); err != nil {
		fmt.Fprintf(os.Stderr, "videotools: cannot create logs directory: %v\n", err)
		return
	}

	// Use environment variable or default
	filePath = os.Getenv("VIDEOTOOLS_LOG_FILE")
	if filePath == "" {
		filePath = "logs/videotools.log"
	}
	f, err := os.OpenFile(filePath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
	if err != nil {
		fmt.Fprintf(os.Stderr, "videotools: cannot open log file %s: %v\n", filePath, err)
		return
	}
	file = f
}

// GetCrashLogPath returns path for crash-specific log file
func GetCrashLogPath() string {
	return "logs/crashes.log"
}

// GetConversionLogPath returns path for conversion-specific log file
func GetConversionLogPath() string {
	return "logs/conversion.log"
}

// GetPlayerLogPath returns path for player-specific log file
func GetPlayerLogPath() string {
	return "logs/player.log"
}

// getStackTrace returns current goroutine stack trace
func getStackTrace() string {
	buf := make([]byte, 4096)
	n := runtime.Stack(buf, false)
	return string(buf[:n])
}

// RecoverPanic logs a recovered panic with a stack trace.
// Intended for use in deferred calls inside goroutines.
func RecoverPanic() {
	if r := recover(); r != nil {
		Crash(CatSystem, "Recovered panic: %v", r)
	}
}

// Error logs an error message with a category (always logged, even when debug is off)
func Error(cat Category, format string, args ...interface{}) {
	msg := fmt.Sprintf("%s ERROR: %s", cat, fmt.Sprintf(format, args...))
	timestamp := time.Now().Format(time.RFC3339Nano)
	if file != nil {
		fmt.Fprintf(file, "%s %s\n", timestamp, msg)
	}
	history = append(history, fmt.Sprintf("%s %s", timestamp, msg))
	if len(history) > historyMax {
		history = history[len(history)-historyMax:]
	}
	logger.Printf("%s %s", timestamp, msg)
}

// Debug logs a debug message with a category
func Debug(cat Category, format string, args ...interface{}) {
	if !debugOn {
		return
	}
	msg := fmt.Sprintf("%s %s", cat, fmt.Sprintf(format, args...))
	timestamp := time.Now().Format(time.RFC3339Nano)
	if file != nil {
		fmt.Fprintf(file, "%s %s\n", timestamp, msg)
	}
	logger.Printf("%s %s", timestamp, msg)
}

// Info logs an informational message
func Info(cat Category, format string, args ...interface{}) {
	msg := fmt.Sprintf("%s INFO: %s", cat, fmt.Sprintf(format, args...))
	timestamp := time.Now().Format(time.RFC3339Nano)
	if file != nil {
		fmt.Fprintf(file, "%s %s\n", timestamp, msg)
	}
	logger.Printf("%s %s", timestamp, msg)
}

// Crash logs a critical error with stack trace for debugging crashes
func Crash(cat Category, format string, args ...interface{}) {
	msg := fmt.Sprintf("%s CRASH: %s", cat, fmt.Sprintf(format, args...))
	timestamp := time.Now().Format(time.RFC3339Nano)

	// Log to main log file
	if file != nil {
		fmt.Fprintf(file, "%s %s\n", timestamp, msg)
	}
	logger.Printf("%s %s", timestamp, msg)

	// Also log to dedicated crash log
	if crashFile, err := os.OpenFile(GetCrashLogPath(), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644); err == nil {
		fmt.Fprintf(crashFile, "%s %s\n", timestamp, msg)
		fmt.Fprintf(crashFile, "Stack trace:\n%s\n", timestamp, getStackTrace())
		crashFile.Sync()
	}
}

// Fatal logs a fatal error and exits (always logged, even when debug is off)
func Fatal(cat Category, format string, args ...interface{}) {
	msg := fmt.Sprintf("%s FATAL: %s", cat, fmt.Sprintf(format, args...))
	timestamp := time.Now().Format(time.RFC3339Nano)
	if file != nil {
		fmt.Fprintf(file, "%s %s\n", timestamp, msg)
		file.Sync()
	}
	logger.Printf("%s %s", timestamp, msg)
	os.Exit(1)
}

// Close closes log file
func Close() {
	if file != nil {
		file.Close()
	}
}

// SetDebug enables or disables debug logging.
func SetDebug(enabled bool) {
	debugOn = enabled
}

// FilePath returns the active log file path, if initialized.
func FilePath() string {
	return filePath
}
