package server

import (
	"database/sql"
	"fmt"
	"net/http"
	"os"

	"github.com/a-h/templ/examples/integration-gin/gintemplrenderer"
	"github.com/gin-contrib/cors"
	"github.com/gin-gonic/gin"
	"github.com/haydenhargreaves/Potion/internal/app/handlers"
	"github.com/haydenhargreaves/Potion/internal/app/service"
	domain "github.com/haydenhargreaves/Potion/internal/domain/server"
	"github.com/haydenhargreaves/Potion/internal/infrastructure/auth"
	"github.com/haydenhargreaves/Potion/internal/infrastructure/database/repository"
	"github.com/joho/godotenv"

	_ "github.com/lib/pq"
)

type Server struct {
	port   int
	Router *gin.Engine
	config cors.Config
	DB     *sql.DB
}

// Init initializes the server with the provided port. CORS settings are defined here.
// A pointer to a server object is returned which allows for method chaining.
func Init(port int) *Server {
	// TODO: Set this to release in prod
	gin.SetMode(gin.DebugMode)

	server := &Server{
		Router: gin.Default(),
		port:   port,
		config: cors.DefaultConfig(),
	}

	// Some stuff for templ rendering
	htmlRenderer := server.Router.HTMLRender
	server.Router.HTMLRender = &gintemplrenderer.HTMLTemplRenderer{FallbackHtmlRenderer: htmlRenderer}

	// Disable proxy warnings
	server.Router.SetTrustedProxies(nil)

	// Setup the CORS settings and active them
	server.config.AllowAllOrigins = true
	server.Router.Use(cors.New(server.config))

	return server
}

func (s *Server) ConfigureAuth() *Server {
	err := godotenv.Load(".env")
	if err != nil {
		panic("Could not load env file")
	}

	redirect_domain := os.Getenv("DOMAIN")

	var (
		redirectUrl  string   = fmt.Sprintf("%s%s", redirect_domain, domain.API_AUTH_CALLBACK)
		clientId     string   = os.Getenv("GOOGLE_CLIENT_ID")
		clientSecret string   = os.Getenv("GOOGLE_CLIENT_SECRET")
		scope        []string = []string{
			"https://www.googleapis.com/auth/userinfo.email",
			"https://www.googleapis.com/auth/userinfo.profile",
		}
	)

	// Setup Google OAuth
	auth.NewGoogleConfig(redirectUrl, clientId, clientSecret, scope)

	return s
}

func (s *Server) ConnectDatabase() *Server {
	err := godotenv.Load(".env")
	if err != nil {
		panic("Could not load env file")
	}

	var connUrl string = os.Getenv("DATABASE_URL")

	db, err := sql.Open("postgres", connUrl)
	if err != nil {
		panic("Could not connect to database: " + err.Error())
	}

	if err = db.Ping(); err != nil {
		panic("Error pinging database: " + err.Error())
	}

	s.DB = db

	return s
}

// Start starts the server on the port provided when the server was initialized
func (s *Server) Start() {
	s.Router.Run(fmt.Sprintf(":%d", s.port))
}

func (s *Server) Setup() *Server {
	err := godotenv.Load(".env")
	if err != nil {
		panic("Could not load env file")
	}

	jwtSecret := []byte(os.Getenv("JWT_SECRET"))

	// Initialize and inject dependencies
	userRepo := repository.NewUserRepository(s.DB)
	recipeRepo := repository.NewRecipeRepository(s.DB)
	userService := service.NewUserService(userRepo)
	authService := service.NewAuthService(userRepo, jwtSecret)
	recipeService := service.NewRecipeService(recipeRepo)

	deps := &domain.InjectedDependencies{
		UserService:   userService,
		AuthService:   authService,
		RecipeService: recipeService,
	}

	// Apply middleware
	s.Router.Use(DepedencyInjectionMiddleware(deps))
	s.Router.Use(JwtAuthMiddleWare(jwtSecret))

	// Redirect index to home page: Update this as needed
	s.Router.GET("/", func(ctx *gin.Context) { ctx.Redirect(http.StatusSeeOther, domain.WEB_HOME) })

	// Wrap all routes with a version
	router_v1 := s.Router.Group(domain.VERSION)

	// Domain specific routers
	router_web := router_v1.Group(domain.WEB)
	router_api := router_v1.Group(domain.API)
	router_state := router_web.Group("state")

	// Static routes
	router_web.Static("/static", "./web/static")

	// API router endpoints
	router_api.GET("/", func(ctx *gin.Context) { ctx.JSON(200, gin.H{"message": "Server is active."}) })
	router_api.GET("/tmp", func(ctx *gin.Context) {
		if !domain.IsLoggedIn(ctx) {
			ctx.JSON(200, gin.H{"error": "User is not logged in. Please login to continue"})
			return
		}

		userId := ctx.MustGet("userId").(int)
		userEmail := ctx.MustGet("userEmail").(string)

		ctx.JSON(200, gin.H{"id": userId, "email": userEmail})
	})

	// WEB router endpoints
	router_web.GET("/", func(ctx *gin.Context) { ctx.Redirect(http.StatusSeeOther, domain.WEB_HOME) })
	router_web.GET("/login", handlers.LoginPage)
	router_web.GET("/home", handlers.HomePage)
	router_web.GET("/favorites", handlers.FavoritesPage)
	router_web.GET("/create", handlers.CreatePage)
	router_web.GET("/profile", handlers.ProfilePage)
	router_web.GET("/list", handlers.ListPage)

	// WEB state endpoints
	router_state.POST("/tags", handlers.NewTag)
	router_state.POST("/tags/delete", handlers.DeleteTag)

	// Authentication
	router_api.GET("/auth/login", handlers.GoogleLogin)
	router_api.GET("/auth/callback", handlers.GoogleCallback)
	router_api.GET("/auth/logout", handlers.Logout)

	// Recipe endpoints
	// TODO: This should be post. Temp!
	router_api.POST("/recipe", handlers.CreateRecipe)

	return s
}
