package main

import (
	"context"
	"log/slog"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"

	"Engine/internal/database"
	"Engine/internal/handler"
	"Engine/internal/service"
)

func main() {
	logger := slog.New(slog.NewJSONHandler(os.Stdout, nil))

	dbPath := os.Getenv("DB_PATH")
	if dbPath == "" {
		dbPath = "fpa.db"
	}

	db, err := database.Connect(context.Background(), dbPath)
	if err != nil {
		logger.Error("failed to open database", "error", err)
		os.Exit(1)
	}
	defer db.Close()

	if err := database.Migrate(db); err != nil {
		logger.Error("migration failed", "error", err)
		os.Exit(1)
	}

	// Reference data (FK parents — must exist before budgets/actuals)
	referenceRepo := database.NewReferenceRepo(db)
	referenceH := handler.NewReferenceHandler(referenceRepo)

	// Core FP&A
	budgetRepo := database.NewBudgetRepo(db)
	actualsRepo := database.NewActualsRepo(db)
	budgetSvc := service.NewBudgetService(budgetRepo)
	varianceSvc := service.NewVarianceService(budgetRepo, actualsRepo)
	budgetH := handler.NewBudgetHandler(budgetSvc)
	actualsH := handler.NewActualsHandler(actualsRepo)
	varianceH := handler.NewVarianceHandler(varianceSvc)
	reportRepo := database.NewReportRepo(db)
	reportSvc := service.NewReportService(reportRepo)
	reportH := handler.NewReportHandler(reportSvc)
	forecastH := handler.NewForecastHandler(budgetRepo, actualsRepo)

	mux := http.NewServeMux()

	// Reference endpoints
	mux.HandleFunc("POST /api/v1/department/create", referenceH.CreateDepartment)
	mux.HandleFunc("POST /api/v1/department/activity", referenceH.SetActivityDepartment)
	mux.HandleFunc("DELETE /api/v1/department/delete", referenceH.DeleteDepartment)
	mux.HandleFunc("GET /api/v1/department/list", referenceH.ListDepartments)
	mux.HandleFunc("GET /api/v1/department/", referenceH.GetDepartment)

	mux.HandleFunc("POST /api/v1/gl-account/create", referenceH.CreateGLAccount)
	mux.HandleFunc("DELETE /api/v1/gl-account/delete", referenceH.DeleteGLAccount)
	mux.HandleFunc("GET /api/v1/gl-account/list", referenceH.ListGLAccounts)
	mux.HandleFunc("GET /api/v1/gl-account", referenceH.ListGLAccounts)

	// Budget endpoints
	mux.HandleFunc("POST /api/v1/budget/create", budgetH.Create)
	mux.HandleFunc("PUT /api/v1/budget/update", budgetH.Update)
	mux.HandleFunc("DELETE /api/v1/budget/delete", budgetH.Delete)

	// Actuals + variance
	mux.HandleFunc("POST /api/v1/actuals/ingest", actualsH.Ingest)
	mux.HandleFunc("POST /api/v1/actuals/ingest/batch", actualsH.IngestBatch)
	mux.HandleFunc("GET /api/v1/variance", varianceH.Report)
	mux.HandleFunc("GET /api/v1/variance/alerts", varianceH.Alerts)
	mux.HandleFunc("GET /api/v1/variance/reforecast", varianceH.Alerts)

	//forecast
	mux.HandleFunc("GET /api/v1/forecast/export", forecastH.Export)
	mux.HandleFunc("POST /api/v1/forecast/ingest", forecastH.Ingest)

	//reports
	mux.HandleFunc("GET /api/v1/reports/pnl", reportH.PnL)

	mux.HandleFunc("GET /api/v1/health", func(w http.ResponseWriter, r *http.Request) {
		if err := db.PingContext(r.Context()); err != nil {
			http.Error(w, `{"error":"db unhealthy"}`, http.StatusServiceUnavailable)
			return
		}
		w.Header().Set("Content-Type", "application/json")
		w.Write([]byte(`{"status":"ok"}`))
	})

	port := os.Getenv("PORT")
	if port == "" {
		port = "8080"
	}

	srv := &http.Server{
		Addr:         ":" + port,
		Handler:      mux,
		ReadTimeout:  10 * time.Second,
		WriteTimeout: 30 * time.Second,
		IdleTimeout:  120 * time.Second,
	}

	quit := make(chan os.Signal, 1)
	signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		logger.Info("server starting", "port", port, "db", dbPath)
		if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
			logger.Error("server error", "error", err)
			os.Exit(1)
		}
	}()

	<-quit
	logger.Info("shutting down gracefully")
	ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second)
	defer cancel()
	srv.Shutdown(ctx)
}
