package services

import (
	"fmt"
	"time"

	"github.com/jmoiron/sqlx"

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

// DetermineService handles event-driven deadline determination.
// It walks the proceeding event tree and calculates cascading dates.
type DetermineService struct {
	rules      *DeadlineRuleService
	calculator *DeadlineCalculator
}

// NewDetermineService creates a new determine service
func NewDetermineService(db *sqlx.DB, calculator *DeadlineCalculator) *DetermineService {
	return &DetermineService{
		rules:      NewDeadlineRuleService(db),
		calculator: calculator,
	}
}

// TimelineEvent represents a calculated event in the proceeding timeline
type TimelineEvent struct {
	ID               string          `json:"id"`
	Code             string          `json:"code,omitempty"`
	Name             string          `json:"name"`
	Description      string          `json:"description,omitempty"`
	PrimaryParty     string          `json:"primary_party,omitempty"`
	EventType        string          `json:"event_type,omitempty"`
	IsMandatory      bool            `json:"is_mandatory"`
	DurationValue    int             `json:"duration_value"`
	DurationUnit     string          `json:"duration_unit"`
	RuleCode         string          `json:"rule_code,omitempty"`
	DeadlineNotes    string          `json:"deadline_notes,omitempty"`
	IsSpawn          bool            `json:"is_spawn"`
	SpawnLabel       string          `json:"spawn_label,omitempty"`
	HasCondition     bool            `json:"has_condition"`
	ConditionRuleID  string          `json:"condition_rule_id,omitempty"`
	AltRuleCode      string          `json:"alt_rule_code,omitempty"`
	AltDurationValue *int            `json:"alt_duration_value,omitempty"`
	AltDurationUnit  string          `json:"alt_duration_unit,omitempty"`
	Date             string          `json:"date,omitempty"`
	OriginalDate     string          `json:"original_date,omitempty"`
	WasAdjusted      bool            `json:"was_adjusted"`
	Children         []TimelineEvent `json:"children,omitempty"`
}

// DetermineRequest is the input for POST /api/deadlines/determine
type DetermineRequest struct {
	ProceedingType   string          `json:"proceeding_type"`
	TriggerEventDate string          `json:"trigger_event_date"`
	Conditions       map[string]bool `json:"conditions"`
}

// DetermineResponse is the output of the determine endpoint
type DetermineResponse struct {
	ProceedingType  string          `json:"proceeding_type"`
	ProceedingName  string          `json:"proceeding_name"`
	ProceedingColor string          `json:"proceeding_color"`
	TriggerDate     string          `json:"trigger_event_date"`
	Timeline        []TimelineEvent `json:"timeline"`
	TotalDeadlines  int             `json:"total_deadlines"`
}

// GetTimeline returns the proceeding event tree (without date calculations)
func (s *DetermineService) GetTimeline(proceedingTypeCode string) ([]TimelineEvent, *models.ProceedingType, error) {
	rules, pt, err := s.rules.GetFullTimeline(proceedingTypeCode)
	if err != nil {
		return nil, nil, err
	}

	tree := buildTimelineTree(rules)
	return tree, pt, nil
}

// Determine calculates the full timeline with cascading dates
func (s *DetermineService) Determine(req DetermineRequest) (*DetermineResponse, error) {
	timeline, pt, err := s.GetTimeline(req.ProceedingType)
	if err != nil {
		return nil, fmt.Errorf("loading timeline: %w", err)
	}

	triggerDate, err := time.Parse("2006-01-02", req.TriggerEventDate)
	if err != nil {
		return nil, fmt.Errorf("invalid trigger_event_date: %w", err)
	}

	conditions := req.Conditions
	if conditions == nil {
		conditions = make(map[string]bool)
	}

	total := s.calculateDates(timeline, triggerDate, conditions)

	return &DetermineResponse{
		ProceedingType:  pt.Code,
		ProceedingName:  pt.Name,
		ProceedingColor: pt.DefaultColor,
		TriggerDate:     req.TriggerEventDate,
		Timeline:        timeline,
		TotalDeadlines:  total,
	}, nil
}

// calculateDates walks the tree and calculates dates for each node
func (s *DetermineService) calculateDates(events []TimelineEvent, parentDate time.Time, conditions map[string]bool) int {
	total := 0
	for i := range events {
		ev := &events[i]

		// Skip inactive spawns: if this is a spawn node and conditions don't include it, skip
		if ev.IsSpawn && !conditions[ev.ID] {
			continue
		}

		durationValue := ev.DurationValue
		durationUnit := ev.DurationUnit
		ruleCode := ev.RuleCode

		// Apply conditional logic
		if ev.HasCondition && ev.ConditionRuleID != "" {
			if conditions[ev.ConditionRuleID] {
				if ev.AltDurationValue != nil {
					durationValue = *ev.AltDurationValue
				}
				if ev.AltDurationUnit != "" {
					durationUnit = ev.AltDurationUnit
				}
				if ev.AltRuleCode != "" {
					ruleCode = ev.AltRuleCode
				}
			}
		}

		// Calculate this node's date
		if durationValue > 0 {
			rule := models.DeadlineRule{
				DurationValue: durationValue,
				DurationUnit:  durationUnit,
			}
			adjusted, original, wasAdjusted := s.calculator.CalculateEndDate(parentDate, rule)
			ev.Date = adjusted.Format("2006-01-02")
			ev.OriginalDate = original.Format("2006-01-02")
			ev.WasAdjusted = wasAdjusted
		} else {
			ev.Date = parentDate.Format("2006-01-02")
			ev.OriginalDate = parentDate.Format("2006-01-02")
		}

		ev.RuleCode = ruleCode
		total++

		// Recurse: children's dates cascade from this node's date
		if len(ev.Children) > 0 {
			childDate, _ := time.Parse("2006-01-02", ev.Date)
			total += s.calculateDates(ev.Children, childDate, conditions)
		}
	}
	return total
}

// buildTimelineTree converts flat rules to a tree of TimelineEvents
func buildTimelineTree(rules []models.DeadlineRule) []TimelineEvent {
	nodeMap := make(map[string]*TimelineEvent, len(rules))
	var roots []TimelineEvent

	// Create event nodes
	for _, r := range rules {
		ev := ruleToEvent(r)
		nodeMap[r.ID.String()] = &ev
	}

	// Build tree by parent_id
	for _, r := range rules {
		ev := nodeMap[r.ID.String()]
		if r.ParentID != nil {
			parentKey := r.ParentID.String()
			if parent, ok := nodeMap[parentKey]; ok {
				parent.Children = append(parent.Children, *ev)
				continue
			}
		}
		roots = append(roots, *ev)
	}

	return roots
}

func ruleToEvent(r models.DeadlineRule) TimelineEvent {
	ev := TimelineEvent{
		ID:            r.ID.String(),
		Name:          r.Name,
		IsMandatory:   r.IsMandatory,
		DurationValue: r.DurationValue,
		DurationUnit:  r.DurationUnit,
		IsSpawn:       r.IsSpawn,
		HasCondition:  r.ConditionRuleID != nil,
	}
	if r.Code != nil {
		ev.Code = *r.Code
	}
	if r.Description != nil {
		ev.Description = *r.Description
	}
	if r.PrimaryParty != nil {
		ev.PrimaryParty = *r.PrimaryParty
	}
	if r.EventType != nil {
		ev.EventType = *r.EventType
	}
	if r.RuleCode != nil {
		ev.RuleCode = *r.RuleCode
	}
	if r.DeadlineNotes != nil {
		ev.DeadlineNotes = *r.DeadlineNotes
	}
	if r.SpawnLabel != nil {
		ev.SpawnLabel = *r.SpawnLabel
	}
	if r.ConditionRuleID != nil {
		ev.ConditionRuleID = r.ConditionRuleID.String()
	}
	if r.AltRuleCode != nil {
		ev.AltRuleCode = *r.AltRuleCode
	}
	ev.AltDurationValue = r.AltDurationValue
	if r.AltDurationUnit != nil {
		ev.AltDurationUnit = *r.AltDurationUnit
	}
	return ev
}
