// Package server implements the server.
package server

import (
	"context"
	"database/sql"
	"fmt"
	"io/fs"
	"mime"
	"net/http"

	// The sqlite db and migration driver.
	_ "github.com/golang-migrate/migrate/v4/database/sqlite"

	"git.lyda.ie/kevin/boxes/api"
	"git.lyda.ie/kevin/boxes/database"
	"git.lyda.ie/kevin/boxes/web"
	"github.com/golang-migrate/migrate/v4"
	"github.com/golang-migrate/migrate/v4/source/iofs"
	"github.com/labstack/echo/v4"
	"github.com/labstack/echo/v4/middleware"
	"github.com/spf13/cobra"
	"modernc.org/sqlite"
)

func init() {
	mime.AddExtensionType(".ico", "image/vnd.microsoft.icon")
}

// Run starts the metrics and API servers.
func Run(cmd *cobra.Command, _ []string) {
	// Get configuration and basic pieces.
	listen, err := cmd.Flags().GetString("listen")
	cobra.CheckErr(err)
	dbfile, err := cmd.Flags().GetString("database")
	cobra.CheckErr(err)

	// Get the web server up.
	e := echo.New()
	e.Use(middleware.LoggerWithConfig(middleware.LoggerConfig{}))

	// Connect and configure the database.
	// See https://theitsolutions.io/blog/modernc.org-sqlite-with-go
	const pragmas = `
	PRAGMA foreign_keys = ON;
`
	ctx := context.TODO()
	sqlite.RegisterConnectionHook(func(conn sqlite.ExecQuerierContext, _ string) error {
		_, err = conn.ExecContext(ctx, pragmas, nil)

		return err
	})

	migrationFS, err := iofs.New(database.MigrationsFS, "migrations")
	cobra.CheckErr(err)
	migration, err := migrate.NewWithSourceInstance("iofs", migrationFS, "sqlite://"+dbfile)
	cobra.CheckErr(err)
	err = migration.Up()
	if err != nil && err != migrate.ErrNoChange {
		cobra.CheckErr(err)
	}

	db, err := sql.Open("sqlite", dbfile)
	cobra.CheckErr(err)

	// Get the static files served.
	filesTree, err := fs.Sub(web.Files, "files")
	cobra.CheckErr(err)
	filesHandler := http.FileServer(http.FS(filesTree))
	e.GET("/", echo.WrapHandler(filesHandler))
	e.GET("/*.*", echo.WrapHandler(filesHandler))
	//e.GET("/images/*", echo.WrapHandler(http.StripPrefix("/images/", filesHandler)))

	// Get the endpoint handler set up.
	endpoints := NewEndpoints(db)
	server := api.NewStrictHandler(endpoints, nil)
	api.RegisterHandlers(e, server)

	// Put it all together.
	fmt.Printf("Server exited with: %+v\n", e.Start(listen))
}