package log_watcher

import (
	"bufio"
	"encoding/json"
	"log"
	"os"
	"path/filepath"

	"github.com/Vomitblood/cspj-application/server/internal/config"
	"github.com/Vomitblood/cspj-application/server/internal/telegram"
	"github.com/fsnotify/fsnotify"
	tg "github.com/go-telegram-bot-api/telegram-bot-api/v5"
)

var lastReadPosition int64 = 0

type LogEntry struct {
	AuditData struct {
		Messages []string `json:"messages"`
	} `json:"audit_data"`
}

func WatchFile(bot *tg.BotAPI) {
	modsecLogFile := filepath.Join(config.LogDirectory, "host-fs-auditlog.log")
	log.Println("Watching logfile directory:", config.LogDirectory)
	watcher, err := fsnotify.NewWatcher()
	if err != nil {
		log.Fatal("Failed to initialize watcher: ", err)
	}
	defer watcher.Close()

	// add log file to watcher
	err = watcher.Add(modsecLogFile)
	if err != nil {
		log.Fatal("Failed to watch log file: ", err)
	}

	log.Println("Monitoring log file for changes...")

	for {
		select {
		case event, ok := <-watcher.Events:
			if !ok {
				return
			}
			if event.Op&fsnotify.Write == fsnotify.Write {
				log.Println("Log file updated, reading new entries...")
				readNewLines(bot)
			}

		case err, ok := <-watcher.Errors:
			if !ok {
				return
			}
			log.Println("Watcher error: ", err)
		}
	}
}

func readNewLines(bot *tg.BotAPI) {
	modsecLogFile := filepath.Join(config.LogDirectory, "host-fs-auditlog.log")
	file, err := os.Open(modsecLogFile)
	if err != nil {
		log.Println("Failed to reopen log file: ", err)
		return
	}
	defer file.Close()

	// move to the last read position
	file.Seek(lastReadPosition, os.SEEK_SET)
	scanner := bufio.NewScanner(file)

	for scanner.Scan() {
		line := scanner.Text()
		var logEntry LogEntry

		// try to parse json
		if err := json.Unmarshal([]byte(line), &logEntry); err != nil {
			log.Println("⚠️ Failed to parse JSON: ", err)
			// skip invalid json lines
			// very crucial as modsecurity does not respect the json spec
			continue
		}

		// send index 0 element will do
		if len(logEntry.AuditData.Messages) > 0 {
			telegram.SendTelegramAlert(bot, logEntry.AuditData.Messages[0])
		}
	}

	// update last read position
	lastReadPosition, _ = file.Seek(0, os.SEEK_CUR)

	if err := scanner.Err(); err != nil {
		log.Println("Error reading log file: ", err)
	}
}
