package service

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"

	"git.huangwc.com/pig/pig-farm-controller/internal/app/dto"
	"git.huangwc.com/pig/pig-farm-controller/internal/domain/device"
	"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/repository"
)

var (
	// ErrDeviceInUse 表示设备正在被任务使用，无法删除
	ErrDeviceInUse = errors.New("设备正在被一个或多个任务使用，无法删除")

	// ErrAreaControllerInUse 表示区域主控正在被设备使用，无法删除
	ErrAreaControllerInUse = errors.New("区域主控正在被一个或多个设备使用，无法删除")

	// ErrDeviceTemplateInUse 表示设备模板正在被设备使用，无法删除
	ErrDeviceTemplateInUse = errors.New("设备模板正在被一个或多个设备使用，无法删除")
)

// DeviceService 定义了应用层的设备服务接口，用于协调设备相关的业务逻辑。
// DeviceService 定义了应用层的设备服务接口，用于协调设备相关的业务逻辑。
type DeviceService interface {
	CreateDevice(ctx context.Context, req *dto.CreateDeviceRequest) (*dto.DeviceResponse, error)
	GetDevice(ctx context.Context, id uint32) (*dto.DeviceResponse, error)
	ListDevices(ctx context.Context) ([]*dto.DeviceResponse, error)
	UpdateDevice(ctx context.Context, id uint32, req *dto.UpdateDeviceRequest) (*dto.DeviceResponse, error)
	DeleteDevice(ctx context.Context, id uint32) error
	ManualControl(ctx context.Context, id uint32, req *dto.ManualControlDeviceRequest) error

	CreateAreaController(ctx context.Context, req *dto.CreateAreaControllerRequest) (*dto.AreaControllerResponse, error)
	GetAreaController(ctx context.Context, id uint32) (*dto.AreaControllerResponse, error)
	ListAreaControllers(ctx context.Context) ([]*dto.AreaControllerResponse, error)
	UpdateAreaController(ctx context.Context, id uint32, req *dto.UpdateAreaControllerRequest) (*dto.AreaControllerResponse, error)
	DeleteAreaController(ctx context.Context, id uint32) error

	CreateDeviceTemplate(ctx context.Context, req *dto.CreateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error)
	GetDeviceTemplate(ctx context.Context, id uint32) (*dto.DeviceTemplateResponse, error)
	ListDeviceTemplates(ctx context.Context) ([]*dto.DeviceTemplateResponse, error)
	UpdateDeviceTemplate(ctx context.Context, id uint32, req *dto.UpdateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error)
	DeleteDeviceTemplate(ctx context.Context, id uint32) error
}

// deviceService 是 DeviceService 接口的具体实现。
type deviceService struct {
	ctx                   context.Context
	deviceRepo            repository.DeviceRepository
	areaControllerRepo    repository.AreaControllerRepository
	deviceTemplateRepo    repository.DeviceTemplateRepository
	deviceDomainSvc       device.Service
	thresholdAlarmService ThresholdAlarmService
}

// NewDeviceService 创建一个新的 DeviceService 实例。
func NewDeviceService(
	ctx context.Context,
	deviceRepo repository.DeviceRepository,
	areaControllerRepo repository.AreaControllerRepository,
	deviceTemplateRepo repository.DeviceTemplateRepository,
	deviceDomainSvc device.Service,
	thresholdAlarmService ThresholdAlarmService,
) DeviceService {
	return &deviceService{
		ctx:                   ctx,
		deviceRepo:            deviceRepo,
		areaControllerRepo:    areaControllerRepo,
		deviceTemplateRepo:    deviceTemplateRepo,
		deviceDomainSvc:       deviceDomainSvc,
		thresholdAlarmService: thresholdAlarmService,
	}
}

// --- Devices ---

func (s *deviceService) CreateDevice(ctx context.Context, req *dto.CreateDeviceRequest) (*dto.DeviceResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateDevice")
	propertiesJSON, err := json.Marshal(req.Properties)
	if err != nil {
		return nil, err // Consider wrapping this error for better context
	}

	device := &models.Device{
		Name:             req.Name,
		DeviceTemplateID: req.DeviceTemplateID,
		AreaControllerID: req.AreaControllerID,
		Location:         req.Location,
		Properties:       propertiesJSON,
	}

	if err := device.SelfCheck(); err != nil {
		return nil, err
	}

	if err := s.deviceRepo.Create(serviceCtx, device); err != nil {
		return nil, err
	}

	createdDevice, err := s.deviceRepo.FindByID(serviceCtx, device.ID)
	if err != nil {
		return nil, err
	}

	return dto.NewDeviceResponse(createdDevice)
}

func (s *deviceService) GetDevice(ctx context.Context, id uint32) (*dto.DeviceResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetDevice")
	device, err := s.deviceRepo.FindByID(serviceCtx, id)
	if err != nil {
		return nil, err
	}
	return dto.NewDeviceResponse(device)
}

func (s *deviceService) ListDevices(ctx context.Context) ([]*dto.DeviceResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListDevices")
	devices, err := s.deviceRepo.ListAll(serviceCtx)
	if err != nil {
		return nil, err
	}
	return dto.NewListDeviceResponse(devices)
}

func (s *deviceService) UpdateDevice(ctx context.Context, id uint32, req *dto.UpdateDeviceRequest) (*dto.DeviceResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateDevice")
	existingDevice, err := s.deviceRepo.FindByID(serviceCtx, id)
	if err != nil {
		return nil, err
	}

	propertiesJSON, err := json.Marshal(req.Properties)
	if err != nil {
		return nil, err
	}

	existingDevice.Name = req.Name
	existingDevice.DeviceTemplateID = req.DeviceTemplateID
	existingDevice.AreaControllerID = req.AreaControllerID
	existingDevice.Location = req.Location
	existingDevice.Properties = propertiesJSON

	if err := existingDevice.SelfCheck(); err != nil {
		return nil, err
	}

	if err := s.deviceRepo.Update(serviceCtx, existingDevice); err != nil {
		return nil, err
	}

	updatedDevice, err := s.deviceRepo.FindByID(serviceCtx, existingDevice.ID)
	if err != nil {
		return nil, err
	}

	return dto.NewDeviceResponse(updatedDevice)
}

func (s *deviceService) DeleteDevice(ctx context.Context, id uint32) error {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteDevice")

	// 检查设备是否存在
	_, err := s.deviceRepo.FindByID(serviceCtx, id)
	if err != nil {
		return err // 如果未找到，会返回 gorm.ErrRecordNotFound
	}

	// 在删除前检查设备是否被任务使用
	inUse, err := s.deviceRepo.IsDeviceInUse(serviceCtx, id, []models.TaskType{models.TaskTypeDeviceThresholdCheck})
	if err != nil {
		// 如果检查过程中发生数据库错误，则返回错误
		return fmt.Errorf("检查设备使用情况失败: %w", err)
	}
	if inUse {
		// 如果设备正在被使用，则返回特定的业务错误
		return ErrDeviceInUse
	}

	// TODO 这个应该用事务处理
	err = s.thresholdAlarmService.DeleteDeviceThresholdAlarmByDeviceID(serviceCtx, id)
	if err != nil {
		return fmt.Errorf("删除设备阈值告警失败: %w", err)
	}

	// 只有在未被使用时，才执行删除操作
	return s.deviceRepo.Delete(serviceCtx, id)
}

func (s *deviceService) ManualControl(ctx context.Context, id uint32, req *dto.ManualControlDeviceRequest) error {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "ManualControl")
	dev, err := s.deviceRepo.FindByID(serviceCtx, id)
	if err != nil {
		return err
	}

	if req.Action == nil {
		return s.deviceDomainSvc.Collect(serviceCtx, dev.AreaControllerID, []*models.Device{dev})
	} else {
		action := device.DeviceActionStart
		switch *req.Action {
		case "off":
			action = device.DeviceActionStop
		case "on":
			action = device.DeviceActionStart
		default:
			return errors.New("invalid action")
		}
		return s.deviceDomainSvc.Switch(serviceCtx, dev, action)
	}
}

// --- Area Controllers ---

func (s *deviceService) CreateAreaController(ctx context.Context, req *dto.CreateAreaControllerRequest) (*dto.AreaControllerResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateAreaController")
	propertiesJSON, err := json.Marshal(req.Properties)
	if err != nil {
		return nil, err
	}

	ac := &models.AreaController{
		Name:       req.Name,
		NetworkID:  req.NetworkID,
		Location:   req.Location,
		Properties: propertiesJSON,
	}

	if err := ac.SelfCheck(); err != nil {
		return nil, err
	}

	if err := s.areaControllerRepo.Create(serviceCtx, ac); err != nil {
		return nil, err
	}

	return dto.NewAreaControllerResponse(ac)
}

func (s *deviceService) GetAreaController(ctx context.Context, id uint32) (*dto.AreaControllerResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetAreaController")
	ac, err := s.areaControllerRepo.FindByID(serviceCtx, id)
	if err != nil {
		return nil, err
	}
	return dto.NewAreaControllerResponse(ac)
}

func (s *deviceService) ListAreaControllers(ctx context.Context) ([]*dto.AreaControllerResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListAreaControllers")
	acs, err := s.areaControllerRepo.ListAll(serviceCtx)
	if err != nil {
		return nil, err
	}
	return dto.NewListAreaControllerResponse(acs)
}

func (s *deviceService) UpdateAreaController(ctx context.Context, id uint32, req *dto.UpdateAreaControllerRequest) (*dto.AreaControllerResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateAreaController")
	existingAC, err := s.areaControllerRepo.FindByID(serviceCtx, id)
	if err != nil {
		return nil, err
	}

	propertiesJSON, err := json.Marshal(req.Properties)
	if err != nil {
		return nil, err
	}

	existingAC.Name = req.Name
	existingAC.NetworkID = req.NetworkID
	existingAC.Location = req.Location
	existingAC.Properties = propertiesJSON

	if err := existingAC.SelfCheck(); err != nil {
		return nil, err
	}

	if err := s.areaControllerRepo.Update(serviceCtx, existingAC); err != nil {
		return nil, err
	}

	return dto.NewAreaControllerResponse(existingAC)
}

func (s *deviceService) DeleteAreaController(ctx context.Context, id uint32) error {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteAreaController")

	// 1. 检查是否存在
	_, err := s.areaControllerRepo.FindByID(serviceCtx, id)
	if err != nil {
		return err // 如果未找到，gorm会返回 ErrRecordNotFound
	}

	// 2. 检查是否被使用（业务逻辑）
	inUse, err := s.areaControllerRepo.IsAreaControllerUsedByTasks(serviceCtx, id, []models.TaskType{models.TaskTypeAreaCollectorThresholdCheck})
	if err != nil {
		return err // 返回数据库检查错误
	}
	if inUse {
		return ErrAreaControllerInUse // 返回业务错误
	}

	// TODO 这个应该用事务处理
	err = s.thresholdAlarmService.DeleteAreaThresholdAlarmByAreaControllerID(serviceCtx, id)
	if err != nil {
		return fmt.Errorf("删除区域阈值告警失败: %w", err)
	}

	// 3. 执行删除
	return s.areaControllerRepo.Delete(serviceCtx, id)
}

// --- Device Templates ---

func (s *deviceService) CreateDeviceTemplate(ctx context.Context, req *dto.CreateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "CreateDeviceTemplate")
	commandsJSON, err := json.Marshal(req.Commands)
	if err != nil {
		return nil, err
	}

	valuesJSON, err := json.Marshal(req.Values)
	if err != nil {
		return nil, err
	}

	deviceTemplate := &models.DeviceTemplate{
		Name:         req.Name,
		Manufacturer: req.Manufacturer,
		Description:  req.Description,
		Category:     req.Category,
		Commands:     commandsJSON,
		Values:       valuesJSON,
	}

	if err := deviceTemplate.SelfCheck(); err != nil {
		return nil, err
	}

	if err := s.deviceTemplateRepo.Create(serviceCtx, deviceTemplate); err != nil {
		return nil, err
	}

	return dto.NewDeviceTemplateResponse(deviceTemplate)
}

func (s *deviceService) GetDeviceTemplate(ctx context.Context, id uint32) (*dto.DeviceTemplateResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "GetDeviceTemplate")
	deviceTemplate, err := s.deviceTemplateRepo.FindByID(serviceCtx, id)
	if err != nil {
		return nil, err
	}
	return dto.NewDeviceTemplateResponse(deviceTemplate)
}

func (s *deviceService) ListDeviceTemplates(ctx context.Context) ([]*dto.DeviceTemplateResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "ListDeviceTemplates")
	deviceTemplates, err := s.deviceTemplateRepo.ListAll(serviceCtx)
	if err != nil {
		return nil, err
	}
	return dto.NewListDeviceTemplateResponse(deviceTemplates)
}

func (s *deviceService) UpdateDeviceTemplate(ctx context.Context, id uint32, req *dto.UpdateDeviceTemplateRequest) (*dto.DeviceTemplateResponse, error) {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "UpdateDeviceTemplate")
	existingDeviceTemplate, err := s.deviceTemplateRepo.FindByID(serviceCtx, id)
	if err != nil {
		return nil, err
	}

	commandsJSON, err := json.Marshal(req.Commands)
	if err != nil {
		return nil, err
	}

	valuesJSON, err := json.Marshal(req.Values)
	if err != nil {
		return nil, err
	}

	existingDeviceTemplate.Name = req.Name
	existingDeviceTemplate.Manufacturer = req.Manufacturer
	existingDeviceTemplate.Description = req.Description
	existingDeviceTemplate.Category = req.Category
	existingDeviceTemplate.Commands = commandsJSON
	existingDeviceTemplate.Values = valuesJSON

	if err := existingDeviceTemplate.SelfCheck(); err != nil {
		return nil, err
	}

	if err := s.deviceTemplateRepo.Update(serviceCtx, existingDeviceTemplate); err != nil {
		return nil, err
	}

	return dto.NewDeviceTemplateResponse(existingDeviceTemplate)
}

func (s *deviceService) DeleteDeviceTemplate(ctx context.Context, id uint32) error {
	serviceCtx := logs.AddFuncName(ctx, s.ctx, "DeleteDeviceTemplate")

	// 1. 检查是否存在
	_, err := s.deviceTemplateRepo.FindByID(serviceCtx, id)
	if err != nil {
		return err
	}

	// 2. 检查是否被使用（业务逻辑）
	inUse, err := s.deviceTemplateRepo.IsInUse(serviceCtx, id)
	if err != nil {
		return err
	}
	if inUse {
		return ErrDeviceTemplateInUse // 返回业务错误
	}

	// 3. 执行删除
	return s.deviceTemplateRepo.Delete(serviceCtx, id)
}
