// File: /home/meninjar/goprint/service-general/main.go
package main

import (
	"context"
	"fmt"
	"net/http"
	"os"
	"os/signal"
	"syscall"

	"service/internal/auth"
	antrol "service/internal/bpjs/antrol/reference"
	aplicareBed "service/internal/bpjs/aplicare/bed"
	apotekDpho "service/internal/bpjs/apotek/reference/dpho"
	apotekPoli "service/internal/bpjs/apotek/reference/poli"
	"service/internal/bpjs/vclaim/peserta"
	"service/internal/bpjs/vclaim/sep"
	"service/internal/infrastructure/cache"
	"service/internal/infrastructure/config"
	"service/internal/infrastructure/database"
	grpcHandlers "service/internal/infrastructure/transport/grpc/handlers"
	httpServer "service/internal/infrastructure/transport/http/servers"
	bpjsFactory "service/internal/interfaces/bpjs"
	"service/internal/interfaces/minio"
	satuSehatFactory "service/internal/interfaces/satusehat"

	satuSehatAuth "service/internal/satusehat/reference/auth"
	satuSehatKfa "service/internal/satusehat/reference/kfa"
	satuSehatLocation "service/internal/satusehat/reference/location"
	satuSehatOrganization "service/internal/satusehat/reference/organization"
	satuSehatPatient "service/internal/satusehat/reference/patient"
	satuSehatPractitioner "service/internal/satusehat/reference/practitioner"
	satuSehatCondition "service/internal/satusehat/usecase/condition"
	satuSehatEncounter "service/internal/satusehat/usecase/encounter"
	satuSehatProcedure "service/internal/satusehat/usecase/procedure"
	"service/pkg/logger"

	imagingStudyWorker "service/internal/worker/satusehat/imagingstudy"
	// roleComponent "service/internal/master/role/component"
	roleMaster "service/internal/master/role/master"
	rolePages "service/internal/master/role/pages"
	rolePermission "service/internal/master/role/permission"

	_ "service/docs/swagger" // Wajib: import swagger docs yang di-generate oleh swag CLI

	grpcServers "service/internal/infrastructure/transport/grpc/servers"

	"golang.org/x/sync/errgroup"
)

//	@title			GoPrint Service General API
//	@version		1.0.0
//	@description	REST API for Service General including Multi-DB, BPJS, and SatuSehat integrations.
//	@termsOfService	http://swagger.io/terms/

//	@contact.name	API Support
//	@contact.email	support@example.com

//	@license.name	Apache 2.0
//	@license.url	http://www.apache.org/licenses/LICENSE-2.0.html

// @BasePath					/api/v1
// @securityDefinitions.apikey	BearerAuth
// @in							header
// @name						Authorization
// @description				Type "Bearer" followed by a space and JWT token.
func main() {
	// 1. Load Config
	cfg := config.LoadConfig()

	// 2. Init Logger dengan konfigurasi dari config
	loggerConfig := logger.Config{
		Level:        cfg.Logger.Level,
		Format:       cfg.Logger.Format,
		Output:       "both", // Output ke console dan ke dalam folder daily logs
		ServiceName:  "service-general",
		EnableCaller: cfg.Server.Mode != "production", // Aktifkan caller info di non-production
		Environment:  cfg.Server.Mode,                 // Gunakan mode server untuk environment
	}
	logger.Init(loggerConfig)

	// Sekarang bisa menggunakan logger custom
	logger.Default().Info("Application starting",
		logger.String("service", "service-general"),
		logger.String("environment", cfg.Server.Mode),
		logger.String("log_level", cfg.Logger.Level),
	)
	// Di dalam main.go

	//  Asumsikan di config.yaml Anda ada database bernama "postgres" dan "mysql_legacy" contoh pemanggilan dan prosesing 2 tabel berbeda dalam satu repository

	// reportRepo := report.NewRepository(dbService, "postgres", "mysql_legacy")
	// reportService := report.NewService(reportRepo)

	// reportService ini tinggal di-pass ke dalam Handler untuk dijadikan endpoint REST API.

	// Add debugging to see what config was loaded
	logger.Default().Debug("Config loaded",
		logger.Any("database_count", len(cfg.Databases)),
		logger.Any("cache_enabled", cfg.Cache.Enabled),
		logger.Any("server_mode", cfg.Server.Mode),
	)

	if err := cfg.Validate(); err != nil {
		logger.Default().Fatal("Config validation failed", logger.ErrorField(err))
	}

	// --- Inisialisasi Minio Object Storage ---
	minio.Connect()
	logger.Default().Info("Minio Object Storage client initialized")

	// 3. Init Database Manager (Mendukung CQRS & Multi-DB)
	dbService := database.New(cfg)
	defer dbService.Close()

	// Menjalankan Automigrasi
	// if err := dbService.Migrate(); err != nil {
	// 	logger.Default().Warn("Database migration completed with issues", logger.ErrorField(err))
	// }

	// Get Master/Primary GORM DB Connection untuk modul lama
	// Gunakan nama koneksi "postgres" atau "default" sesuai config.yaml Anda
	gormDB, err := dbService.GetGormDB("default")
	if err != nil {
		logger.Default().Fatal("Failed to get primary gorm database", logger.ErrorField(err))
	}

	// --- [NEW] Inisialisasi Message Broker (Kafka / RabbitMQ) untuk Event-Driven Microservice ---
	// Jika bertindak sebagai gateway atau microservice independen, inisialisasi Kafka di sini
	// kafkaProducer := broker.NewKafkaProducer(cfg.Kafka.Brokers)
	// defer kafkaProducer.Close()
	// logger.Default().Info("✅ Kafka Producer initialized")

	// 4. Init Cache using factory pattern
	cacheFactory := cache.NewFactory(cfg.Cache)
	cacheManager, err := cacheFactory.CreateManager()
	if err != nil {
		logger.Default().Fatal("Failed to initialize cache manager", logger.ErrorField(err))
	}
	defer cacheManager.Close()

	if cfg.Cache.Enabled {
		logger.Default().Info("Cache initialized", logger.String("host", cfg.Cache.Redis.Host), logger.Int("port", cfg.Cache.Redis.Port))
	} else {
		logger.Default().Warn("Cache disabled, using NoOp cache")
	}

	// 5. Init Layers (CQRS Repositories & Services)
	// Modul Auth
	authCmdRepo := auth.NewCommandRepository(dbService, "default")
	authQueryRepo := auth.NewQueryRepository(dbService, "default")
	authSvc := auth.NewService(authCmdRepo, authQueryRepo, cacheManager, cfg)

	// Inisialisasi Role Pages, Permission, dan Component (Menggunakan pola CQRS baru)
	rolePagesCmdRepo := rolePages.NewCommandRepository(dbService, "default")
	rolePagesQueryRepo := rolePages.NewQueryRepository(dbService, "default")
	rolePagesService := rolePages.NewService(rolePagesCmdRepo, rolePagesQueryRepo, cacheManager)

	rolePermissionCmdRepo := rolePermission.NewCommandRepository(dbService, "default")
	rolePermissionQueryRepo := rolePermission.NewQueryRepository(dbService, "default")
	rolePermissionService := rolePermission.NewService(rolePermissionCmdRepo, rolePermissionQueryRepo, rolePagesQueryRepo, cacheManager)

	// Modul Role Master (Hasil Generator)
	roleMasterCmdRepo := roleMaster.NewCommandRepository(dbService, "default")
	roleMasterQueryRepo := roleMaster.NewQueryRepository(dbService, "default")
	roleMasterService := roleMaster.NewService(roleMasterCmdRepo, roleMasterQueryRepo, cacheManager)

	// roleComponentCmdRepo := roleComponent.NewCommandRepository(dbService, "postgres")
	// roleComponentQueryRepo := roleComponent.NewQueryRepository(dbService, "postgres")
	// roleComponentService := roleComponent.NewService(roleComponentCmdRepo, roleComponentQueryRepo)

	// --- [NEW] Inisialisasi Modul CQRS Baru (Memanfaatkan DB Service) ---
	// Contoh ketika mengimplementasikan entity "Province" atau "Person":
	// provinceCmdRepo := province.NewCommandRepository(dbService, "default")
	// provinceQueryRepo := province.NewQueryRepository(dbService, "default")
	// provinceSvc := province.NewService(provinceCmdRepo, provinceQueryRepo)

	// --- [NEW] Inisialisasi Factory BPJS ---
	bpjsServices := bpjsFactory.NewBPJSFactory(cfg.Bpjs)

	// 1. Modul VClaim (Lama)
	vclaimServiceEngine := bpjsServices.VClaim()
	sepRepo := sep.NewRepository(vclaimServiceEngine)
	sepService := sep.NewService(sepRepo)
	pesertaRepo := peserta.NewRepository(vclaimServiceEngine)
	pesertaService := peserta.NewService(pesertaRepo)

	// 2. Modul Antrean RS (Baru)
	antreanClient := bpjsServices.AntreanRS()
	antrolRepo := antrol.NewRepository(antreanClient)
	antrolService := antrol.NewService(antrolRepo)

	// 3. Modul Aplicares (Bed Ketersediaan Tempat Tidur)
	aplicareClient := bpjsServices.Aplicare()
	bedRepo := aplicareBed.NewRepository(aplicareClient)
	bedService := aplicareBed.NewService(bedRepo)

	// 4. Modul Apotek (Referensi DPHO dan Poli)
	apotekClient := bpjsServices.Apotek()
	dphoRepo := apotekDpho.NewRepository(apotekClient)
	dphoService := apotekDpho.NewService(dphoRepo)

	poliApotekRepo := apotekPoli.NewRepository(apotekClient)
	poliApotekService := apotekPoli.NewService(poliApotekRepo)

	// --- [NEW] Inisialisasi Modul Satu Sehat ---
	satusehatFact := satuSehatFactory.NewSatuSehatFactory(cfg.SatuSehat)
	satusehatClient := satusehatFact.Client()
	satuSehatAuthSvc := satuSehatAuth.NewService(satusehatClient, cacheManager)
	satuSehatPatientRepo := satuSehatPatient.NewRepository(satusehatClient)
	satuSehatPatientSvc := satuSehatPatient.NewService(satuSehatPatientRepo)

	satuSehatPractitionerRepo := satuSehatPractitioner.NewRepository(satusehatClient)
	satuSehatPractitionerSvc := satuSehatPractitioner.NewService(satuSehatPractitionerRepo)

	satuSehatOrganizationRepo := satuSehatOrganization.NewRepository(satusehatClient)
	satuSehatOrganizationSvc := satuSehatOrganization.NewService(satuSehatOrganizationRepo)

	satuSehatLocationRepo := satuSehatLocation.NewRepository(satusehatClient)
	satuSehatLocationSvc := satuSehatLocation.NewService(satuSehatLocationRepo)

	satuSehatKfaRepo := satuSehatKfa.NewRepository(satusehatClient)
	satuSehatKfaSvc := satuSehatKfa.NewService(satuSehatKfaRepo)

	satuSehatEncounterRepo := satuSehatEncounter.NewRepository(satusehatClient, dbService)
	satuSehatEncounterSvc := satuSehatEncounter.NewService(satuSehatEncounterRepo)

	satuSehatProcedureRepo := satuSehatProcedure.NewRepository(satusehatClient)
	satuSehatProcedureSvc := satuSehatProcedure.NewService(satuSehatProcedureRepo)

	satuSehatConditionRepo := satuSehatCondition.NewRepository(satusehatClient)
	satuSehatConditionSvc := satuSehatCondition.NewService(satuSehatConditionRepo)

	// 6. Server Orchestration (Dual Protocol & Background Workers)
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	// Gunakan errgroup untuk mengelola lifecycle server
	g, ctx := errgroup.WithContext(ctx)

	// --- [NEW] Start Background Workers ---
	if cfg.SatuSehat.Enabled {
		imagingStudyWorkerCfg := imagingStudyWorker.Config{
			DBManager:       dbService,
			InternalBaseURL: fmt.Sprintf("http://localhost:%d%s", cfg.Server.REST.Port, cfg.Swagger.BasePath), // otomatis: http://localhost:8080/api/v1
			InternalToken:   "",                                                                               // Isi token di sini jika endpoint internal Usecase Anda membutuhkan Bearer Token
			OrganizationID:  cfg.SatuSehat.OrgID,
		}
		isWorker := imagingStudyWorker.NewWorker(imagingStudyWorkerCfg)

		g.Go(func() error {
			isWorker.Run(ctx)
			return nil
		})
	}

	serverCount := 0

	// --- [A] Start REST Server ---
	if cfg.Server.REST.Enabled {
		serverCount++

		// Menggunakan ServiceRegistry agar HTTP Server lebih rapi dan scalable
		registry := &httpServer.ServiceRegistry{
			Config:       cfg,
			DBManager:    dbService,
			PrimaryDB:    gormDB,
			CacheManager: cacheManager,
			AuthService:  authSvc,
			Master: &httpServer.MasterServices{
				RolePages:      rolePagesService,
				RolePermission: rolePermissionService,
				RoleMaster:     roleMasterService,
			},
			VClaim: &httpServer.VClaimServices{
				Sep:     sepService,
				Peserta: pesertaService,
			},
			Antrol: &httpServer.AntrolServices{
				Reference: antrolService,
			},
			Aplicare: &httpServer.AplicaresServices{
				Bed: bedService,
			},
			Apotek: &httpServer.ApotekServices{
				DPHO: dphoService,
				Poli: poliApotekService,
			},
			SatuSehat: &httpServer.SatuSehatServices{
				Auth:         satuSehatAuthSvc,
				Patient:      satuSehatPatientSvc,
				Practitioner: satuSehatPractitionerSvc,
				Organization: satuSehatOrganizationSvc,
				Location:     satuSehatLocationSvc,
				KFA:          satuSehatKfaSvc,
				Encounter:    satuSehatEncounterSvc,
				Procedure:    satuSehatProcedureSvc,
				Condition:    satuSehatConditionSvc,
				// TODO: Inisialisasi service sisanya di sini saat repo & service usecase lainnya sudah Anda buat, contoh:
				// AllergyIntolerance:    allergySvc,
				// CarePlan:              careplanSvc,
				// ClinicalImpression:    clinicalImpressionSvc,
				// Composition:           compositionSvc,
				// DiagnosticReport:      diagnosticReportSvc,
				// EpisodeOfCare:         episodeOfCareSvc,
				// ImagingStudy:          imagingStudySvc,
				// Immunization:          immunizationSvc,
				// Medication:            medicationSvc,
				// MedicationDispense:    medicationDispenseSvc,
				// MedicationRequest:     medicationRequestSvc,
				// MedicationStatement:   medicationStatementSvc,
				// Observation:           observationSvc,
				// QuestionnaireResponse: questionnaireResponseSvc,
				// ServiceRequest:        serviceRequestSvc,
				// Specimen:              specimenSvc,
			},
		}

		restSrv := httpServer.NewHTTPServer(&cfg.Server.REST, registry)

		g.Go(func() error {
			logger.Default().Info("REST API running", logger.Int("port", cfg.Server.REST.Port))
			if err := restSrv.Start(&cfg.Server); err != nil && err != http.ErrServerClosed {
				logger.Default().Error("REST Server error", logger.ErrorField(err))
				return err
			}
			return nil
		})
	}

	// --- [B] Start gRPC Server ---
	if cfg.Server.GRPC.Enabled {
		serverCount++

		// 1. Buat gRPC handlers
		permissionHandler := grpcHandlers.NewPermissionHandler(rolePermissionService)
		// 2. Buat dan isi gRPC service registry
		grpcRegistry := &grpcServers.ServiceRegistry{
			PermissionHandler: permissionHandler,
		}

		// 3. Buat gRPC server dengan registry yang sudah diisi
		grpcSrv := grpcServers.NewGRPCServer(
			&cfg.Server.GRPC,
			grpcRegistry,
		)

		g.Go(func() error {
			logger.Default().Info("gRPC Server running", logger.Int("port", cfg.Server.GRPC.Port))
			if err := grpcSrv.Start(); err != nil {
				logger.Default().Error("gRPC Server error", logger.ErrorField(err))
				return err
			}
			return nil
		})

		g.Go(func() error {
			<-ctx.Done()
			logger.Default().Info("Gracefully stopping gRPC Server...")
			grpcSrv.Stop()
			return nil
		})
	}

	if serverCount == 0 {
		logger.Default().Fatal("No server enabled (REST and gRPC are both disabled in config)")
	}

	// --- [C] Graceful Shutdown Listener ---
	g.Go(func() error {
		quit := make(chan os.Signal, 1)
		signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)

		select {
		case <-quit:
			logger.Default().Warn("Signal received, shutting down servers...")
			cancel()
		case <-ctx.Done():
		}
		return nil
	})

	logger.Default().Info("Application started successfully")

	if err := g.Wait(); err != nil {
		logger.Default().Error("Server shutdown with error", logger.ErrorField(err))
	} else {
		logger.Default().Info("Server shutdown successful")
	}
}
