package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"strings"
	"time"

	"github.com/getsentry/sentry-go"
)

func main() {
	// Try multiple environment variables for DSN
	dsn := getEnvDSN()
	if dsn == "" {
		fmt.Fprintf(os.Stderr, "Error: No DSN environment variable found.\n")
		fmt.Fprintf(os.Stderr, "Please set one of the following environment variables:\n")
		fmt.Fprintf(os.Stderr, "  - GLITCHTIP_DSN\n")
		fmt.Fprintf(os.Stderr, "  - SENTRY_DSN\n")
		fmt.Fprintf(os.Stderr, "  - BUGSINK_DSN\n")
		os.Exit(1)
	}

	// Initialize with the DSN found
	err := sentry.Init(sentry.ClientOptions{
		Dsn: dsn,
	})
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error initializing client: %s\n", err)
		os.Exit(1)
	}

	if len(os.Args) > 1 && os.Args[1] == "server" {
		startServer()
	} else {
		processLogs()
	}

	defer func() {
		success := sentry.Flush(10 * time.Second)
		if !success {
			fmt.Fprintf(os.Stderr, "Failed to flush buffer within the expected time.\n")
		} else {
			fmt.Println("GlitchTip buffer flushed successfully.")
		}
	}()
}

// getEnvDSN checks multiple environment variables for a DSN
func getEnvDSN() string {
	// Try different environment variables in order of preference
	envVars := []string{
		"GLITCHTIP_DSN",
		"SENTRY_DSN",
		"BUGSINK_DSN",
	}

	for _, envVar := range envVars {
		if dsn := os.Getenv(envVar); dsn != "" {
			fmt.Printf("Using DSN from %s environment variable\n", envVar)
			return dsn
		}
	}

	return ""
}

func processLogs() {
	var reader io.Reader
	var filePath string

	if len(os.Args) == 2 {
		filePath = os.Args[1]
		file, err := os.Open(filePath)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error opening file: %s\n", err)
			os.Exit(1)
		}
		defer file.Close()
		reader = file
		fmt.Println("Reading from file:", filePath)
	} else if isInputFromPipe() {
		reader = os.Stdin
		fmt.Println("Reading from stdin")
	} else {
		fmt.Fprintf(os.Stderr, "Usage: %s <path to log file> OR pipe input\n", os.Args[0])
		os.Exit(1)
	}

	scanner := bufio.NewScanner(reader)
	var content strings.Builder
	for scanner.Scan() {
		line := scanner.Text()
		content.WriteString(line)
		content.WriteString("\n")
		fmt.Println("Read line:", line)
	}

	if err := scanner.Err(); err != nil {
		fmt.Fprintf(os.Stderr, "Error reading input: %s\n", err)
		os.Exit(1)
	}

	logMessage := content.String()
	fmt.Println("Final log message:", logMessage)
	sendToSentry(logMessage)
}

func isInputFromPipe() bool {
	fileInfo, err := os.Stdin.Stat()
	if err != nil {
		return false
	}
	return fileInfo.Mode()&os.ModeCharDevice == 0
}

func sendToSentry(logMessage string) {
	fmt.Println("Sending log message to Sentry:", logMessage)

	// Try to parse as JSON first
	var parsedLog map[string]interface{}
	err := json.Unmarshal([]byte(logMessage), &parsedLog)

	if err == nil {
		// Successfully parsed JSON

		// Check for common log fields
		level := "info"
		message := logMessage
		tags := make(map[string]string)

		if lvl, ok := parsedLog["level"].(string); ok {
			level = lvl
		} else if lvl, ok := parsedLog["log_level"].(string); ok {
			level = lvl
		}

		if msg, ok := parsedLog["message"].(string); ok {
			message = msg
		} else if msg, ok := parsedLog["msg"].(string); ok {
			message = msg
		}

		// Add any additional fields as tags
		for k, v := range parsedLog {
			if k != "level" && k != "log_level" && k != "message" && k != "msg" {
				if strVal, ok := v.(string); ok {
					tags[k] = strVal
				} else if jsonVal, err := json.Marshal(v); err == nil {
					tags[k] = string(jsonVal)
				}
			}
		}

		// Create a more structured event based on the level
		var eventID *sentry.EventID

		sentry.WithScope(func(scope *sentry.Scope) {
			scope.SetLevel(getSentryLevel(level))

			// Add all the extracted fields as tags
			for k, v := range tags {
				scope.SetTag(k, v)
			}

			// Capture based on level
			if getSentryLevel(level) >= sentry.LevelError {
				eventID = sentry.CaptureException(fmt.Errorf(message))
			} else {
				eventID = sentry.CaptureMessage(message)
			}
		})

		if eventID != nil {
			fmt.Printf("Sent message to GlitchTip with event ID: %s\n", *eventID)
		} else {
			fmt.Fprintf(os.Stderr, "Failed to send message to GlitchTip.\n")
		}
	} else {
		// Not a valid JSON, send as a simple message
		eventID := sentry.CaptureMessage(logMessage)
		if eventID != nil {
			fmt.Printf("Sent message to GlitchTip with event ID: %s\n", *eventID)
		} else {
			fmt.Fprintf(os.Stderr, "Failed to send message to GlitchTip.\n")
		}
	}
}

// Helper function to convert string log levels to Sentry levels
func getSentryLevel(level string) sentry.Level {
	switch strings.ToLower(level) {
	case "fatal", "panic":
		return sentry.LevelFatal
	case "error", "err":
		return sentry.LevelError
	case "warning", "warn":
		return sentry.LevelWarning
	case "info":
		return sentry.LevelInfo
	case "debug":
		return sentry.LevelDebug
	default:
		return sentry.LevelInfo
	}
}

func startServer() {
	http.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
		bodyBytes, err := io.ReadAll(r.Body)
		if err != nil {
			http.Error(w, "Error reading request body", http.StatusInternalServerError)
			fmt.Fprintf(os.Stderr, "Error reading request body: %s\n", err)
			return
		}

		fmt.Printf("Received request body: %s\n", bodyBytes) // Log the full request body

		var logMessage interface{}
		err = json.Unmarshal(bodyBytes, &logMessage)
		if err != nil {
			http.Error(w, "Invalid JSON", http.StatusBadRequest)
			fmt.Fprintf(os.Stderr, "Invalid JSON: %s\nReceived: %s\n", err, string(bodyBytes))
			return
		}

		switch v := logMessage.(type) {
		case map[string]interface{}:
			logBytes, err := json.Marshal(v)
			if err != nil {
				http.Error(w, "Error processing log message", http.StatusInternalServerError)
				fmt.Fprintf(os.Stderr, "Error processing log message: %s\n", err)
				return
			}
			fmt.Printf("Processed log: %s\n", logBytes)
			sendToSentry(string(logBytes))
		case []interface{}:
			for _, item := range v {
				if logItem, ok := item.(map[string]interface{}); ok {
					logBytes, err := json.Marshal(logItem)
					if err != nil {
						http.Error(w, "Error processing log message", http.StatusInternalServerError)
						fmt.Fprintf(os.Stderr, "Error processing log message: %s\n", err)
						return
					}
					fmt.Printf("Processed log item: %s\n", logBytes)
					sendToSentry(string(logBytes))
				} else {
					http.Error(w, "Invalid JSON item in array", http.StatusBadRequest)
					fmt.Fprintf(os.Stderr, "Invalid JSON item in array: %s\n", item)
					return
				}
			}
		default:
			http.Error(w, "Invalid JSON format", http.StatusBadRequest)
			fmt.Fprintf(os.Stderr, "Invalid JSON format: %s\n", v)
			return
		}
		w.WriteHeader(http.StatusNoContent)
	})

	fmt.Println("Starting server on 0.0.0.0:5050")
	err := http.ListenAndServe("0.0.0.0:5050", nil)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error starting server: %s\n", err)
		os.Exit(1)
	}
}
