package main

import (
	"context"
	"os"
	"os/signal"
	"time"

	"github.com/charmingruby/homunc/apps/fraud/config"
	"github.com/charmingruby/homunc/apps/fraud/internal/fraud"
	"github.com/charmingruby/homunc/apps/fraud/pkg/database/postgres"
	"github.com/charmingruby/homunc/apps/fraud/pkg/messaging/rabbitmq"
	"github.com/charmingruby/homunc/apps/fraud/pkg/telemetry"
	"github.com/joho/godotenv"
)

func main() {
	if err := godotenv.Load(); err != nil {
		telemetry.Log.Warn(".env file not found")
	}

	telemetry.Log.Info("loading environment...")

	cfg, err := config.New()
	if err != nil {
		exitWithShutdown(
			nil,
			nil,
			nil,
			"failed to load environment variables",
			err,
		)
	}

	telemetry.Log.Info("environment loaded")

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	telemetryShutdown, err := telemetry.New(ctx, *cfg)
	if err != nil {
		exitWithShutdown(
			nil,
			nil,
			nil,
			"failed to start OTEL Collector",
			err,
		)
	}

	telemetry.Log.Info("connecting to RabbitMQ...")

	queue, err := rabbitmq.New(telemetry.Log, &rabbitmq.Config{
		URL:       cfg.RabbitMQURL,
		QueueName: cfg.RabbitMQQueueName,
	})
	if err != nil {
		exitWithShutdown(
			telemetryShutdown,
			nil,
			nil,
			"failed to connect to RabbitMQ",
			err,
		)
	}

	telemetry.Log.Info("connected to RabbitMQ")

	telemetry.Log.Info("connecting to Postgres...")

	db, err := postgres.New(cfg.PostgresURL)
	if err != nil {
		exitWithShutdown(
			telemetryShutdown,
			queue,
			nil,
			"failed to connect to Postgres",
			err,
		)
	}

	telemetry.Log.Info("connected to Postgres")

	telemetry.Log.Info("setting up fraud module")

	if err := fraud.New(fraud.Input{
		Queue: queue,
		DB:    db,
	}); err != nil {
		exitWithShutdown(
			telemetryShutdown,
			queue,
			db,
			"error setting up fraud module",
			err,
		)
	}

	telemetry.Log.Info("all fraud module setted")

	telemetry.Log.Info("application is running healthy")

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, os.Interrupt)
	<-quit

	telemetry.Log.Info("received interrupt signal")

	gracefulShutdown(telemetryShutdown, db, queue)

	telemetry.Log.Info("gracefully shutdown")
}

func gracefulShutdown(
	telemetryShutdown func(ctx context.Context) error,
	database *postgres.Client,
	queue *rabbitmq.Client,
) error {
	if telemetryShutdown != nil {
		telemetryCtx, telemetryCancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer telemetryCancel()
		if err := telemetryShutdown(telemetryCtx); err != nil {
			telemetry.Log.Error("failed to shutdown OTEL", "error", err)
		} else {
			telemetry.UseFallbackLogger()
		}
	}

	if database != nil {
		databaseCtx, cancelDatabaseCtx := context.WithTimeout(context.Background(), time.Second*10)
		defer cancelDatabaseCtx()
		if err := database.Close(databaseCtx); err != nil {
			telemetry.Log.Error("failed to shutdown Postgres", "error", err)
		}
	}

	if queue != nil {
		queue.Close()
	}

	return nil
}

func exitWithShutdown(
	telemetryShutdown func(ctx context.Context) error,
	queue *rabbitmq.Client,
	db *postgres.Client,
	msg string,
	err error,
) {
	telemetry.Log.Error(msg, "error", err)
	gracefulShutdown(telemetryShutdown, db, queue)
	os.Exit(1)
}
