package app

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

	"github.com/gocql/gocql"
	"go.uber.org/zap"

	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/config"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/interface/http"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/interface/scheduler"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/internal/interface/scheduler/tasks"
	"codeberg.org/mapleopentech/monorepo/cloud/maplefile-backend/pkg/storage/database/cassandradb"
)

// Application represents the main application using Wire DI
type Application struct {
	config              *config.Config
	httpServer          *http.WireServer
	logger              *zap.Logger
	migrator            *cassandradb.Migrator
	scheduler           *scheduler.Scheduler
	ipAnonymizationTask *tasks.IPAnonymizationTask
	dbSession           *gocql.Session
}

// ProvideApplication creates the application instance for Wire
func ProvideApplication(
	cfg *config.Config,
	httpServer *http.WireServer,
	logger *zap.Logger,
	migrator *cassandradb.Migrator,
	sched *scheduler.Scheduler,
	ipAnonymizationTask *tasks.IPAnonymizationTask,
	dbSession *gocql.Session,
) *Application {
	return &Application{
		config:              cfg,
		httpServer:          httpServer,
		logger:              logger,
		migrator:            migrator,
		scheduler:           sched,
		ipAnonymizationTask: ipAnonymizationTask,
		dbSession:           dbSession,
	}
}

// Start starts the application
func (app *Application) Start() error {
	app.logger.Info("🚀 MapleFile Backend Starting (Wire DI)",
		zap.String("version", app.config.App.Version),
		zap.String("environment", app.config.App.Environment),
		zap.String("di_framework", "Google Wire"))

	// Run database migrations automatically on startup if enabled
	if app.config.Database.AutoMigrate {
		app.logger.Info("Auto-migration enabled, running database migrations...")
		if err := app.migrator.Up(); err != nil {
			app.logger.Error("Failed to run database migrations", zap.Error(err))
			return fmt.Errorf("migration failed: %w", err)
		}
		app.logger.Info("✅ Database migrations completed successfully")

		// Wait for schema agreement across all Cassandra nodes
		// This ensures all nodes have the new schema before we start accepting requests
		app.logger.Info("⏳ Waiting for Cassandra schema agreement...")
		ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
		defer cancel()
		if err := app.dbSession.AwaitSchemaAgreement(ctx); err != nil {
			app.logger.Warn("Schema agreement wait failed, continuing anyway",
				zap.Error(err),
				zap.String("note", "This may cause transient errors on first requests"))
		} else {
			app.logger.Info("✅ Cassandra schema agreement reached")
		}
	} else {
		app.logger.Info("Auto-migration disabled (DATABASE_AUTO_MIGRATE=false), skipping migrations")
	}

	// Register scheduled tasks
	app.logger.Info("Registering scheduled tasks...")
	if err := app.scheduler.RegisterTask(app.ipAnonymizationTask); err != nil {
		app.logger.Error("Failed to register IP anonymization task", zap.Error(err))
		return fmt.Errorf("task registration failed: %w", err)
	}

	// Start scheduler
	if err := app.scheduler.Start(); err != nil {
		app.logger.Error("Failed to start scheduler", zap.Error(err))
		return fmt.Errorf("scheduler startup failed: %w", err)
	}

	// Start HTTP server in goroutine
	errChan := make(chan error, 1)
	go func() {
		if err := app.httpServer.Start(); err != nil {
			errChan <- err
		}
	}()

	// Wait for interrupt signal or server error
	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	select {
	case err := <-errChan:
		app.logger.Error("HTTP server failed", zap.Error(err))
		return fmt.Errorf("server startup failed: %w", err)
	case sig := <-quit:
		app.logger.Info("Received shutdown signal", zap.String("signal", sig.String()))
	}

	app.logger.Info("👋 MapleFile Backend Shutting Down")

	// Graceful shutdown with timeout
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	// Stop scheduler first
	app.logger.Info("Stopping scheduler...")
	if err := app.scheduler.Stop(); err != nil {
		app.logger.Error("Scheduler shutdown error", zap.Error(err))
		// Continue with shutdown even if scheduler fails
	}

	// Stop HTTP server
	if err := app.httpServer.Shutdown(ctx); err != nil {
		app.logger.Error("Server shutdown error", zap.Error(err))
		return fmt.Errorf("server shutdown failed: %w", err)
	}

	app.logger.Info("✅ MapleFile Backend Stopped Successfully")
	return nil
}
