package ucca

// ObligationStatus is the Advisor's vertical slice over the compliance graph for ONE legal
// obligation: which accepted controls satisfy it, what evidence they require, what's missing,
// and the resulting status. The point is "the required evidence is (not) present", not "a
// document exists". citation_spans is pending until the Legal-Knowledge-Graph session attaches
// them to the obligation (the upper half of the bridge).
type ObligationStatus struct {
	ObligationID  string                    `json:"obligation_id"`
	LegalBasis    []string                  `json:"legal_basis"` // the obligation's citation_units
	Status        string                    `json:"status"`      // erfuellt | offen | unklar
	Controls      []ObligationControlStatus `json:"controls"`
	CitationSpans string                    `json:"citation_spans"` // "pending" until the registry fills them
}

// ObligationControlStatus is one control under an obligation with its evidence picture.
type ObligationControlStatus struct {
	Framework        string                `json:"framework"`
	Control          string                `json:"control"`
	MappingType      string                `json:"mapping_type"`
	RequiredEvidence []EvidenceRequirement `json:"required_evidence"`
	MissingEvidence  []EvidenceRequirement `json:"missing_evidence"`
}

// AssessObligationStatus traverses obligation_id -> (citation_unit) -> accepted Controls ->
// required Evidence -> Status. hasEvidence reports whether a given (framework, control,
// evidence_type) is already collected; pass nil in the MVP (no collection yet) -> everything
// required is missing and the status is "offen". Unknown or unmapped obligation -> "unklar".
func AssessObligationStatus(joins *ObligationJoinKeys, mappings *ControlMappingSet, evidence *EvidenceRequirementSet, obligationID string, hasEvidence func(framework, control, evidenceType string) bool) ObligationStatus {
	ob := joins.FindObligation(obligationID)
	if ob == nil {
		return ObligationStatus{ObligationID: obligationID, Status: "unklar", CitationSpans: "pending"}
	}
	st := ObligationStatus{
		ObligationID:  obligationID,
		LegalBasis:    ob.CitationUnits,
		CitationSpans: "pending",
		Controls:      []ObligationControlStatus{},
	}
	ctrls := AcceptedControlsForObligation(*ob, mappings)
	if len(ctrls) == 0 {
		st.Status = "unklar" // no accepted control reaches it — we cannot assess
		return st
	}
	anyMissing := false
	for _, m := range ctrls {
		req := evidence.RequiredFor(m.TargetFramework, m.TargetControl)
		missing := make([]EvidenceRequirement, 0, len(req))
		for _, e := range req {
			if hasEvidence == nil || !hasEvidence(e.Framework, e.Control, e.EvidenceType) {
				missing = append(missing, e)
			}
		}
		if len(missing) > 0 {
			anyMissing = true
		}
		st.Controls = append(st.Controls, ObligationControlStatus{
			Framework:        m.TargetFramework,
			Control:          m.TargetControl,
			MappingType:      m.MappingType,
			RequiredEvidence: req,
			MissingEvidence:  missing,
		})
	}
	if anyMissing {
		st.Status = "offen"
	} else {
		st.Status = "erfuellt"
	}
	return st
}
