package main

import (
	"context"
	"errors"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/jackc/pgx/v5/pgxpool"

	"osaet/backend/internal/admin"
)

func main() {
	if err := run(); err != nil {
		fmt.Fprintln(os.Stderr, "error:", err)
		os.Exit(1)
	}
}

func run() error {
	command := "serve"
	if len(os.Args) > 1 {
		command = os.Args[1]
	}

	cfg := admin.LoadConfig()
	if err := cfg.Validate(); err != nil {
		return err
	}

	ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
	defer stop()

	db, err := admin.OpenDatabase(ctx, cfg.DatabaseURL)
	if err != nil {
		return err
	}
	defer db.Close()

	switch command {
	case "serve":
		return serve(ctx, cfg, db)
	case "migrate":
		return admin.RunMigrations(ctx, db, cfg.MigrationsDir)
	case "create-user":
		return createUser(ctx, db)
	case "import-markdown":
		return importMarkdown(ctx, cfg, db)
	default:
		return fmt.Errorf("unknown command %q", command)
	}
}

func createUser(ctx context.Context, db *pgxpool.Pool) error {
	if len(os.Args) < 3 {
		return errors.New("usage: osaet-admin create-user <username>")
	}

	password := os.Getenv("OSAET_ADMIN_PASSWORD")
	if password == "" {
		return errors.New("OSAET_ADMIN_PASSWORD is required")
	}

	user, err := admin.NewStore(db).CreateOrUpdateUser(ctx, os.Args[2], password)
	if err != nil {
		return err
	}

	fmt.Fprintf(os.Stdout, "admin user %q is ready\n", user.Username)
	return nil
}

func importMarkdown(ctx context.Context, cfg admin.Config, db *pgxpool.Pool) error {
	postsDir := cfg.PostsDir
	if len(os.Args) >= 3 {
		postsDir = os.Args[2]
	}

	result, err := admin.NewStore(db).ImportMarkdownPosts(ctx, postsDir)
	if err != nil {
		return err
	}

	fmt.Fprintf(os.Stdout, "imported %d markdown post(s), skipped %d file(s)\n", result.Imported, result.Skipped)
	return nil
}

func serve(ctx context.Context, cfg admin.Config, db *pgxpool.Pool) error {
	server := &http.Server{
		Addr:              cfg.Addr,
		Handler:           admin.NewServerWithContext(ctx, db, cfg).Router(),
		ReadHeaderTimeout: 5 * time.Second,
	}

	errCh := make(chan error, 1)
	go func() {
		errCh <- server.ListenAndServe()
	}()

	select {
	case <-ctx.Done():
		shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
		defer cancel()
		return server.Shutdown(shutdownCtx)
	case err := <-errCh:
		if errors.Is(err, http.ErrServerClosed) {
			return nil
		}
		return err
	}
}
