package app

import (
	"context"
	"net/http"
	"time"

	"go.uber.org/zap"
	"gorm.io/gorm"

	"api_bb/internal/config"
	"api_bb/internal/database"
	"api_bb/internal/routes"
	"api_bb/pkg/logger"
)

type App struct {
	cfg    *config.Config
	db     *database.Database
	server *http.Server
}

func NewApp(cfg *config.Config) *App {
	return &App{
		cfg: cfg,
	}
}

// Initialize инициализирует приложение (БД, миграции, роутинг)
func (a *App) Initialize() error {
	zapLogger := logger.Get()

	// Инициализация базы данных
	dbConfig := &database.Config{
		URL: a.cfg.DatabaseURL,
	}
	a.db = database.NewDatabase(dbConfig)

	// Подключение к БД
	if err := a.db.Connect(); err != nil {
		return err
	}

	// Проверка соединения
	if err := a.db.Ping(); err != nil {
		return err
	}

	// Выполнение миграций
	if err := a.db.Migrate(); err != nil {
		return err
	}

	// Настройка роутера
	router := routes.SetupRouter(a.db.DB, a.cfg)

	// Настройка HTTP сервера
	a.server = &http.Server{
		Addr:    ":" + a.cfg.Port,
		Handler: router,
	}

	zapLogger.Info("application initialized successfully")
	return nil
}

// Start запускает HTTP сервер
func (a *App) Start() error {
	zapLogger := logger.Get()

	zapLogger.Info("starting HTTP server", zap.String("port", a.cfg.Port))
	
	if err := a.server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
		return err
	}

	return nil
}

// Shutdown gracefully останавливает приложение
func (a *App) Shutdown() error {
	zapLogger := logger.Get()

	zapLogger.Info("shutdown signal received")

	// Graceful shutdown сервера
	ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
	defer cancel()

	a.server.SetKeepAlivesEnabled(false)
	if err := a.server.Shutdown(ctx); err != nil {
		zapLogger.Error("could not gracefully shutdown the server", zap.Error(err))
		return err
	}

	// Закрытие соединения с БД
	if err := a.db.Close(); err != nil {
		return err
	}

	zapLogger.Info("application shutdown completed")
	return nil
}

// GetDB возвращает экземпляр базы данных
func (a *App) GetDB() *gorm.DB {
	return a.db.DB
}