///|
/// Severity of a station or option validation issue.
pub enum ValidationLevel {
  Error
  Warning
  Info
} derive(Eq, Debug)

///|
/// One validation issue.
pub struct ValidationIssue {
  level : ValidationLevel
  field : String
  message : String
} derive(Debug)

///|
fn issue(
  level : ValidationLevel,
  field : String,
  message : String,
) -> ValidationIssue {
  { level, field, message, }
}

///|
/// Validate station coordinates.
pub fn validate_coordinates(
  lat : Double,
  lon : Double,
) -> Array[ValidationIssue] {
  let issues = Array(capacity=4)
  if lat < -90.0 || lat > 90.0 {
    issues.push(
      issue(ValidationLevel::Error, "lat", "纬度必须在 -90 到 90 之间"),
    )
  }
  if lon < -180.0 || lon > 180.0 {
    issues.push(
      issue(
        ValidationLevel::Error,
        "lon",
        "经度必须在 -180 到 180 之间",
      ),
    )
  }
  if lat > 66.5 || lat < -66.5 {
    issues.push(
      issue(
        ValidationLevel::Warning,
        "lat",
        "极区日照模型可能不适用",
      ),
    )
  }
  issues
}

///|
/// Validate station datum.
pub fn validate_datum(datum : Double) -> Array[ValidationIssue] {
  if datum < -100.0 || datum > 100.0 {
    [
      issue(
        ValidationLevel::Error,
        "datum",
        "基准面偏移超出合理范围",
      ),
    ]
  } else if datum != 0.0 {
    [
      issue(
        ValidationLevel::Info,
        "datum",
        "非零基准面将直接改变报告中的绝对潮高",
      ),
    ]
  } else {
    []
  }
}

///|
/// Validate one harmonic component.
pub fn validate_harmonic(harmonic : Harmonic) -> Array[ValidationIssue] {
  let issues = Array(capacity=3)
  if harmonic.amplitude < 0.0 {
    issues.push(
      issue(
        ValidationLevel::Error,
        harmonic.constituent.name,
        "振幅不能为负数",
      ),
    )
  }
  if harmonic.amplitude > 20.0 {
    issues.push(
      issue(
        ValidationLevel::Warning,
        harmonic.constituent.name,
        "振幅异常大,请检查单位",
      ),
    )
  }
  if harmonic.phase_lag < 0.0 || harmonic.phase_lag >= 360.0 {
    issues.push(
      issue(
        ValidationLevel::Warning,
        harmonic.constituent.name,
        "迟角建议归一化到 0 到 360 度",
      ),
    )
  }
  issues
}

///|
/// Validate a station and all of its harmonic components.
pub fn validate_station(station : Station) -> Array[ValidationIssue] {
  let issues = Array(capacity=station.components.length() + 8)
  issues.append(validate_coordinates(station.lat, station.lon))
  issues.append(validate_datum(station.datum))
  if station.components.is_empty() {
    issues.push(
      issue(
        ValidationLevel::Error,
        "components",
        "站点至少需要一个分潮",
      ),
    )
  }
  let mut has_m2 = false
  let mut has_s2 = false
  for harmonic in station.components {
    issues.append(validate_harmonic(harmonic))
    if harmonic.constituent.name == "M2" {
      has_m2 = true
    }
    if harmonic.constituent.name == "S2" {
      has_s2 = true
    }
  }
  if !has_m2 {
    issues.push(
      issue(
        ValidationLevel::Warning,
        "M2",
        "缺少 M2 会影响半日潮精度",
      ),
    )
  }
  if !has_s2 {
    issues.push(
      issue(
        ValidationLevel::Warning,
        "S2",
        "缺少 S2 会影响大潮小潮变化",
      ),
    )
  }
  issues
}

///|
/// Validate a UTC offset.
pub fn validate_timezone(tz : Double) -> Array[ValidationIssue] {
  if tz < -12.0 || tz > 14.0 {
    [
      issue(
        ValidationLevel::Warning,
        "tz",
        "UTC 偏移超出常见范围,请确认单位是小时",
      ),
    ]
  } else {
    []
  }
}

///|
/// Validate a prediction window in hours.
pub fn validate_hours(hours : Double) -> Array[ValidationIssue] {
  if hours <= 0.0 {
    [issue(ValidationLevel::Error, "hours", "预测时长必须大于 0")]
  } else if hours > 24.0 * 31.0 {
    [
      issue(
        ValidationLevel::Warning,
        "hours",
        "预测时长超过 31 天,报告可能过长",
      ),
    ]
  } else {
    []
  }
}

///|
/// Return true when no error-level issues exist.
pub fn is_valid(issues : Array[ValidationIssue]) -> Bool {
  !issues.any(item => item.level == ValidationLevel::Error)
}