package services

import (
	"fmt"

	"github.com/jmoiron/sqlx"

	"mgit.msbls.de/m/KanzlAI-mGMT/internal/models"
)

const ruleColumns = `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_spawn, spawn_label, is_active, created_at, updated_at`

// 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 `+ruleColumns+`
			 FROM deadline_rules
			 WHERE proceeding_type_id = $1 AND is_active = true
			 ORDER BY sequence_order`, *proceedingTypeID)
	} else {
		err = s.db.Select(&rules,
			`SELECT `+ruleColumns+`
			 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 `+ruleColumns+`
		 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
}

// GetFullTimeline returns the full event tree for a proceeding type using a recursive CTE.
// Unlike GetRuleTree, this follows parent_id across proceeding types (includes cross-type spawns).
func (s *DeadlineRuleService) GetFullTimeline(proceedingTypeCode string) ([]models.DeadlineRule, *models.ProceedingType, error) {
	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, nil, fmt.Errorf("resolving proceeding type %q: %w", proceedingTypeCode, err)
	}

	var rules []models.DeadlineRule
	err = s.db.Select(&rules, `
		WITH RECURSIVE tree AS (
			SELECT * FROM deadline_rules
			WHERE proceeding_type_id = $1 AND parent_id IS NULL AND is_active = true
			UNION ALL
			SELECT dr.* FROM deadline_rules dr
			JOIN tree t ON dr.parent_id = t.id
			WHERE dr.is_active = true
		)
		SELECT `+ruleColumns+` FROM tree ORDER BY sequence_order`, pt.ID)
	if err != nil {
		return nil, nil, fmt.Errorf("fetching timeline for type %q: %w", proceedingTypeCode, err)
	}

	return rules, &pt, 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 `+ruleColumns+`
		 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 `+ruleColumns+`
		 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
}
