package core

import (
	"context"
	"fmt"
	"time"

	"git.huangwc.com/pig/pig-farm-controller/internal/app/service"
	"git.huangwc.com/pig/pig-farm-controller/internal/app/webhook"
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/alarm"
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/device"
	domain_notify "git.huangwc.com/pig/pig-farm-controller/internal/domain/notify"
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/pig"
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/plan"
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/recipe"
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/task"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/config"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/database"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/logs"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/notify"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/transport/lora"
	"git.huangwc.com/pig/pig-farm-controller/internal/infra/utils/token"

	"gorm.io/gorm"
	"gorm.io/gorm/schema"
)

// Infrastructure 聚合了所有基础设施层的组件。
type Infrastructure struct {
	storage        database.Storage
	repos          *Repositories
	lora           *LoraComponents
	tokenGenerator token.Generator
}

// initInfrastructure 初始化所有基础设施层组件。
func initInfrastructure(ctx context.Context, cfg *config.Config) (*Infrastructure, error) {
	storage, err := initStorage(ctx, cfg.Database)
	if err != nil {
		return nil, err
	}

	repos := initRepositories(ctx, storage.GetDB(ctx))

	lora, err := initLora(ctx, cfg, repos)
	if err != nil {
		return nil, err
	}

	tokenGenerator := token.NewTokenGenerator([]byte(cfg.App.JWTSecret))

	return &Infrastructure{
		storage:        storage,
		repos:          repos,
		lora:           lora,
		tokenGenerator: tokenGenerator,
	}, nil
}

// Repositories 聚合了所有的仓库实例。
type Repositories struct {
	userRepo              repository.UserRepository
	deviceRepo            repository.DeviceRepository
	areaControllerRepo    repository.AreaControllerRepository
	deviceTemplateRepo    repository.DeviceTemplateRepository
	planRepo              repository.PlanRepository
	pendingTaskRepo       repository.PendingTaskRepository
	executionLogRepo      repository.ExecutionLogRepository
	sensorDataRepo        repository.SensorDataRepository
	deviceCommandLogRepo  repository.DeviceCommandLogRepository
	pendingCollectionRepo repository.PendingCollectionRepository
	userActionLogRepo     repository.UserActionLogRepository
	pigBatchRepo          repository.PigBatchRepository
	pigBatchLogRepo       repository.PigBatchLogRepository
	pigFarmRepo           repository.PigFarmRepository
	pigPenRepo            repository.PigPenRepository
	pigTransferLogRepo    repository.PigTransferLogRepository
	pigTradeRepo          repository.PigTradeRepository
	pigSickPigLogRepo     repository.PigSickLogRepository
	medicationLogRepo     repository.MedicationLogRepository
	notificationRepo      repository.NotificationRepository
	alarmRepo             repository.AlarmRepository
	pigTypeRepo           repository.PigTypeRepository
	rawMaterialRepo       repository.RawMaterialRepository
	nutrientRepo          repository.NutrientRepository
	unitOfWork            repository.UnitOfWork
}

// initRepositories 初始化所有的仓库。
func initRepositories(ctx context.Context, db *gorm.DB) *Repositories {
	baseCtx := context.Background()
	return &Repositories{
		userRepo:              repository.NewGormUserRepository(logs.AddCompName(baseCtx, "UserRepo"), db),
		deviceRepo:            repository.NewGormDeviceRepository(logs.AddCompName(baseCtx, "DeviceRepo"), db),
		areaControllerRepo:    repository.NewGormAreaControllerRepository(logs.AddCompName(baseCtx, "AreaControllerRepo"), db),
		deviceTemplateRepo:    repository.NewGormDeviceTemplateRepository(logs.AddCompName(baseCtx, "DeviceTemplateRepo"), db),
		planRepo:              repository.NewGormPlanRepository(logs.AddCompName(baseCtx, "PlanRepo"), db),
		pendingTaskRepo:       repository.NewGormPendingTaskRepository(logs.AddCompName(baseCtx, "PendingTaskRepo"), db),
		executionLogRepo:      repository.NewGormExecutionLogRepository(logs.AddCompName(baseCtx, "ExecutionLogRepo"), db),
		sensorDataRepo:        repository.NewGormSensorDataRepository(logs.AddCompName(baseCtx, "SensorDataRepo"), db),
		deviceCommandLogRepo:  repository.NewGormDeviceCommandLogRepository(logs.AddCompName(baseCtx, "DeviceCommandLogRepo"), db),
		pendingCollectionRepo: repository.NewGormPendingCollectionRepository(logs.AddCompName(baseCtx, "PendingCollectionRepo"), db),
		userActionLogRepo:     repository.NewGormUserActionLogRepository(logs.AddCompName(baseCtx, "UserActionLogRepo"), db),
		pigBatchRepo:          repository.NewGormPigBatchRepository(logs.AddCompName(baseCtx, "PigBatchRepo"), db),
		pigBatchLogRepo:       repository.NewGormPigBatchLogRepository(logs.AddCompName(baseCtx, "PigBatchLogRepo"), db),
		pigFarmRepo:           repository.NewGormPigFarmRepository(logs.AddCompName(baseCtx, "PigFarmRepo"), db),
		pigPenRepo:            repository.NewGormPigPenRepository(logs.AddCompName(baseCtx, "PigPenRepo"), db),
		pigTransferLogRepo:    repository.NewGormPigTransferLogRepository(logs.AddCompName(baseCtx, "PigTransferLogRepo"), db),
		pigTradeRepo:          repository.NewGormPigTradeRepository(logs.AddCompName(baseCtx, "PigTradeRepo"), db),
		pigSickPigLogRepo:     repository.NewGormPigSickLogRepository(logs.AddCompName(baseCtx, "PigSickPigLogRepo"), db),
		medicationLogRepo:     repository.NewGormMedicationLogRepository(logs.AddCompName(baseCtx, "MedicationLogRepo"), db),
		notificationRepo:      repository.NewGormNotificationRepository(logs.AddCompName(baseCtx, "NotificationRepo"), db),
		alarmRepo:             repository.NewGormAlarmRepository(logs.AddCompName(baseCtx, "AlarmRepo"), db),
		pigTypeRepo:           repository.NewGormPigTypeRepository(logs.AddCompName(baseCtx, "PigTypeRepo"), db),
		rawMaterialRepo:       repository.NewGormRawMaterialRepository(logs.AddCompName(baseCtx, "RawMaterialRepo"), db),
		nutrientRepo:          repository.NewGormNutrientRepository(logs.AddCompName(baseCtx, "NutrientRepo"), db),
		unitOfWork:            repository.NewGormUnitOfWork(logs.AddCompName(baseCtx, "UnitOfWork"), db),
	}
}

// DomainServices 聚合了所有的领域服务实例。
type DomainServices struct {
	pigPenTransferManager   pig.PigPenTransferManager
	pigTradeManager         pig.PigTradeManager
	pigSickManager          pig.SickPigManager
	pigBatchDomain          pig.PigBatchService
	generalDeviceService    device.Service
	taskFactory             plan.TaskFactory
	planExecutionManager    plan.ExecutionManager
	analysisPlanTaskManager plan.AnalysisPlanTaskManager
	planService             plan.Service
	notifyService           domain_notify.Service
	alarmService            alarm.AlarmService
	recipeService           recipe.Service
}

// initDomainServices 初始化所有的领域服务。
func initDomainServices(ctx context.Context, cfg *config.Config, infra *Infrastructure) (*DomainServices, error) {
	baseCtx := context.Background()

	notifyService, err := initNotifyService(ctx, cfg.Notify, infra.repos.userRepo, infra.repos.notificationRepo)
	if err != nil {
		return nil, fmt.Errorf("初始化通知服务失败: %w", err)
	}

	// 猪群管理相关
	pigPenTransferManager := pig.NewPigPenTransferManager(logs.AddCompName(baseCtx, "PigPenTransferManager"), infra.repos.pigPenRepo, infra.repos.pigTransferLogRepo, infra.repos.pigBatchRepo)
	pigTradeManager := pig.NewPigTradeManager(logs.AddCompName(baseCtx, "PigTradeManager"), infra.repos.pigTradeRepo)
	pigSickManager := pig.NewSickPigManager(logs.AddCompName(baseCtx, "PigSickManager"), infra.repos.pigSickPigLogRepo, infra.repos.medicationLogRepo)
	pigBatchDomain := pig.NewPigBatchService(
		logs.AddCompName(baseCtx, "PigBatchDomain"),
		infra.repos.pigBatchRepo,
		infra.repos.pigBatchLogRepo,
		infra.repos.unitOfWork,
		pigPenTransferManager,
		pigTradeManager, pigSickManager,
	)

	// 通用设备服务
	generalDeviceService := device.NewGeneralDeviceService(
		logs.AddCompName(baseCtx, "GeneralDeviceService"),
		infra.repos.deviceRepo,
		infra.repos.deviceCommandLogRepo,
		infra.repos.pendingCollectionRepo,
		infra.lora.comm,
	)

	// 告警服务
	alarmService := alarm.NewAlarmService(
		logs.AddCompName(baseCtx, "AlarmService"),
		infra.repos.alarmRepo,
		infra.repos.unitOfWork,
	)

	// 任务工厂
	taskFactory := task.NewTaskFactory(logs.AddCompName(baseCtx, "TaskFactory"),
		infra.repos.sensorDataRepo,
		infra.repos.deviceRepo,
		infra.repos.alarmRepo,
		generalDeviceService,
		notifyService,
		alarmService,
	)

	// 计划任务管理器
	analysisPlanTaskManager := plan.NewAnalysisPlanTaskManager(logs.AddCompName(baseCtx, "AnalysisPlanTaskManager"), infra.repos.planRepo, infra.repos.pendingTaskRepo, infra.repos.executionLogRepo)

	// 任务执行器
	planExecutionManager := plan.NewPlanExecutionManager(
		logs.AddCompName(baseCtx, "PlanExecutionManager"),
		infra.repos.pendingTaskRepo,
		infra.repos.executionLogRepo,
		infra.repos.deviceRepo,
		infra.repos.sensorDataRepo,
		infra.repos.planRepo,
		analysisPlanTaskManager,
		taskFactory,
		generalDeviceService,
		time.Duration(cfg.Task.Interval)*time.Second,
		cfg.Task.NumWorkers,
	)

	// 计划管理器
	planService := plan.NewPlanService(
		logs.AddCompName(baseCtx, "PlanService"),
		planExecutionManager,
		analysisPlanTaskManager,
		infra.repos.planRepo,
		infra.repos.deviceRepo,
		infra.repos.unitOfWork,
		taskFactory,
	)

	// 配方管理服务
	recipeService := recipe.NewRecipeService(
		logs.AddCompName(baseCtx, "RecipeService"),
		infra.repos.nutrientRepo,
		infra.repos.rawMaterialRepo,
		infra.repos.pigTypeRepo,
	)

	return &DomainServices{
		pigPenTransferManager:   pigPenTransferManager,
		pigTradeManager:         pigTradeManager,
		pigSickManager:          pigSickManager,
		pigBatchDomain:          pigBatchDomain,
		generalDeviceService:    generalDeviceService,
		analysisPlanTaskManager: analysisPlanTaskManager,
		taskFactory:             taskFactory,
		planExecutionManager:    planExecutionManager,
		planService:             planService,
		notifyService:           notifyService,
		alarmService:            alarmService,
		recipeService:           recipeService,
	}, nil
}

// AppServices 聚合了所有的应用服务实例。
type AppServices struct {
	pigFarmService        service.PigFarmService
	pigBatchService       service.PigBatchService
	monitorService        service.MonitorService
	deviceService         service.DeviceService
	planService           service.PlanService
	userService           service.UserService
	auditService          service.AuditService
	thresholdAlarmService service.ThresholdAlarmService
	feedManagementService service.FeedManagementService
}

// initAppServices 初始化所有的应用服务。
func initAppServices(ctx context.Context, infra *Infrastructure, domainServices *DomainServices) *AppServices {
	baseCtx := context.Background()
	pigFarmService := service.NewPigFarmService(logs.AddCompName(baseCtx, "PigFarmService"), infra.repos.pigFarmRepo, infra.repos.pigPenRepo, infra.repos.pigBatchRepo, domainServices.pigBatchDomain, infra.repos.unitOfWork)
	pigBatchService := service.NewPigBatchService(logs.AddCompName(baseCtx, "PigBatchService"), domainServices.pigBatchDomain)
	monitorService := service.NewMonitorService(
		logs.AddCompName(baseCtx, "MonitorService"),
		infra.repos.sensorDataRepo,
		infra.repos.deviceCommandLogRepo,
		infra.repos.executionLogRepo,
		infra.repos.planRepo,
		infra.repos.pendingCollectionRepo,
		infra.repos.userActionLogRepo,
		infra.repos.medicationLogRepo,
		infra.repos.pigBatchRepo,
		infra.repos.pigBatchLogRepo,
		infra.repos.pigTransferLogRepo,
		infra.repos.pigSickPigLogRepo,
		infra.repos.pigTradeRepo,
		infra.repos.notificationRepo,
	)

	// 初始化阈值告警服务
	thresholdAlarmService := service.NewThresholdAlarmService(
		logs.AddCompName(baseCtx, "ThresholdAlarmService"),
		domainServices.alarmService,
		domainServices.planService,
		infra.repos.alarmRepo,
		infra.repos.planRepo,
		infra.repos.areaControllerRepo,
		infra.repos.deviceRepo,
	)

	deviceService := service.NewDeviceService(
		logs.AddCompName(baseCtx, "DeviceService"),
		infra.repos.deviceRepo,
		infra.repos.areaControllerRepo,
		infra.repos.deviceTemplateRepo,
		domainServices.generalDeviceService,
		thresholdAlarmService,
	)

	auditService := service.NewAuditService(logs.AddCompName(baseCtx, "AuditService"), infra.repos.userActionLogRepo)
	planService := service.NewPlanService(logs.AddCompName(baseCtx, "AppPlanService"), domainServices.planService)
	userService := service.NewUserService(logs.AddCompName(baseCtx, "UserService"), infra.repos.userRepo, infra.tokenGenerator, domainServices.notifyService)
	feedManagementService := service.NewFeedManagementService(logs.AddCompName(baseCtx, "FeedManagementService"), domainServices.recipeService)

	return &AppServices{
		pigFarmService:        pigFarmService,
		pigBatchService:       pigBatchService,
		monitorService:        monitorService,
		deviceService:         deviceService,
		auditService:          auditService,
		planService:           planService,
		userService:           userService,
		thresholdAlarmService: thresholdAlarmService,
		feedManagementService: feedManagementService,
	}
}

// LoraComponents 聚合了所有 LoRa 相关组件。
type LoraComponents struct {
	listenHandler webhook.ListenHandler
	comm          transport.Communicator
	loraListener  transport.Listener
}

// initLora 根据配置初始化 LoRa 相关组件。
func initLora(
	ctx context.Context,
	cfg *config.Config,
	repos *Repositories,
) (*LoraComponents, error) {
	var listenHandler webhook.ListenHandler
	var comm transport.Communicator
	var loraListener transport.Listener
	baseCtx := context.Background()

	logger := logs.GetLogger(ctx)
	if cfg.Lora.Mode == config.LoraMode_LoRaWAN {
		logger.Info("当前运行模式: lora_wan。初始化 ChirpStack 监听器和传输层。")
		listenHandler = webhook.NewChirpStackListener(logs.AddCompName(baseCtx, "ChirpStackListener"), repos.sensorDataRepo, repos.deviceRepo, repos.areaControllerRepo, repos.deviceCommandLogRepo, repos.pendingCollectionRepo)
		comm = lora.NewChirpStackTransport(logs.AddCompName(baseCtx, "ChirpStackTransport"), cfg.ChirpStack)
		loraListener = lora.NewPlaceholderTransport(logs.AddCompName(baseCtx, "PlaceholderTransport"))
	} else {
		logger.Info("当前运行模式: lora_mesh。初始化 LoRa Mesh 传输层和占位符监听器。")
		listenHandler = webhook.NewPlaceholderListener(logs.AddCompName(baseCtx, "PlaceholderListener"))
		tp, err := lora.NewLoRaMeshUartPassthroughTransport(logs.AddCompName(baseCtx, "LoRaMeshTransport"), cfg.LoraMesh, repos.areaControllerRepo, repos.pendingCollectionRepo, repos.deviceRepo, repos.sensorDataRepo)
		if err != nil {
			return nil, fmt.Errorf("无法初始化 LoRa Mesh 模块: %w", err)
		}
		loraListener = tp
		comm = tp
	}

	return &LoraComponents{
		listenHandler: listenHandler,
		comm:          comm,
		loraListener:  loraListener,
	}, nil
}

// initNotifyService 根据配置初始化并返回一个通知领域服务。
// 它确保至少有一个 LogNotifier 总是可用，并根据配置启用其他通知器。
func initNotifyService(
	ctx context.Context,
	cfg config.NotifyConfig,
	userRepo repository.UserRepository,
	notificationRepo repository.NotificationRepository,
) (domain_notify.Service, error) {
	var availableNotifiers []notify.Notifier
	logger := logs.GetLogger(ctx)
	baseCtx := context.Background()

	// 1. 总是创建 LogNotifier 作为所有告警的最终记录渠道
	logNotifier := notify.NewLogNotifier(logs.AddCompName(baseCtx, "LogNotifier"))
	availableNotifiers = append(availableNotifiers, logNotifier)
	logger.Info("Log通知器已启用 (作为所有告警的最终记录渠道)")

	// 2. 根据配置，按需创建并收集所有启用的其他 Notifier 实例
	if cfg.SMTP.Enabled {
		smtpNotifier := notify.NewSMTPNotifier(
			logs.AddCompName(baseCtx, "SMTPNotifier"),
			cfg.SMTP.Host,
			cfg.SMTP.Port,
			cfg.SMTP.Username,
			cfg.SMTP.Password,
			cfg.SMTP.Sender,
		)
		availableNotifiers = append(availableNotifiers, smtpNotifier)
		logger.Info("SMTP通知器已启用")
	}

	if cfg.WeChat.Enabled {
		wechatNotifier := notify.NewWechatNotifier(
			logs.AddCompName(baseCtx, "WechatNotifier"),
			cfg.WeChat.CorpID,
			cfg.WeChat.AgentID,
			cfg.WeChat.Secret,
		)
		availableNotifiers = append(availableNotifiers, wechatNotifier)
		logger.Info("企业微信通知器已启用")
	}

	if cfg.Lark.Enabled {
		larkNotifier := notify.NewLarkNotifier(
			logs.AddCompName(baseCtx, "LarkNotifier"),
			cfg.Lark.AppID,
			cfg.Lark.AppSecret,
		)
		availableNotifiers = append(availableNotifiers, larkNotifier)
		logger.Info("飞书通知器已启用")
	}

	// 3. 动态确定首选通知器
	var primaryNotifier notify.Notifier
	primaryNotifierType := models.NotifierType(cfg.Primary)

	// 检查用户指定的主渠道是否已启用
	for _, n := range availableNotifiers {
		if n.Type() == primaryNotifierType {
			primaryNotifier = n
			break
		}
	}

	// 如果用户指定的主渠道未启用或未指定，则自动选择第一个可用的 (这将是 LogNotifier，如果其他都未启用)
	if primaryNotifier == nil {
		primaryNotifier = availableNotifiers[0] // 确保总能找到一个，因为 LogNotifier 总是存在的
		logger.Warnf("配置的首选渠道 '%s' 未启用或未指定，已自动降级使用 '%s' 作为首选渠道。", cfg.Primary, primaryNotifier.Type())
	}

	// 4. 使用创建的 Notifier 列表和 notificationRepo 来组装领域服务
	notifyService, err := domain_notify.NewFailoverService(
		logs.AddCompName(baseCtx, "FailoverNotifyService"),
		userRepo,
		availableNotifiers,
		primaryNotifier.Type(),
		cfg.FailureThreshold,
		notificationRepo,
	)
	if err != nil {
		return nil, fmt.Errorf("创建故障转移通知服务失败: %w", err)
	}

	logger.Infof("通知服务初始化成功，首选渠道: %s, 故障阈值: %d", primaryNotifier.Type(), cfg.FailureThreshold)
	return notifyService, nil
}

// initStorage 封装了数据库的初始化、连接和迁移逻辑。
func initStorage(ctx context.Context, cfg config.DatabaseConfig) (database.Storage, error) {
	logger := logs.GetLogger(ctx)

	// 创建存储实例
	storage := database.NewStorage(logs.AddCompName(context.Background(), "Storage"), cfg)
	if err := storage.Connect(ctx); err != nil {
		// 错误已在 Connect 内部被记录，这里只需包装并返回
		return nil, fmt.Errorf("数据库连接失败: %w", err)
	}

	// 获取所有模型
	allModels := models.GetAllModels()

	// -- 启动时检查：确保所有模型都实现了 schema.Tabler 接口 --
	// 这是一个硬性要求，用于保证代码质量和表名定义的明确性。
	// 如果一个模型没有实现 TableName() string 方法，程序将在此处 panic。
	for _, model := range allModels {
		if _, ok := model.(schema.Tabler); !ok {
			logger.Panicf(fmt.Sprintf("启动失败：模型 %T 未实现 schema.Tabler 接口。请为该模型添加 TableName() string 方法，以显式指定其数据库表名。", model))
		}
	}

	// 执行数据库迁移
	if err := storage.Migrate(ctx, allModels...); err != nil {
		return nil, fmt.Errorf("数据库迁移失败: %w", err)
	}

	logs.GetLogger(ctx).Info("数据库初始化完成。")
	return storage, nil
}
