package app

import (
	"context"
	"errors"
	"fmt"
	"log/slog"
	"net/http"

	"forge.capytal.company/capytalcode/project-comicverse/lib/middleware"
	"forge.capytal.company/capytalcode/project-comicverse/lib/router"
	"forge.capytal.company/capytalcode/project-comicverse/lib/router/rerrors"
	"github.com/minio/minio-go/v7"
	"keikos.work/configs"
	"keikos.work/handlers/pages"
)

type App interface {
	Start() error
	Stop() error
}

type Options struct {
	Port        uint
	Development bool
	Log         *slog.Logger

	S3 *minio.Client

	Assets http.Handler
}

type app struct {
	log *slog.Logger

	s3     *minio.Client
	assets http.Handler

	server *http.Server
}

func (a *app) Start() error {
	if err := a.server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
		return err
	}
	return nil
}

func (a *app) Stop() error {
	if err := a.server.Shutdown(context.Background()); err != nil {
		return err
	}
	return nil
}

func NewWeb(opts Options) App {
	r := router.NewRouter()

	r.Use(middleware.NewLoggerMiddleware(opts.Log))

	if opts.Development {
		configs.DEVELOPMENT = true
		r.Use(middleware.DevMiddleware)
	} else {
		r.Use(middleware.CacheMiddleware)
	}

	r.Use(rerrors.NewErrorMiddleware(pages.ErrorPage{}.Component, opts.Log))

	r.Handle("/", &pages.Home{})
	r.Handle("/assets/", opts.Assets)

	imgs := &pages.Images{S3: opts.S3}
	r.HandleFunc("GET /images", imgs.List)
	r.HandleFunc("GET /images/{name}", imgs.Get)

	srv := http.Server{
		Addr:    fmt.Sprintf(":%d", opts.Port),
		Handler: r,
	}

	return &app{
		log:    opts.Log,
		s3:     opts.S3,
		assets: opts.Assets,
		server: &srv,
	}
}

func NewAdmin(opts Options) App {
	r := router.NewRouter()

	r.Use(middleware.NewLoggerMiddleware(opts.Log))

	if opts.Development {
		configs.DEVELOPMENT = true
		r.Use(middleware.DevMiddleware)
	} else {
		r.Use(middleware.CacheMiddleware)
	}

	r.Use(rerrors.NewErrorMiddleware(pages.ErrorPage{}.Component, opts.Log))

	r.Handle("/", &pages.Dashboard{})
	r.Handle("/assets/", opts.Assets)

	imgs := &pages.Images{S3: opts.S3}
	r.HandleFunc("GET /images", imgs.List)
	r.HandleFunc("GET /images/{name}", imgs.Get)
	r.HandleFunc("POST /images", imgs.Create)
	r.HandleFunc("PUT /images/{name}", imgs.Update)
	r.HandleFunc("DELETE /images/{name}", imgs.Delete)

	srv := http.Server{
		Addr:    fmt.Sprintf(":%d", opts.Port),
		Handler: r,
	}

	return &app{
		log:    opts.Log,
		s3:     opts.S3,
		assets: opts.Assets,
		server: &srv,
	}
}
