package main

import (
	"context"
	"embed"
	"fmt"
	"os"
	"os/signal"
	"strconv"
	"strings"

	"github.com/getsentry/sentry-go"

	"github.com/rjNemo/rentease/internal/config"
	"github.com/rjNemo/rentease/internal/driver/calendar"
	"github.com/rjNemo/rentease/internal/driver/database"
	"github.com/rjNemo/rentease/internal/driver/parser"
	"github.com/rjNemo/rentease/internal/driver/pdf"
	bookingRepo "github.com/rjNemo/rentease/internal/repository/booking"
	"github.com/rjNemo/rentease/internal/server"
	"github.com/rjNemo/rentease/internal/service/auth"
	"github.com/rjNemo/rentease/internal/service/booking"
)

//go:embed assets
var static embed.FS

func main() {
	ctx := context.Background()

	if err := run(ctx, config.NewConfig()); err != nil {
		fmt.Fprintf(os.Stderr, "%s\n", err)
		os.Exit(1)
	}
}

func run(c context.Context, getEnv func(string) string) error {
	ctx, cancel := signal.NotifyContext(c, os.Interrupt)
	defer cancel()

	// init sentry
	if err := sentry.Init(sentry.ClientOptions{
		Dsn:              getEnv("SENTRY_DSN"),
		EnableTracing:    true,
		TracesSampleRate: 1.0,
	}); err != nil {
		return fmt.Errorf("error initializing sentry %w", err)
	}

	// init database
	db, err := database.New(getEnv("DATABASE_URL"))
	if err != nil {
		return fmt.Errorf("error connecting to the database %w", err)
	}

	if err = database.Migrate(db, &booking.Booking{}, &booking.Item{}, &booking.Payment{}); err != nil {
		return fmt.Errorf("error migrating the database %w", err)
	}

	bookingStore := bookingRepo.NewPgStore(db)

	gc, err := calendar.NewGoogleClient(ctx, getEnv("CALENDAR_CREDENTIALS"))
	if err != nil {
		return fmt.Errorf("error building calendar client %w", err)
	}

	// build pdf client
	pc, err := pdf.NewPdfClient(
		getEnv("HTMLDOCS_PROJECT_ID"),
		getEnv("HTMLDOCS_REPORT_PROJECT_ID"),
		getEnv("HTMLDOCS_URL"),
		getEnv("HTMLDOCS_KEY"),
	)
	if err != nil {
		return fmt.Errorf("error starting pdf client %w", err)
	}

	parsingClient := parser.NewBookingAgentParser(getEnv("PARSER_BASE_URL"))

	bookingService, err := booking.NewService(bookingStore, parsingClient, gc, pc)
	if err != nil {
		return fmt.Errorf("error creating booking service: %w", err)
	}

	// build authentication service
	as, err := auth.NewService(
		getEnv("SESSION_SECRET"),
		getEnv("ADMIN"),
		getEnv("ADMIN_SECRET"),
		getEnv("API_KEY"),
	)
	if err != nil {
		return fmt.Errorf("error starting auth service %w", err)
	}

	p := getEnv("PORT")
	port, err := strconv.Atoi(p)
	if err != nil {
		return fmt.Errorf("error parsing PORT env %w", err)
	}

	ogs := getEnv("ORIGINS")
	origins := strings.Split(ogs, ",")

	srv, err := server.New(
		bookingService,
		as,
		config.NewHost(), // TODO: move to the database at some point
		server.WithPort(port),
		server.WithFileSystem(static),
		server.WithDebug(strings.ToLower(getEnv("DEBUG")) == "true"),
		server.WithSecretKey(getEnv("SECRET_KEY")),
		server.WithOrigins(origins),
	)
	if err != nil {
		return fmt.Errorf("error starting server %w", err)
	}

	srv.Start(ctx)
	return nil
}
