using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using FinlyticCore.Dtos.TechnicalAnalysis;
using FinlyticCore.Dtos.Trading;
namespace FinlyticEngine.Database.Entities;
///
/// Persists the full outcome of a single TradeLifecycleService.EvaluateAssetAsync run - one row per
/// evaluated asset, whether or not it produced a trade proposal. This is the append-only audit trail the
/// admin-only "why no proposals" Web UI tab (AdminEvaluationHistoryController) reads from via
/// MqttTopics.Channels.EngineGetEvaluationHistory.
///
[Table("engine_evaluation_snapshots")]
public class EngineEvaluationSnapshotEntity
{
[Key]
public Guid Id { get; set; } = Guid.NewGuid();
[Required]
[MaxLength(20)]
public string Isin { get; set; } = string.Empty;
[MaxLength(30)]
public string Symbol { get; set; } = string.Empty;
[Column(TypeName = "decimal(6,2)")]
public decimal TechnicalScore { get; set; }
[Column(TypeName = "decimal(6,2)")]
public decimal SentimentScore { get; set; }
[Column(TypeName = "decimal(6,2)")]
public decimal FundamentalScore { get; set; }
[Column(TypeName = "decimal(6,2)")]
public decimal CompositeOpportunityScore { get; set; }
///
/// Bonus points CompositeOpportunityScorer added to the raw weighted score based on
/// FinlyticSimulation's backtest-reliability matrix (see ScoringResult.ReliabilityBonus). Always
/// 0 when no reliability data was available or no bonus applied - never fabricated (Rules.md §4).
///
[Column(TypeName = "decimal(6,2)")]
public decimal ReliabilityBonus { get; set; }
public bool PassedEarningsLockout { get; set; }
public int? DaysToNextEarnings { get; set; }
/// Whether the ex-dividend gate (Engine.DividendGateDays) passed. See .
public bool PassedDividendGate { get; set; } = true;
public int? DaysToNextExDividend { get; set; }
///
/// Which FinlyticTechnicals universe-selection mechanism was responsible for this ISIN being scanned in
/// the first place (favorite/discovery/sentiment-spike), captured from
/// StrategyResultDto.UniverseSource at evaluation time. when the evaluated
/// setup did not originate from FinlyticTechnicals' continuously-scanned universe (e.g. a manual "Analyze
/// now" call for an ISIN nobody favorited/discovered/spiked) - never a fabricated guess (Rules.md §4).
///
public UniverseSource? UniverseSource { get; set; }
/// When the ISIN above entered that scan universe, alongside .
public DateTime? UniverseEnteredAtUtc { get; set; }
///
/// Whether FinlyticSimulation's backtest-reliability matrix vetoed this strategy/asset combination (see
/// ScoringResult.PassedSimulationVeto). Defaults to (matching
/// ScoringResult's own default) so a row where this gate was never actually evaluated - e.g. the
/// early-return case - never reads as "vetoed".
///
public bool PassedSimulationVeto { get; set; } = true;
public bool PassedAiValidation { get; set; }
[MaxLength(2048)]
public string AiThesisSummary { get; set; } = string.Empty;
///
/// Whether this evaluation was fired by the autonomous OpportunityPollerBackgroundService scan loop
/// or by an on-demand human request. See for why
/// (not ) is the default/zero value.
///
public TriggerSource TriggerSource { get; set; } = TriggerSource.Unknown;
///
/// Identity of the human caller who triggered this evaluation, resolved server-side from the JWT in
/// FinlyticBackend. Only ever set when is -
/// the autonomous scanner never carries a user identity, so this stays for every
/// row.
///
public Guid? TriggeredByUserId { get; set; }
///
/// Classifies why this evaluation did or did not produce a proposal. See
/// TradeLifecycleService.DetermineOutcomeReason for the exact priority order used when multiple
/// gates failed at once.
///
public OutcomeReason OutcomeReason { get; set; } = OutcomeReason.Unknown;
///
/// The EngineTradeProposalEntity.Id created by this evaluation, set if and only if
/// is . for every
/// rejected/no-setup evaluation - a proposal was never fabricated for those (Rules.md §4).
///
public Guid? ProposalId { get; set; }
[Required]
public DateTime EvaluatedAtUtc { get; set; } = DateTime.UtcNow;
}