// Copyright (C) 2026 Jason M. Schwefel. All Rights Reserved.
package com.coldboreballisticsllc.scopedope.data.session

import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
data class SessionCreatedBy(
    val company:  String = "ColdBoreBallistics",
    val product:  String = "ScopeDOPE",
    val platform: String = "android",
    val version:  String = "",
)

/**
 * On-disk representation of a saved session (.dope file).
 * Cross-platform JSON format — do not rename fields without a version bump.
 * Increment [CURRENT_VERSION] on any breaking change and add a migration
 * in [DopeSessionSerializer].
 */
@Serializable
data class DopeSession(
    @SerialName("\$schema")
    val schema:                 String?          = "https://raw.githubusercontent.com/ColdBoreBallistics/dope-format/main/schema/dope-v3.schema.json",
    val version:                Int              = CURRENT_VERSION,
    @SerialName("created_by")
    val createdBy:              SessionCreatedBy = SessionCreatedBy(),
    @SerialName("session_uuid")
    val sessionUuid:            String           = "",
    @SerialName("saved_at")
    val savedAt:                String           = "",
    val name:                   String           = "",
    @SerialName("label_format_id")
    val labelFormatId:          String?          = null,
    @SerialName("label_selected_distances_m")
    val labelSelectedDistancesM: List<Double>    = emptyList(),
    val ammo:                   SessionAmmo?         = null,
    val rifle:                  SessionRifle?        = null,
    val scope:                  SessionScope?        = null,
    val conditions:             SessionConditions?   = null,
    @SerialName("dope_entries")
    val dopeEntries:            List<SessionDope>    = emptyList(),
    @SerialName("output_distances_m")
    val outputDistancesM:       List<Double>         = emptyList(),
    @SerialName("unit_prefs")
    val unitPrefs:              SessionUnitPrefs?    = null,
) {
    companion object {
        const val CURRENT_VERSION = 3
    }
}

@Serializable
data class SessionAmmo(
    val caliber:                      String  = "",
    val name:                         String  = "",
    val manufacturer:                 String  = "",
    @SerialName("lot_number")
    val lotNumber:                    String  = "",
    @SerialName("muzzle_vel_mps")
    val muzzleVelMps:                 Double  = 0.0,
    val bc:                           Double  = 0.0,
    @SerialName("bc_model")
    val bcModel:                      String  = "G7",
    @SerialName("bc_g1")
    val bcG1:                         Double  = 0.0,
    @SerialName("bc_g7")
    val bcG7:                         Double  = 0.0,
    @SerialName("form_factor")
    val formFactor:                   Double  = 0.0,
    @SerialName("bullet_diameter_mm")
    val bulletDiameterMm:             Double  = 0.0,
    @SerialName("bullet_length_mm")
    val bulletLengthMm:               Double  = 0.0,
    @SerialName("bullet_weight_g")
    val bulletWeightG:                Double  = 0.0,
    @SerialName("mv_sensitivity_mps_per_c")
    val mvSensitivityMpsPerC:         Double  = 0.0,
    @SerialName("mv_reference_temp_c")
    val mvReferenceTempC:             Double  = 15.0,
)

@Serializable
data class SessionRifle(
    val name:              String  = "",
    val brand:             String  = "",
    val model:             String  = "",
    @SerialName("zero_m")
    val zeroM:             Double  = 91.44,
    @SerialName("sight_height_mm")
    val sightHeightMm:     Double  = 38.1,
    @SerialName("twist_rate_mm")
    val twistRateMm:       Double  = 0.0,
    @SerialName("twist_direction")
    val twistDirection:    String  = "RH",
    @SerialName("zero_temperature_c")
    val zeroTemperatureC:  Double? = null,
    @SerialName("zero_altitude_m")
    val zeroAltitudeM:     Double? = null,
    @SerialName("zero_pressure_hpa")
    val zeroPressureHpa:   Double? = null,
)

@Serializable
data class SessionScope(
    val name:  String = "",
    val brand: String = "",
    val model: String = "",
)

@Serializable
data class SessionConditions(
    @SerialName("altitude_m")
    val altitudeM:          Double = 0.0,
    @SerialName("temperature_c")
    val temperatureC:       Double = 15.0,
    @SerialName("pressure_hpa")
    val pressureHpa:        Double = 1013.25,
    @SerialName("humidity_pct")
    val humidityPct:        Double = 50.0,
    @SerialName("wind_speed_mps")
    val windSpeedMps:       Double = 0.0,
    @SerialName("wind_angle_deg")
    val windAngleDeg:       Double = 90.0,
    @SerialName("firing_azimuth_deg")
    val firingAzimuthDeg:   Double = 0.0,
    @SerialName("firing_angle_deg")
    val firingAngleDeg:         Double  = 0.0,
    @SerialName("conditions_source")
    val conditionsSource:       String  = "manual",
    @SerialName("use_coriolis")
    val useCoriolis:            Boolean = false,
    @SerialName("coriolis_latitude_deg")
    val coriolisLatitudeDeg:    Double  = 0.0,
)

@Serializable
data class SerializedConditionsAtMeasurement(
    @SerialName("altitude_m")
    val altitudeM:      Double? = null,
    @SerialName("temperature_c")
    val temperatureC:   Double? = null,
    @SerialName("pressure_hpa")
    val pressureHpa:    Double? = null,
    @SerialName("humidity_pct")
    val humidityPct:    Double? = null,
    @SerialName("wind_speed_mps")
    val windSpeedMps:   Double? = null,
    @SerialName("wind_angle_deg")
    val windAngleDeg:   Double? = null,
)

@Serializable
data class SessionDope(
    @SerialName("distance_m")
    val distanceM:         Double,
    @SerialName("elevation_mil")
    val elevationMil:      Double,
    // Reserved for future observed-windage DOPE tracking; always 0.0 until implemented.
    @SerialName("windage_mil")
    val windageMil:        Double  = 0.0,
    @SerialName("recorded_at")
    val recordedAt:        String? = null,
    @SerialName("conditions_at_measurement")
    val conditionsAtMeasurement: SerializedConditionsAtMeasurement? = null,
)

@Serializable
data class SessionUnitPrefs(
    val system:            String = "us",
    val distance:          String = "yards",
    val adjustment:        String = "mil",
    @SerialName("click_value")
    val clickValue:        String = "0.1_mrad",
)
