package server

import (
	"fmt"
	"net"

	echologrus "github.com/cemkiy/echo-logrus"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"github.com/sirupsen/logrus"

	"git.maze.io/ham/hamview/schema"
)

const DefaultServerListen = ":8073"

// Config holds the server configuration
type Config struct {
	Listen string `yaml:"listen"`
}

// DatabaseConfig holds database configuration
type DatabaseConfig struct {
	Type string `yaml:"type"`
	Conf string `yaml:"conf"`
}

// Server represents the HTTP server
type Server struct {
	listen     string
	listenAddr *net.TCPAddr
	logger     *logrus.Logger
}

// New creates a new Server instance
func New(serverConfig *Config, databaseConfig *DatabaseConfig, logger *logrus.Logger) (*Server, error) {
	if serverConfig.Listen == "" {
		serverConfig.Listen = DefaultServerListen
	}

	listenAddr, err := net.ResolveTCPAddr("tcp", serverConfig.Listen)
	if err != nil {
		return nil, fmt.Errorf("hamview: invalid listen address %q: %v", serverConfig.Listen, err)
	}

	if err = schema.Open(databaseConfig.Type, databaseConfig.Conf); err != nil {
		return nil, err
	}

	return &Server{
		listen:     serverConfig.Listen,
		listenAddr: listenAddr,
		logger:     logger,
	}, nil
}

// Run starts the HTTP server
func (s *Server) Run() error {
	echologrus.Logger = s.logger

	e := echo.New()
	e.HideBanner = true
	e.Logger = echologrus.GetEchoLogger()
	e.Use(echologrus.Hook())
	e.Use(middleware.RequestLogger())
	e.Use(middleware.CORSWithConfig(middleware.CORSConfig{
		AllowOrigins: []string{"*"},
		AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept},
	}))

	// Static files
	e.File("/", "./dashboard/dist/index.html")
	e.Static("/asset/", "./asset")
	e.Static("/assets/", "./dashboard/dist/assets")

	// Setup API routes
	setupRoutes(s, e)

	if s.listenAddr.IP == nil || s.listenAddr.IP.Equal(net.ParseIP("0.0.0.0")) || s.listenAddr.IP.Equal(net.ParseIP("::")) {
		s.logger.Infof("server: listening on http://127.0.0.1:%d", s.listenAddr.Port)
	} else {
		s.logger.Infof("server: listening on http://%s:%d", s.listenAddr.IP, s.listenAddr.Port)
	}

	return e.Start(s.listen)
}

// NewTestServer creates a server for testing with an in-memory SQLite database
func NewTestServer(logger *logrus.Logger) (*Server, error) {
	serverConfig := &Config{
		Listen: "127.0.0.1:0",
	}
	databaseConfig := &DatabaseConfig{
		Type: "sqlite3",
		Conf: ":memory:",
	}

	return New(serverConfig, databaseConfig, logger)
}
