package main

import (
	"context"
	"embed"
	"flag"
	"fmt"
	"io/fs"
	"log/slog"
	"os"
	"os/signal"
	"syscall"

	"github.com/uopi/uopi/internal/broker"
	"github.com/uopi/uopi/internal/config"
	"github.com/uopi/uopi/internal/datasource/epics"
	"github.com/uopi/uopi/internal/datasource/stub"
	"github.com/uopi/uopi/internal/datasource/synthetic"
	"github.com/uopi/uopi/internal/server"
	"github.com/uopi/uopi/internal/storage"
	"github.com/uopi/uopi/web"
)

// The web package's embed.go embeds web/dist at build time.
// This blank import ensures the compiler includes the package.
var _ embed.FS

func main() {
	configPath := flag.String("config", "", "path to TOML config file (optional)")
	flag.Parse()

	log := slog.New(slog.NewTextHandler(os.Stdout, nil))

	cfg, err := config.Load(*configPath)
	if err != nil {
		log.Error("failed to load config", "err", err)
		os.Exit(1)
	}

	if err := os.MkdirAll(cfg.Server.StorageDir, 0o755); err != nil {
		log.Error("failed to create storage dir", "dir", cfg.Server.StorageDir, "err", err)
		os.Exit(1)
	}

	store, err := storage.New(cfg.Server.StorageDir)
	if err != nil {
		log.Error("failed to open interface store", "err", err)
		os.Exit(1)
	}

	webFS, err := fs.Sub(web.FS, "dist")
	if err != nil {
		log.Error("failed to sub web dist", "err", err)
		os.Exit(1)
	}

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

	brk := broker.New(ctx, log)

	// Stub data source is always registered — it provides synthetic test signals
	// used by demo interfaces and unit tests.
	stubDS := stub.New()
	if err := stubDS.Connect(ctx); err != nil {
		log.Error("stub connect", "err", err)
		os.Exit(1)
	}
	brk.Register(stubDS)

	// Synthetic data source: computes derived signals from upstream sources via
	// configurable DSP pipelines defined in synthetic.json inside the storage dir.
	var synthDS *synthetic.Synthetic
	if cfg.Synthetic.Enabled {
		synthDS = synthetic.New(cfg.Server.StorageDir, brk, log)
		if err := synthDS.Connect(ctx); err != nil {
			log.Warn("synthetic connect failed — continuing without synthetic signals", "err", err)
			synthDS = nil
		} else {
			brk.Register(synthDS)
		}
	}

	// EPICS Channel Access data source (requires -tags epics build).
	// epics.Available() returns false when built without the tag.
	if cfg.EPICS.Enabled && epics.Available() {
		ds := epics.New(cfg.EPICS.CAAddrList, cfg.EPICS.ArchiveURL)
		if err := ds.Connect(ctx); err != nil {
			log.Error("epics connect", "err", err)
		} else {
			brk.Register(ds)
		}
	}

	srv := server.New(cfg.Server.Listen, webFS, brk, synthDS, store, cfg.EPICS.ChannelFinderURL, log)

	if err := srv.Start(ctx); err != nil {
		fmt.Fprintf(os.Stderr, "server error: %v\n", err)
		os.Exit(1)
	}
}
