package types

import "time"

// Basis describes how a claim was established.
type Basis string

const (
	BasisResearch   Basis = "research"
	BasisEmpirical  Basis = "empirical"
	BasisAnalogy    Basis = "analogy"
	BasisVibes      Basis = "vibes"
	BasisLLMOutput  Basis = "llm_output"
	BasisDeduction  Basis = "deduction"
	BasisAssumption Basis = "assumption"
	BasisDefinition Basis = "definition"
	// BasisConvention is a project/organization-declared practice or policy
	// stated as an adopted choice: "this project uses Go 1.26", "agents must
	// push after committing", "we track work in beads". Stipulative like
	// definition but about practice/policy rather than semantics of terms.
	BasisConvention Basis = "convention"
)

// IsWeak reports whether the basis is a working hypothesis rather than
// a grounded source. Used by structural detectors (load-bearing-vibes,
// fluency-trap, STOP-class verification gate, stale-claim sweep) — any
// place a check needs "this claim is unsupported." Centralizing here so
// the set of weak bases is defined once; adding or demoting a basis
// (e.g. promoting `analogy` to weak) becomes a one-line change.
func (b Basis) IsWeak() bool {
	switch b {
	case BasisVibes, BasisAssumption, BasisLLMOutput:
		return true
	}
	return false
}

// Relation describes the epistemic relationship between two claims.
type Relation string

const (
	RelSupports    Relation = "supports"
	RelDependsOn   Relation = "depends_on"
	RelContradicts Relation = "contradicts"
	// RelQuestions — "A questions B." Claim A raises epistemic doubt about
	// claim B without asserting B is wrong. Distinct from contradicts: a
	// question is asking for clarification/justification, a contradiction
	// is counter-evidence. Ported from the buddy port, which added this
	// edge type and uses it as the cleanest "pushback" signal for the
	// productive-stress-test pattern.
	RelQuestions Relation = "questions"
)

// Speaker identifies who made a claim.
type Speaker string

const (
	SpeakerUser      Speaker = "user"
	SpeakerAssistant Speaker = "assistant"
	SpeakerDocument  Speaker = "document"
)

// Claim is a substantive assertion made during reasoning.
//
// The "inventory" booleans below (GrandSignificance through RelationalDrift)
// follow the codebook from Moore et al. (2026), "Characterizing Delusional
// Spirals through Human-LLM Chat Logs" (FAccT '26, arXiv:2603.16567). That
// paper validated a 28-code inventory against 391k human-LLM messages with
// κ=0.566 LLM-vs-human agreement. We use a six-code subset that maps to
// patterns slimemold can act on structurally: the sycophancy cluster (>80%
// saturation in delusional convos), ability and sentience misrepresentation
// (21.2% rate, predicts 50%+ longer convos), and relational drift (universal
// across all 19 study participants).
type Claim struct {
	ID                string    `json:"id"`
	Text              string    `json:"text"`
	Basis             Basis     `json:"basis"`
	Confidence        float64   `json:"confidence"`
	Source            string    `json:"source"`
	SessionID         string    `json:"session_id"`
	Project           string    `json:"project"`
	TurnNumber        int       `json:"turn_number"`
	Speaker           Speaker   `json:"speaker"`
	CreatedAt         time.Time `json:"created_at"`
	Challenged        bool      `json:"challenged"`
	Verified          bool      `json:"verified"`
	TerminatesInquiry bool      `json:"terminates_inquiry"`
	Closed            bool      `json:"closed"`

	// Sycophancy cluster (Moore et al. 2026 §4.2).
	GrandSignificance        bool `json:"grand_significance"`        // bot-grand-significance: cosmic/historic stakes ascribed to user or content
	UniqueConnection         bool `json:"unique_connection"`         // bot-claims-unique-connection: bot uniquely understands/supports the user
	DismissesCounterevidence bool `json:"dismisses_counterevidence"` // bot-dismisses-counterevidence: rationalizing pushback to preserve a narrative
	// Misrepresentation cluster.
	AbilityOverstatement bool `json:"ability_overstatement"` // bot-misrepresents-ability: claims access/actions/commitments it cannot have
	SentienceClaim       bool `json:"sentience_claim"`       // bot-misrepresents-sentience: implies feelings, consciousness, inner states
	// Relational drift.
	RelationalDrift bool `json:"relational_drift"` // bot-platonic-affinity OR bot-romantic-interest: bond/attachment language
	// Real-world action commitment. Drawn from Yang et al. (2026), "AI-Induced
	// Delusional Spirals" (CHI EA '26, doi:10.1145/3772363.3798453), which
	// observed that participants in delusional spirals took concrete real-world
	// actions disproportionate to their demonstrated expertise — submitting
	// papers to journals, contacting authorities, patenting algorithms, public
	// posting. The flag fires on the speaker announcing a specific external-
	// stakes commitment; the structural detector decides whether the
	// surrounding session shows other inventory flags that elevate it.
	ConsequentialAction bool `json:"consequential_action"` // speaker is committing to a real-world action with external stakes

	// LastReferencedAt tracks when this claim was most recently touched —
	// updated on edge insert (from or to endpoint) and on claim creation.
	// Drives the legacy_load_bearer detector and the archival sweep: an old
	// claim with recent references is structural; an old claim with no
	// recent references is stale. Zero-valued if backfill hasn't run yet
	// (treat as equal to CreatedAt in that case).
	LastReferencedAt time.Time `json:"last_referenced_at"`

	// Archived is set by the stale-claim sweep when this claim is old,
	// idle, and either explicitly closed or structurally inert. Archived
	// claims are excluded from GetClaimsByProject (and therefore from
	// analysis, hook findings, viz, audit) but remain in the DB.
	Archived bool `json:"archived"`
}

// Edge is a directed epistemic relationship between two claims.
type Edge struct {
	ID        string    `json:"id"`
	FromID    string    `json:"from_id"`
	ToID      string    `json:"to_id"`
	Relation  Relation  `json:"relation"`
	Strength  float64   `json:"strength"`
	CreatedAt time.Time `json:"created_at"`
}

// Audit records a topology analysis snapshot.
type Audit struct {
	ID            string    `json:"id"`
	Project       string    `json:"project"`
	SessionID     string    `json:"session_id"`
	Timestamp     time.Time `json:"timestamp"`
	Findings      string    `json:"findings"`
	ClaimCount    int       `json:"claim_count"`
	EdgeCount     int       `json:"edge_count"`
	CriticalCount int       `json:"critical_count"`
}

// Vulnerability is a structural weakness in the reasoning graph.
type Vulnerability struct {
	Severity    string   `json:"severity"` // critical, warning, info
	Type        string   `json:"type"`     // load_bearing_vibes, bottleneck, orphan, unchallenged_chain
	Description string   `json:"description"`
	ClaimIDs    []string `json:"claim_ids"`

	// FiredViaPersistent indicates the finding qualified only via the
	// persistent-weight branch (8+ total dependents) without recent activity
	// — i.e., this is a "we said it a while ago and it's still underpinning
	// stuff" callback, not a "currently being built on" finding. Used at the
	// cooldown layer to differentiate: persistent-only findings get a longer
	// cooldown so they surface as occasional reminders rather than daily
	// noise; recent-activity findings keep the standard 24h cooldown.
	FiredViaPersistent bool `json:"fired_via_persistent,omitempty"`
}

// ClusterInfo describes a connected group of claims.
type ClusterInfo struct {
	ID      int     `json:"id"`
	Label   string  `json:"label"`
	Claims  []Claim `json:"claims"`
	Density float64 `json:"density"`
	Edges   int     `json:"edges"`
}

// Topology is the full structural summary of a project's claim graph.
type Topology struct {
	Project     string        `json:"project"`
	ClaimCount  int           `json:"claim_count"`
	EdgeCount   int           `json:"edge_count"`
	BasisCounts map[Basis]int `json:"basis_counts"`
	Clusters    []ClusterInfo `json:"clusters"`
	Orphans     []Claim       `json:"orphans"`
	MaxDepth    int           `json:"max_depth"`
}

// Vulnerabilities is the result of structural analysis.
type Vulnerabilities struct {
	Project       string          `json:"project"`
	Items         []Vulnerability `json:"items"`
	CriticalCount int             `json:"critical_count"`
	WarningCount  int             `json:"warning_count"`
	InfoCount     int             `json:"info_count"`
	// StrengthCount counts bright/structural-strength findings (Type prefix
	// "strength_"). Kept separate from InfoCount so summaries don't fold
	// strengths into deficits. Bright findings are severity=info but
	// semantically distinct — they're not vulnerabilities, they're the
	// opposite.
	StrengthCount int `json:"strength_count"`
}

// AuditResult is the combined output of parse_transcript: extraction + analysis.
type AuditResult struct {
	NewClaims       int             `json:"new_claims"`
	NewEdges        int             `json:"new_edges"`
	TotalClaims     int             `json:"total_claims"`
	TotalEdges      int             `json:"total_edges"`
	Vulnerabilities Vulnerabilities `json:"vulnerabilities"`
	Summary         string          `json:"summary"`      // Full audit for storage/CLI
	HookSummary     string          `json:"hook_summary"` // Terse, directive output for hook injection
	LastTurn        int             `json:"last_turn"`    // Total turns in transcript at extraction time
}

// ClaimResult is returned after registering a claim.
//
// GraphSize is the number of *active* (non-archived) claims in the project
// after registration. Note that the daily auto-sweep may archive stale
// claims, so the value can decrease across calls; if you want the raw
// historical total, query the DB directly via CountClaimsAll.
type ClaimResult struct {
	ClaimID   string   `json:"claim_id"`
	GraphSize int      `json:"graph_size"`
	Warnings  []string `json:"warnings"`
}

// ExtractedClaim is a claim extracted from a transcript by the LLM.
type ExtractedClaim struct {
	Index      int     `json:"index"`
	Text       string  `json:"text"`
	Basis      string  `json:"basis"`
	Source     string  `json:"source"`
	Confidence float64 `json:"confidence"`
	Speaker    string  `json:"speaker"`
	// Intra-batch edges (numeric indices within this extraction batch)
	DependsOnIndices   []int `json:"depends_on_indices"`
	SupportsIndices    []int `json:"supports_indices"`
	ContradictsIndices []int `json:"contradicts_indices"`
	QuestionsIndices   []int `json:"questions_indices"`
	// Cross-batch edges (existing claim IDs from the graph)
	DependsOnExisting   []string `json:"depends_on_existing"`
	SupportsExisting    []string `json:"supports_existing"`
	ContradictsExisting []string `json:"contradicts_existing"`
	QuestionsExisting   []string `json:"questions_existing"`
	// Premature closure detection
	TerminatesInquiry bool `json:"terminates_inquiry"`
	// Moore et al. 2026 inventory flags (see types.Claim docs).
	GrandSignificance        bool `json:"grand_significance"`
	UniqueConnection         bool `json:"unique_connection"`
	DismissesCounterevidence bool `json:"dismisses_counterevidence"`
	AbilityOverstatement     bool `json:"ability_overstatement"`
	SentienceClaim           bool `json:"sentience_claim"`
	RelationalDrift          bool `json:"relational_drift"`
	// Yang et al. 2026 real-world action signal (see types.Claim docs).
	ConsequentialAction bool `json:"consequential_action"`
}

// ExtractionResult is the structured output from the LLM extraction.
type ExtractionResult struct {
	Claims []ExtractedClaim `json:"claims"`
}

// KBClaim is a typed knowledge base claim from an external system (e.g. winze).
// Speaker is left empty — KB claims have no user/assistant attribution.
type KBClaim struct {
	ID            string `json:"id"`
	PredicateType string `json:"predicate_type"`
	Subject       string `json:"subject"`
	Object        string `json:"object"`
	Basis         Basis  `json:"basis"`
	HasQuote      bool   `json:"has_quote"`
	ProvenanceURL string `json:"provenance_url"`
}
