namespace PowderCoating.Application.DTOs.AI;
/// Request payload sent from JS wizard to the AiAnalyzeItem endpoint.
public class AiAnalyzeItemRequest
{
/// TempIds of already-uploaded photos.
public List PhotoTempIds { get; set; } = new();
// User-supplied context
public string ReferenceDimension { get; set; } = string.Empty; // e.g. "longest edge is 18 inches"
public int Quantity { get; set; } = 1;
public string DesiredColor { get; set; } = string.Empty;
public int CoatCount { get; set; } = 1;
/// Material type — helps AI factor in prep, outgassing, cure time (e.g. "Cast Iron", "Aluminum").
public string? MaterialType { get; set; }
/// Approximate weight in lbs per piece — used to factor heavy-handling surcharge into time estimate.
public decimal? EstimatedWeightLbs { get; set; }
// Follow-up support (null on first call)
public List? ConversationHistory { get; set; }
public string? FollowUpAnswer { get; set; }
public int CompanyId { get; set; }
///
/// Id of the the user selected before analyzing.
/// When set, the controller loads that setup and passes it to the AI service so the
/// correct blast rate is injected into the prompt.
///
public int? BlastSetupId { get; set; }
}
public class AiConversationTurn
{
public string Role { get; set; } = string.Empty; // "user" | "assistant"
public string Content { get; set; } = string.Empty;
}
/// Result returned to the JS wizard from AI analysis.
public class AiAnalyzeItemResult
{
public bool Success { get; set; }
public string? ErrorMessage { get; set; }
// When NeedsFollowUp is true, the wizard shows FollowUpQuestion to the user
public bool NeedsFollowUp { get; set; }
public string? FollowUpQuestion { get; set; }
public int FollowUpRound { get; set; } // 1 or 2
// Final item data (populated when NeedsFollowUp is false)
public string? Description { get; set; }
public decimal SurfaceAreaSqFt { get; set; }
public string Complexity { get; set; } = "Moderate"; // Simple|Moderate|Complex|Extreme
public int EstimatedMinutes { get; set; }
public string? AiReasoning { get; set; }
public string Confidence { get; set; } = "Medium"; // Low|Medium|High
// Pricing preview (filled by server using company operating costs)
public decimal EstimatedUnitPrice { get; set; }
public decimal EstimatedTotal { get; set; }
public decimal PowderCostPerLb { get; set; }
public decimal CoverageSqFtPerLb { get; set; } = 30m;
public decimal TransferEfficiency { get; set; } = 65m;
// Raw conversation to pass back for follow-up round 2
public List ConversationHistory { get; set; } = new();
// AI-generated standardized tags from fixed taxonomy
public List Tags { get; set; } = new();
// Historical price benchmark from completed jobs
public AiBenchmarkResult? Benchmark { get; set; }
// ID of the persisted AiItemPrediction record — returned on final result (not follow-up rounds).
// The JS wizard stores this per-item so the controller can link prediction to QuoteItem/JobItem on save.
public int? AiPredictionId { get; set; }
// Pricing breakdown — helps users understand and sanity-check the estimate
public AiPricingBreakdown? Breakdown { get; set; }
}
///
/// Display-only cost breakdown returned to the client.
/// Contains computed dollar amounts only — rates, percentages, and coverage constants are intentionally excluded.
///
public class AiPricingBreakdown
{
public decimal SurfaceAreaSqFt { get; set; }
public decimal PowderLbsPerCoat { get; set; }
public int CoatCount { get; set; }
public decimal MaterialCost { get; set; }
public decimal ConsumablesCost { get; set; }
public int EstimatedMinutes { get; set; }
/// Non-zero when a material-type minimum floor exists for this material.
public int MaterialMinMinutes { get; set; }
/// True when the floor was actually applied (AI returned less than the floor).
public bool MinFloorApplied { get; set; }
public decimal LaborCost { get; set; }
public int OvenCycleMinutes { get; set; }
public decimal OvenCost { get; set; }
public bool RequiresPreheat { get; set; }
public int PreheatMinutes { get; set; }
public decimal PreheatCost { get; set; }
public decimal SubtotalBeforeComplexity { get; set; }
public string Complexity { get; set; } = string.Empty;
public decimal ComplexityCharge { get; set; }
public decimal SubtotalBeforeMarkup { get; set; }
public decimal MarkupAmount { get; set; }
public decimal UnitPrice { get; set; }
}
/// Request to recalculate AI item price when the user overrides sq ft, minutes, or complexity.
public class AiRecalcPriceRequest
{
public decimal SurfaceAreaSqFt { get; set; }
public int EstimatedMinutes { get; set; }
public string Complexity { get; set; } = "Moderate";
public int CoatCount { get; set; } = 1;
}
/// Result of a price recalculation — returns the new unit price and a display-safe breakdown.
public class AiRecalcPriceResult
{
public bool Success { get; set; }
public decimal UnitPrice { get; set; }
public AiPricingBreakdown? Breakdown { get; set; }
}
/// Historical price benchmark from completed jobs with similar complexity and size.
public class AiBenchmarkResult
{
public int MatchCount { get; set; }
public decimal MinPrice { get; set; }
public decimal MaxPrice { get; set; }
public decimal AvgPrice { get; set; }
public string ComplexityLevel { get; set; } = string.Empty;
public decimal SqFtRangeMin { get; set; }
public decimal SqFtRangeMax { get; set; }
}
///
/// Company-specific AI context passed to the quote service on every analysis call.
/// Contains optional free-text profile and recent accepted predictions as few-shot calibration examples.
///
public class CompanyAiContext
{
/// Free-text description of the shop's specialties and pricing style (from Company Settings).
public string? ProfileText { get; set; }
/// Recent predictions the user accepted without override — used as few-shot calibration examples.
public List AcceptedExamples { get; set; } = new();
}
/// A single accepted-prediction record formatted for AI few-shot context.
public class AiFewShotExample
{
public string Description { get; set; } = string.Empty;
public decimal SurfaceAreaSqFt { get; set; }
public string Complexity { get; set; } = string.Empty;
public int EstimatedMinutes { get; set; }
public decimal FinalUnitPrice { get; set; }
public string? Tags { get; set; }
}
/// Internal structured response that Claude returns as JSON.
public class ClaudeQuoteResponse
{
public string Description { get; set; } = string.Empty;
public decimal SurfaceAreaSqFt { get; set; }
public string Complexity { get; set; } = "Moderate";
public int EstimatedMinutes { get; set; }
public bool RequiresPreheat { get; set; }
public int PreheatMinutes { get; set; }
public string Confidence { get; set; } = "Medium";
public bool NeedsFollowUp { get; set; }
public string? FollowUpQuestion { get; set; }
public string Reasoning { get; set; } = string.Empty;
public List Tags { get; set; } = new();
}