// Package main — единый сервис bj-server.
//
// Объединяет в одном процессе: lk-gateway (REST API ЛК + admin web UI),
// m2m-core (FSM сделки, репозиторий, эмиссия и потребление Decision),
// nsd-adapter (REST к ИШ НРД и опрос входящих, когда профиль настроен),
// notify (заглушка отправки уведомлений). lk-emulator живёт отдельным
// бинарником как QA-инструмент.
//
// Архитектура подсказана объёмом 100-1000 сделок/день: для такого
// потока избыточно держать 5 отдельных процессов и микросервисную
// шину. Один Go-бинарник проще деплоить, проще наблюдать и
// масштабировать вертикально, а компоненты внутри по-прежнему
// разделены пакетами internal/<...>.
package main

import (
	"context"
	"log"
	"os"
	"os/signal"
	"syscall"
	"time"

	"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/lkgateway"
	"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/m2m"
	"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter"
	"git.zetit.ru/zuevav/Bridge-and-Join-s/internal/nsdadapter/igw"
)

const serviceName = "bj-server"

func main() {
	addr := getenv("BJ_HTTP_ADDR", ":8080")
	defaultSender := m2m.DeponentCode(getenv("BJ_M2M_SENDER", "MC0079200000"))
	defaultReceiver := m2m.DeponentCode(getenv("BJ_M2M_RECEIVER", "MC0010300000"))
	setupPath := os.Getenv("BJ_SETUP_PATH")

	cfg := lkgateway.ServerConfig{
		Addr:            addr,
		DefaultSender:   defaultSender,
		DefaultReceiver: defaultReceiver,
		SetupPath:       setupPath,
		// CheckOptions не задаём — server.go использует свой снапшот-based
		// вариант, который читает актуальные значения из setup.json
		// (DSN, crypto-сокет, URL ИШ, профиль), а не из ENV. Так проверки
		// статуса совпадают с тем, что реально настроено в UI.
	}

	srv, err := lkgateway.NewServer(cfg)
	if err != nil {
		log.Fatalf("%s: NewServer: %v", serviceName, err)
	}
	if cb := os.Getenv("BJ_LK_CALLBACK_URL"); cb != "" {
		srv.SetCallbackURL(cb)
	}

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

	// Опционально — поллер входящих пакетов ИШ НРД. Запускается если
	// BJ_NSD_PROFILE задан (после установки реального ИШ через UI этот
	// блок будет тянуть Decisions из настоящего НРД и применять их через
	// lkgateway.Service.ApplyDecision).
	if profileName := os.Getenv("BJ_NSD_PROFILE"); profileName != "" {
		go runNSDPoller(ctx, profileName)
	}

	// notify-демон: пока заглушка, в M3-M4 будет рассылать события
	// (e-mail, Yandex Messenger, Telegram, WS-push в admin-ui).
	go runNotifyWorker(ctx)

	log.Printf("%s: запуск, HTTP %s", serviceName, addr)
	runErr := srv.Run(ctx)
	stop()
	if runErr != nil {
		log.Printf("%s: %v", serviceName, runErr)
		os.Exit(1)
	}
}

// runNSDPoller — фоновый поллер входящих пакетов ИШ НРД.
func runNSDPoller(ctx context.Context, profileName string) {
	profile, err := nsdadapter.LookupProfile(profileName)
	if err != nil {
		log.Printf("%s: NSD poller: %v (доступные профили: %v)", serviceName, err, nsdadapter.AvailableProfiles())
		return
	}
	interval := 30 * time.Second
	if v := os.Getenv("BJ_NSD_POLL_INTERVAL"); v != "" {
		if d, err := time.ParseDuration(v); err == nil {
			interval = d
		}
	}
	client := igw.NewClient(profile.IGWBaseURL, igw.WithRetry(profile.RetryMax, profile.RetryBackoff))
	log.Printf("%s: NSD poller: профиль %s, канал %s, ИШ %s, интервал %s",
		serviceName, profile.Name, profile.Channel, profile.IGWBaseURL, interval)

	t := time.NewTicker(interval)
	defer t.Stop()
	since := time.Now().UTC().Add(-time.Hour)
	for {
		select {
		case <-ctx.Done():
			return
		case <-t.C:
			for _, kind := range nsdadapter.IncomingPackageKinds() {
				pkgs, err := client.ListIncoming(ctx, igw.ListFilter{
					Channel: profile.Channel,
					Date:    since,
					Type:    string(kind),
				})
				if err != nil {
					log.Printf("%s: NSD poller ListIncoming(%s, %s): %v", serviceName, profile.Channel, kind, err)
					continue
				}
				for _, p := range pkgs {
					log.Printf("%s: NSD входящий пакет id=%d (%s) типа %s, канал %s, state %s",
						serviceName, p.ID, p.Name, p.Type, p.Channel, p.State)
					// TODO(M3): GetPackage(p.ID) → unpack ZIP → парсить XML →
					// передавать в lkgateway.Service.ApplyDecision
				}
			}
			since = time.Now().UTC()
		}
	}
}

// runNotifyWorker — заглушка демона уведомлений.
func runNotifyWorker(ctx context.Context) {
	t := time.NewTicker(time.Minute)
	defer t.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case <-t.C:
			// На M3-M4 здесь будет: вытащить очередь событий из БД,
			// разослать по настроенным каналам (e-mail, мессенджер).
		}
	}
}

func getenv(k, def string) string {
	if v, ok := os.LookupEnv(k); ok && v != "" {
		return v
	}
	return def
}
