package main

import (
	"fmt"
	"os"
	"os/signal"
	"sort"
	"strings"
	"syscall"

	ads "bsm/full/module/base/ads/cmd/main"
	cloud "bsm/full/module/base/cloud/cmd/main"
	cms "bsm/full/module/base/cms/cmd/main"
	feedback "bsm/full/module/base/feedback/cmd/main"
	fts "bsm/full/module/base/fts/cmd/main"
	initial "bsm/full/module/base/initial/cmd/main"
	logs "bsm/full/module/base/logs/cmd/main"
	mgt "bsm/full/module/base/mgt/cmd/main"
	passport "bsm/full/module/base/passport/cmd/main"
	sender "bsm/full/module/base/sender/cmd/main"
	address "bsm/full/module/ec/address/cmd/main"
	delivery "bsm/full/module/ec/delivery/cmd"
	mall "bsm/full/module/ec/mall/cmd/main"
	market "bsm/full/module/ec/market/cmd/main"
	order "bsm/full/module/ec/order/cmd/main"
	wallet "bsm/full/module/finance/wallet/cmd/main"
)

type component struct {
	name string
	run  func()
}

var components = []component{
	{"ads", ads.Run},
	{"cloud", cloud.Run},
	{"cms", cms.Run},
	{"feedback", feedback.Run},
	{"fts", fts.Run},
	{"initial", initial.Run},
	{"logs", logs.Run},
	{"mgt", mgt.Run},
	{"passport", passport.Run},
	{"sender", sender.Run},
	{"address", address.Run},
	{"delivery", delivery.Run},
	{"mall", mall.Run},
	{"market", market.Run},
	{"order", order.Run},
	{"wallet", wallet.Run},
}

func main() {
	selected, err := selectComponents(os.Getenv("BSM_SERVICES"))
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(2)
	}

	failures := make(chan error, len(selected))
	for _, item := range selected {
		item := item
		go func() {
			defer func() {
				if value := recover(); value != nil {
					failures <- fmt.Errorf("component %s panicked: %v", item.name, value)
				}
			}()
			fmt.Printf("starting component %s\n", item.name)
			item.run()
			failures <- fmt.Errorf("component %s stopped unexpectedly", item.name)
		}()
	}

	stop := make(chan os.Signal, 1)
	signal.Notify(stop, syscall.SIGINT, syscall.SIGTERM)
	select {
	case sig := <-stop:
		fmt.Printf("received %s, stopping monolith\n", sig)
	case err := <-failures:
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

func selectComponents(raw string) ([]component, error) {
	if strings.TrimSpace(raw) == "" || strings.EqualFold(strings.TrimSpace(raw), "all") {
		return components, nil
	}

	wanted := make(map[string]struct{})
	for _, name := range strings.Split(raw, ",") {
		name = strings.ToLower(strings.TrimSpace(name))
		if name != "" {
			wanted[name] = struct{}{}
		}
	}

	selected := make([]component, 0, len(wanted))
	for _, item := range components {
		if _, ok := wanted[item.name]; ok {
			selected = append(selected, item)
			delete(wanted, item.name)
		}
	}
	if len(wanted) != 0 {
		unknown := make([]string, 0, len(wanted))
		for name := range wanted {
			unknown = append(unknown, name)
		}
		sort.Strings(unknown)
		return nil, fmt.Errorf("unknown BSM_SERVICES: %s", strings.Join(unknown, ", "))
	}
	if len(selected) == 0 {
		return nil, fmt.Errorf("BSM_SERVICES did not select any component")
	}
	return selected, nil
}
