///|
/// A compact, reusable identity card for one CSV dataset.
///
/// The passport combines structural metadata, data-quality scoring, schema
/// inference, recommendations, and a stable shape fingerprint into one artifact
/// that can be stored in docs, release notes, or CI logs.
pub(all) struct CsvDataPassport {
  name : String
  fingerprint : String
  dialect : String
  rows : Int
  columns : Int
  cells : Int
  missing_cells : Int
  parse_issues : Int
  quality_issues : Int
  required_columns : Int
  optional_columns : Int
  numeric_columns : Int
  boolean_columns : Int
  text_columns : Int
  unique_columns : Int
  score : CsvAuditScore
  schema : Array[CsvInferredColumnRule]
  recommendations : Array[CsvAuditRecommendation]
} derive(Eq, Debug)

///|
/// Build a data passport from CSV text using automatic dialect detection.
pub fn csv_data_passport(input : String, name : String) -> CsvDataPassport {
  csv_data_passport_from_audit(audit_csv(input), name)
}

///|
/// Build a data passport from an existing audit report.
pub fn csv_data_passport_from_audit(
  audit : CsvAuditReport,
  name : String,
) -> CsvDataPassport {
  let score = audit_quality_score(audit)
  let schema = audit.inferred_schema
  {
    name,
    fingerprint: passport_fingerprint(audit.table),
    dialect: dialect_name(audit.dialect),
    rows: audit.table.rows.length(),
    columns: audit.table.headers.length(),
    cells: audit.table.rows.length() * audit.table.headers.length(),
    missing_cells: score.missing_cells,
    parse_issues: audit.parse_issues.length(),
    quality_issues: audit.quality_issues.length(),
    required_columns: passport_required_columns(schema),
    optional_columns: passport_optional_columns(schema),
    numeric_columns: passport_numeric_columns(schema),
    boolean_columns: passport_boolean_columns(schema),
    text_columns: passport_text_columns(schema),
    unique_columns: passport_unique_columns(schema),
    score,
    schema,
    recommendations: audit_recommendations(audit),
  }
}

///|
/// Render a CSV data passport as Markdown.
pub fn csv_data_passport_markdown(input : String, name : String) -> String {
  csv_data_passport_report_markdown(csv_data_passport(input, name))
}

///|
/// Render a CSV data passport as compact JSON.
pub fn csv_data_passport_json(input : String, name : String) -> String {
  csv_data_passport_report_json(csv_data_passport(input, name))
}

///|
/// Render a prepared passport model as Markdown.
pub fn csv_data_passport_report_markdown(passport : CsvDataPassport) -> String {
  let out = StringBuilder()
  out.write_string("# CSV Data Passport\n\n")
  out.write_string("## Identity\n\n")
  out.write_string("- Name: ")
  passport_write_markdown_cell(out, passport.name)
  out.write_char('\n')
  out.write_string("- Fingerprint: `")
  passport_write_markdown_cell(out, passport.fingerprint)
  out.write_string("`\n")
  out.write_string("- Dialect: ")
  passport_write_markdown_cell(out, passport.dialect)
  out.write_char('\n')
  out.write_string("- Rows: \{passport.rows}\n")
  out.write_string("- Columns: \{passport.columns}\n")
  out.write_string("- Cells: \{passport.cells}\n")
  out.write_string("\n## Quality\n\n")
  out.write_string(
    "- Score: **\{passport.score.score}** (\{passport.score.grade})\n",
  )
  out.write_string("- Risk: \{passport.score.risk}\n")
  out.write_string(
    "- Missing cells: \{passport.missing_cells}/\{passport.cells}\n",
  )
  out.write_string("- Parse issues: \{passport.parse_issues}\n")
  out.write_string("- Table quality issues: \{passport.quality_issues}\n")
  out.write_string("\n## Schema Summary\n\n")
  out.write_string("| metric | value |\n")
  out.write_string("| --- | ---: |\n")
  out.write_string("| required columns | \{passport.required_columns} |\n")
  out.write_string("| optional columns | \{passport.optional_columns} |\n")
  out.write_string("| numeric columns | \{passport.numeric_columns} |\n")
  out.write_string("| boolean columns | \{passport.boolean_columns} |\n")
  out.write_string("| text columns | \{passport.text_columns} |\n")
  out.write_string("| fully unique columns | \{passport.unique_columns} |\n")
  out.write_string("\n## Columns\n\n")
  passport_write_schema_markdown(out, passport.schema)
  out.write_string("\n## Recommendations\n\n")
  for item in passport.recommendations {
    out.write_string("- **")
    passport_write_markdown_cell(out, item.severity)
    out.write_string("**: ")
    passport_write_markdown_cell(out, item.message)
    out.write_char('\n')
  }
  out.to_string()
}

///|
/// Render a prepared passport model as compact JSON.
pub fn csv_data_passport_report_json(passport : CsvDataPassport) -> String {
  let out = StringBuilder()
  out.write_char('{')
  out.write_string("\"name\":")
  passport_write_json_string(out, passport.name)
  out.write_string(",\"fingerprint\":")
  passport_write_json_string(out, passport.fingerprint)
  out.write_string(",\"dialect\":")
  passport_write_json_string(out, passport.dialect)
  out.write_string(",\"summary\":{")
  out.write_string("\"rows\":\{passport.rows}")
  out.write_string(",\"columns\":\{passport.columns}")
  out.write_string(",\"cells\":\{passport.cells}")
  out.write_string(",\"missing_cells\":\{passport.missing_cells}")
  out.write_string(",\"parse_issues\":\{passport.parse_issues}")
  out.write_string(",\"quality_issues\":\{passport.quality_issues}")
  out.write_char('}')
  out.write_string(",\"score\":")
  passport_write_score_json(out, passport.score)
  out.write_string(",\"schema_summary\":{")
  out.write_string("\"required_columns\":\{passport.required_columns}")
  out.write_string(",\"optional_columns\":\{passport.optional_columns}")
  out.write_string(",\"numeric_columns\":\{passport.numeric_columns}")
  out.write_string(",\"boolean_columns\":\{passport.boolean_columns}")
  out.write_string(",\"text_columns\":\{passport.text_columns}")
  out.write_string(",\"unique_columns\":\{passport.unique_columns}")
  out.write_char('}')
  out.write_string(",\"schema\":")
  passport_write_schema_json(out, passport.schema)
  out.write_string(",\"recommendations\":")
  passport_write_recommendations_json(out, passport.recommendations)
  out.write_char('}')
  out.to_string()
}

///|
fn passport_fingerprint(table : CsvTable) -> String {
  let mut checksum = 17
  checksum = passport_roll(checksum, table.headers.length())
  checksum = passport_roll(checksum, table.rows.length())
  for i in 0.. Int {
  (seed * 131 + value + 17) % 1000003
}

///|
fn passport_roll_string_shape(seed : Int, value : String, salt : Int) -> Int {
  let mut next = passport_roll(seed, passport_string_length(value) + salt)
  if value.is_empty() {
    next = passport_roll(next, 1)
  } else {
    next = passport_roll(next, 7)
  }
  next
}

///|
fn passport_string_length(value : String) -> Int {
  let mut count = 0
  for _ in value.iter() {
    count += 1
  }
  count
}

///|
fn passport_required_columns(schema : Array[CsvInferredColumnRule]) -> Int {
  let mut count = 0
  for rule in schema {
    if rule.required {
      count += 1
    }
  }
  count
}

///|
fn passport_optional_columns(schema : Array[CsvInferredColumnRule]) -> Int {
  schema.length() - passport_required_columns(schema)
}

///|
fn passport_numeric_columns(schema : Array[CsvInferredColumnRule]) -> Int {
  let mut count = 0
  for rule in schema {
    match rule.kind {
      Integer | Float => count += 1
      _ => ()
    }
  }
  count
}

///|
fn passport_boolean_columns(schema : Array[CsvInferredColumnRule]) -> Int {
  let mut count = 0
  for rule in schema {
    match rule.kind {
      Boolean => count += 1
      _ => ()
    }
  }
  count
}

///|
fn passport_text_columns(schema : Array[CsvInferredColumnRule]) -> Int {
  let mut count = 0
  for rule in schema {
    match rule.kind {
      Text => count += 1
      _ => ()
    }
  }
  count
}

///|
fn passport_unique_columns(schema : Array[CsvInferredColumnRule]) -> Int {
  let mut count = 0
  for rule in schema {
    if rule.total > 0 && rule.unique == rule.total {
      count += 1
    }
  }
  count
}

///|
fn passport_write_schema_markdown(
  out : StringBuilder,
  schema : Array[CsvInferredColumnRule],
) -> Unit {
  out.write_string("| column | type | required | empty | unique | examples |\n")
  out.write_string("| --- | --- | --- | ---: | ---: | --- |\n")
  for rule in schema {
    out.write_string("| ")
    passport_write_markdown_cell(out, rule.name)
    out.write_string(" | ")
    out.write_string(passport_column_type_name(rule.kind))
    out.write_string(" | ")
    out.write_string(if rule.required { "yes" } else { "no" })
    out.write_string(" | \{rule.empty}/\{rule.total} | \{rule.unique} | ")
    passport_write_markdown_cell(out, passport_join(rule.examples, ", "))
    out.write_string(" |\n")
  }
}

///|
fn passport_write_score_json(
  out : StringBuilder,
  score : CsvAuditScore,
) -> Unit {
  out.write_char('{')
  out.write_string("\"score\":\{score.score}")
  out.write_string(",\"grade\":")
  passport_write_json_string(out, score.grade)
  out.write_string(",\"risk\":")
  passport_write_json_string(out, score.risk)
  out.write_string(",\"structure_score\":\{score.structure_score}")
  out.write_string(",\"completeness_score\":\{score.completeness_score}")
  out.write_string(",\"consistency_score\":\{score.consistency_score}")
  out.write_string(",\"uniqueness_score\":\{score.uniqueness_score}")
  out.write_string(",\"issue_penalty\":\{score.issue_penalty}")
  out.write_string(",\"missing_cells\":\{score.missing_cells}")
  out.write_string(",\"total_cells\":\{score.total_cells}")
  out.write_char('}')
}

///|
fn passport_write_schema_json(
  out : StringBuilder,
  schema : Array[CsvInferredColumnRule],
) -> Unit {
  out.write_char('[')
  for i in 0.. 0 {
      out.write_char(',')
    }
    let rule = schema[i]
    out.write_char('{')
    out.write_string("\"column\":")
    passport_write_json_string(out, rule.name)
    out.write_string(",\"type\":")
    passport_write_json_string(out, passport_column_type_name(rule.kind))
    out.write_string(",\"required\":")
    passport_write_json_bool(out, rule.required)
    out.write_string(",\"total\":\{rule.total}")
    out.write_string(",\"empty\":\{rule.empty}")
    out.write_string(",\"unique\":\{rule.unique}")
    out.write_string(",\"examples\":")
    passport_write_string_array_json(out, rule.examples)
    out.write_char('}')
  }
  out.write_char(']')
}

///|
fn passport_write_recommendations_json(
  out : StringBuilder,
  recommendations : Array[CsvAuditRecommendation],
) -> Unit {
  out.write_char('[')
  for i in 0.. 0 {
      out.write_char(',')
    }
    let item = recommendations[i]
    out.write_char('{')
    out.write_string("\"severity\":")
    passport_write_json_string(out, item.severity)
    out.write_string(",\"message\":")
    passport_write_json_string(out, item.message)
    out.write_char('}')
  }
  out.write_char(']')
}

///|
fn passport_write_string_array_json(
  out : StringBuilder,
  values : Array[String],
) -> Unit {
  out.write_char('[')
  for i in 0.. 0 {
      out.write_char(',')
    }
    passport_write_json_string(out, values[i])
  }
  out.write_char(']')
}

///|
fn passport_column_type_name(kind : CsvColumnType) -> String {
  match kind {
    Text => "text"
    Integer => "integer"
    Float => "float"
    Boolean => "boolean"
  }
}

///|
fn passport_cell_at(row : Array[String], index : Int) -> String {
  if index >= 0 && index < row.length() {
    row[index]
  } else {
    ""
  }
}

///|
fn passport_join(values : Array[String], separator : String) -> String {
  let out = StringBuilder()
  for i in 0.. 0 {
      out.write_string(separator)
    }
    out.write_string(values[i])
  }
  out.to_string()
}

///|
fn passport_write_json_bool(out : StringBuilder, value : Bool) -> Unit {
  out.write_string(if value { "true" } else { "false" })
}

///|
fn passport_write_json_string(out : StringBuilder, value : String) -> Unit {
  out.write_char('"')
  for ch in value.iter() {
    if ch == '"' {
      out.write_string("\\\"")
    } else if ch == '\\' {
      out.write_string("\\\\")
    } else if ch == '\n' {
      out.write_string("\\n")
    } else if ch == '\r' {
      out.write_string("\\r")
    } else if ch == '\t' {
      out.write_string("\\t")
    } else {
      out.write_char(ch)
    }
  }
  out.write_char('"')
}

///|
fn passport_write_markdown_cell(out : StringBuilder, value : String) -> Unit {
  for ch in value.iter() {
    if ch == '|' {
      out.write_string("\\|")
    } else if ch == '\n' || ch == '\r' {
      out.write_char(' ')
    } else {
      out.write_char(ch)
    }
  }
}