package services import ( "fmt" "github.com/jmoiron/sqlx" "mgit.msbls.de/m/KanzlAI-mGMT/internal/models" ) // DeadlineRuleService handles deadline rule queries type DeadlineRuleService struct { db *sqlx.DB } // NewDeadlineRuleService creates a new deadline rule service func NewDeadlineRuleService(db *sqlx.DB) *DeadlineRuleService { return &DeadlineRuleService{db: db} } // List returns deadline rules, optionally filtered by proceeding type func (s *DeadlineRuleService) List(proceedingTypeID *int) ([]models.DeadlineRule, error) { var rules []models.DeadlineRule var err error if proceedingTypeID != nil { err = s.db.Select(&rules, `SELECT id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, sequence_order, condition_rule_id, alt_duration_value, alt_duration_unit, alt_rule_code, is_active, created_at, updated_at FROM deadline_rules WHERE proceeding_type_id = $1 AND is_active = true ORDER BY sequence_order`, *proceedingTypeID) } else { err = s.db.Select(&rules, `SELECT id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, sequence_order, condition_rule_id, alt_duration_value, alt_duration_unit, alt_rule_code, is_active, created_at, updated_at FROM deadline_rules WHERE is_active = true ORDER BY proceeding_type_id, sequence_order`) } if err != nil { return nil, fmt.Errorf("listing deadline rules: %w", err) } return rules, nil } // RuleTreeNode represents a deadline rule with its children type RuleTreeNode struct { models.DeadlineRule Children []RuleTreeNode `json:"children,omitempty"` } // GetRuleTree returns a hierarchical tree of rules for a proceeding type func (s *DeadlineRuleService) GetRuleTree(proceedingTypeCode string) ([]RuleTreeNode, error) { // First resolve proceeding type code to ID var pt models.ProceedingType err := s.db.Get(&pt, `SELECT id, code, name, description, jurisdiction, default_color, sort_order, is_active FROM proceeding_types WHERE code = $1 AND is_active = true`, proceedingTypeCode) if err != nil { return nil, fmt.Errorf("resolving proceeding type %q: %w", proceedingTypeCode, err) } // Get all rules for this proceeding type var rules []models.DeadlineRule err = s.db.Select(&rules, `SELECT id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, sequence_order, condition_rule_id, alt_duration_value, alt_duration_unit, alt_rule_code, is_active, created_at, updated_at FROM deadline_rules WHERE proceeding_type_id = $1 AND is_active = true ORDER BY sequence_order`, pt.ID) if err != nil { return nil, fmt.Errorf("listing rules for type %q: %w", proceedingTypeCode, err) } return buildTree(rules), nil } // GetByIDs returns deadline rules by their IDs func (s *DeadlineRuleService) GetByIDs(ids []string) ([]models.DeadlineRule, error) { if len(ids) == 0 { return nil, nil } query, args, err := sqlx.In( `SELECT id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, sequence_order, condition_rule_id, alt_duration_value, alt_duration_unit, alt_rule_code, is_active, created_at, updated_at FROM deadline_rules WHERE id IN (?) AND is_active = true ORDER BY sequence_order`, ids) if err != nil { return nil, fmt.Errorf("building IN query: %w", err) } query = s.db.Rebind(query) var rules []models.DeadlineRule err = s.db.Select(&rules, query, args...) if err != nil { return nil, fmt.Errorf("fetching rules by IDs: %w", err) } return rules, nil } // GetRulesForProceedingType returns all active rules for a proceeding type ID func (s *DeadlineRuleService) GetRulesForProceedingType(proceedingTypeID int) ([]models.DeadlineRule, error) { var rules []models.DeadlineRule err := s.db.Select(&rules, `SELECT id, proceeding_type_id, parent_id, code, name, description, primary_party, event_type, is_mandatory, duration_value, duration_unit, timing, rule_code, deadline_notes, sequence_order, condition_rule_id, alt_duration_value, alt_duration_unit, alt_rule_code, is_active, created_at, updated_at FROM deadline_rules WHERE proceeding_type_id = $1 AND is_active = true ORDER BY sequence_order`, proceedingTypeID) if err != nil { return nil, fmt.Errorf("listing rules for proceeding type %d: %w", proceedingTypeID, err) } return rules, nil } // ListProceedingTypes returns all active proceeding types func (s *DeadlineRuleService) ListProceedingTypes() ([]models.ProceedingType, error) { var types []models.ProceedingType err := s.db.Select(&types, `SELECT id, code, name, description, jurisdiction, default_color, sort_order, is_active FROM proceeding_types WHERE is_active = true ORDER BY sort_order`) if err != nil { return nil, fmt.Errorf("listing proceeding types: %w", err) } return types, nil } // buildTree converts a flat list of rules into a hierarchical tree func buildTree(rules []models.DeadlineRule) []RuleTreeNode { nodeMap := make(map[string]*RuleTreeNode, len(rules)) var roots []RuleTreeNode // Create nodes for _, r := range rules { node := RuleTreeNode{DeadlineRule: r} nodeMap[r.ID.String()] = &node } // Build tree for _, r := range rules { node := nodeMap[r.ID.String()] if r.ParentID != nil { parentKey := r.ParentID.String() if parent, ok := nodeMap[parentKey]; ok { parent.Children = append(parent.Children, *node) continue } } roots = append(roots, *node) } return roots }