package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/igodwin/notifier/api/rest"
	"github.com/igodwin/notifier/internal/config"
	"github.com/igodwin/notifier/internal/domain"
	"github.com/igodwin/notifier/internal/logging"
	"github.com/igodwin/notifier/internal/notifier"
	"github.com/igodwin/notifier/internal/queue"
	"github.com/igodwin/notifier/internal/service"
	"google.golang.org/grpc"
	"google.golang.org/grpc/reflection"
)

func main() {
	// Load configuration
	cfg, err := config.Load("")
	if err != nil {
		// Use basic logger before we have config
		logger, _ := logging.NewFromConfig("info", "stdout")
		logger.Warnf("Failed to load config, using defaults: %v", err)
		cfg = getDefaultConfig()
	}

	// Create logger from config
	logger, err := logging.NewFromConfig(cfg.Logging.Level, cfg.Logging.OutputPath)
	if err != nil {
		// Fallback to stdout if log file can't be opened
		logger, _ = logging.NewFromConfig(cfg.Logging.Level, "stdout")
		logger.Warnf("Failed to open log file, using stdout: %v", err)
	}

	logger.Infof("Starting Notifier Service in mode: %s", cfg.Server.Mode)

	// Create context
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Initialize queue
	var q domain.Queue
	if cfg.Queue.Type == "local" {
		q, err = queue.NewLocalQueue(cfg.Queue.Local)
		if err != nil {
			logger.Fatalf("Failed to create queue: %v", err)
		}
		logger.Info("Using local queue")
	} else {
		logger.Fatalf("Queue type %s not implemented yet", cfg.Queue.Type)
	}

	// Initialize notifier factory and register notifiers
	factory := notifier.NewFactory()
	registerNotifiers(cfg, factory, logger)

	// Check if any notifiers are registered
	if len(factory.SupportedTypes()) == 0 {
		logger.Fatal("No notifiers configured. Please enable at least one notifier in config.yaml")
	}

	logger.Infof("Supported notification types: %v", factory.SupportedTypes())

	// Create notification service
	svc := service.NewNotificationService(factory, q, cfg.Queue.WorkerCount)

	// Start workers
	if err := svc.Start(ctx); err != nil {
		logger.Fatalf("Failed to start service: %v", err)
	}
	logger.Infof("Started %d worker(s)", cfg.Queue.WorkerCount)

	// Wait group for both servers
	var wg sync.WaitGroup

	// Start gRPC server if enabled
	var grpcServer *grpc.Server
	if cfg.Server.Mode == "both" || cfg.Server.Mode == "grpc" {
		wg.Add(1)
		grpcServer = startGRPCServer(ctx, &wg, cfg, svc, logger)
	}

	// Start REST server if enabled
	var restServer *http.Server
	if cfg.Server.Mode == "both" || cfg.Server.Mode == "rest" {
		wg.Add(1)
		restServer = startRESTServer(ctx, &wg, cfg, svc, logger)
	}

	// Wait for interrupt signal
	sigChan := make(chan os.Signal, 1)
	signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM)
	<-sigChan

	logger.Info("Shutting down servers...")

	// Graceful shutdown
	shutdownCtx, shutdownCancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer shutdownCancel()

	// Stop REST server
	if restServer != nil {
		if err := restServer.Shutdown(shutdownCtx); err != nil {
			logger.Errorf("Error during REST server shutdown: %v", err)
		}
	}

	// Stop gRPC server
	if grpcServer != nil {
		grpcServer.GracefulStop()
	}

	// Wait for servers to stop
	wg.Wait()

	// Stop service
	if err := svc.Stop(); err != nil {
		logger.Errorf("Error stopping service: %v", err)
	}

	logger.Info("Servers stopped")
}

func registerNotifiers(cfg *config.Config, factory *notifier.Factory, logger *logging.Logger) {
	if cfg.Notifiers.Stdout {
		stdoutNotifier := notifier.NewStdoutNotifier()
		if err := factory.RegisterNotifier(domain.TypeStdout, stdoutNotifier); err != nil {
			logger.Fatalf("Failed to register stdout notifier: %v", err)
		}
		logger.Info("Registered stdout notifier")
	}

	if cfg.Notifiers.SMTP != nil {
		smtpNotifier, err := notifier.NewSMTPNotifier(cfg.Notifiers.SMTP)
		if err != nil {
			logger.Warnf("Failed to create SMTP notifier: %v", err)
		} else {
			if err := factory.RegisterNotifier(domain.TypeEmail, smtpNotifier); err != nil {
				logger.Fatalf("Failed to register SMTP notifier: %v", err)
			}
			logger.Info("Registered SMTP notifier")
		}
	}

	if cfg.Notifiers.Slack != nil {
		slackNotifier, err := notifier.NewSlackNotifier(cfg.Notifiers.Slack)
		if err != nil {
			logger.Warnf("Failed to create Slack notifier: %v", err)
		} else {
			if err := factory.RegisterNotifier(domain.TypeSlack, slackNotifier); err != nil {
				logger.Fatalf("Failed to register Slack notifier: %v", err)
			}
			logger.Info("Registered Slack notifier")
		}
	}

	if cfg.Notifiers.Ntfy != nil {
		ntfyNotifier, err := notifier.NewNtfyNotifier(cfg.Notifiers.Ntfy)
		if err != nil {
			logger.Warnf("Failed to create Ntfy notifier: %v", err)
		} else {
			if err := factory.RegisterNotifier(domain.TypeNtfy, ntfyNotifier); err != nil {
				logger.Fatalf("Failed to register Ntfy notifier: %v", err)
			}
			logger.Info("Registered Ntfy notifier")
		}
	}
}

func startGRPCServer(ctx context.Context, wg *sync.WaitGroup, cfg *config.Config, svc domain.NotificationService, logger *logging.Logger) *grpc.Server {
	addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.GRPCPort)

	lis, err := net.Listen("tcp", addr)
	if err != nil {
		logger.Fatalf("Failed to listen on %s: %v", addr, err)
	}

	grpcServer := grpc.NewServer()

	// TODO: Register gRPC service implementation when protobuf is generated
	// pb.RegisterNotifierServiceServer(grpcServer, grpcHandler)

	// Enable reflection for tools like grpcurl
	reflection.Register(grpcServer)

	go func() {
		defer wg.Done()
		logger.Infof("gRPC server listening on %s", addr)
		if err := grpcServer.Serve(lis); err != nil {
			logger.Fatalf("Failed to serve gRPC: %v", err)
		}
	}()

	return grpcServer
}

func startRESTServer(ctx context.Context, wg *sync.WaitGroup, cfg *config.Config, svc domain.NotificationService, logger *logging.Logger) *http.Server {
	router := rest.NewRouter(svc)

	addr := fmt.Sprintf("%s:%d", cfg.Server.Host, cfg.Server.RESTPort)
	server := &http.Server{
		Addr:         addr,
		Handler:      router,
		ReadTimeout:  15 * time.Second,
		WriteTimeout: 15 * time.Second,
		IdleTimeout:  60 * time.Second,
	}

	go func() {
		defer wg.Done()
		logger.Infof("REST server listening on %s", addr)
		if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			logger.Fatalf("Failed to start REST server: %v", err)
		}
	}()

	return server
}

func getDefaultConfig() *config.Config {
	return &config.Config{
		Server: config.ServerConfig{
			GRPCPort: 50051,
			RESTPort: 8080,
			Host:     "0.0.0.0",
			Mode:     "both",
		},
		Queue: domain.QueueConfig{
			Type:          "local",
			WorkerCount:   5,
			RetryAttempts: 3,
			Local: &domain.LocalQueueConfig{
				BufferSize: 1000,
			},
		},
		Notifiers: config.NotifiersConfig{
			Stdout: true,
		},
	}
}
