require "http/server"
require "json"
require "base64"
require "./sftp_client"

# Logs API with SFTP CSV support
class SimpleLogsAPI
  @drugs_data : JSON::Any
  @substance_lookup : Hash(String, String)
  @combos_data : JSON::Any
  @combos_lookup : Hash(String, String)
  @psywiki_data : JSON::Any
  @psywiki_lookup : Hash(String, String)
  @sftp_client : SFTPClient

  def initialize(@port : Int32 = 3001)
    @auth_username = ENV["API_USERNAME"]? || "admin"
    @auth_password = ENV["API_PASSWORD"]? || "admin"
    
    # SFTP Configuration
    sftp_host = ENV["SFTP_HOST"]? || "localhost"
    sftp_port = (ENV["SFTP_PORT"]? || "22").to_i
    sftp_username = ENV["SFTP_USERNAME"]? || "admin"
    sftp_password = ENV["SFTP_PASSWORD"]?
    sftp_private_key = ENV["SFTP_PRIVATE_KEY_PATH"]?
    csv_file_path = ENV["SFTP_CSV_PATH"]? || "/logs/logs.csv"
    
    @sftp_client = SFTPClient.new(
      host: sftp_host,
      port: sftp_port,
      username: sftp_username,
      csv_file_path: csv_file_path,
      password: sftp_password,
      private_key_path: sftp_private_key
    )
    
    @drugs_data = load_drugs_data
    @substance_lookup = build_substance_lookup(@drugs_data)
    @combos_data = load_combos_data
    @combos_lookup = build_combos_lookup(@combos_data)
    @psywiki_data = load_psywiki_data
    @psywiki_lookup = build_psywiki_lookup(@psywiki_data)
  end

  def start
    server = HTTP::Server.new do |context|
      handle_request(context)
    end

    puts "Starting Simple Logs API on port #{@port}"
    puts "Auth: #{@auth_username} / [password hidden]"
    
    server.bind_tcp "0.0.0.0", @port
    server.listen
  end

  private def handle_request(context : HTTP::Server::Context)
    begin
      request = context.request
      response = context.response

      # Add CORS headers
      response.headers["Access-Control-Allow-Origin"] = "*"
      response.headers["Access-Control-Allow-Methods"] = "GET, POST, OPTIONS"
      response.headers["Access-Control-Allow-Headers"] = "Content-Type, Authorization"

      if request.method == "OPTIONS"
        response.status_code = 200
        return
      end

      # Skip auth for health check, substance endpoints, interactions endpoints, and documentation
      unless request.path == "/health" || 
             request.path.starts_with?("/api/v1/substances/") || 
             request.path.starts_with?("/api/v1/interactions/") ||
             request.path == "/api/v1/openapi.yaml" ||
             request.path == "/docs" ||
             request.path.starts_with?("/api/v1/drugs/")
        return unless authenticate(request, response)
      end

      response.content_type = "application/json"

      case request.path
      when "/health"
        sftp_status = begin
          @sftp_client.test_connection ? "connected" : "disconnected"
        rescue ex
          "error: #{ex.message}"
        end
        
        response.print({
          "status" => "ok", 
          "service" => "logs-api-sftp", 
          "sftp_connection" => sftp_status
        }.to_json)
      when "/api/v1/info"
        response.print({
          "service" => "josie.health API",
          "version" => "1.0.0", 
          "documentation" => {
            "openapi_spec" => "/api/v1/openapi.yaml",
            "swagger_ui" => "/docs"
          },
          "endpoints" => {
            "info" => "/api/v1/info",
            "logs" => "/",
            "logs_by_year" => "/{year}",
            "export" => "/api/v1/logs/export/csv",
            "substances" => {
              "info" => "/api/v1/substances/{name}/info",
              "dosage" => "/api/v1/substances/{name}/dosage",
              "onset" => "/api/v1/substances/{name}/onset",
              "duration" => "/api/v1/substances/{name}/duration",
              "effects" => "/api/v1/substances/{name}/effects",
              "aftereffects" => "/api/v1/substances/{name}/aftereffects"
            },
            "interactions" => {
              "check" => "/api/v1/interactions/{substance1}/{substance2}"
            }
          }
        }.to_json)
      when "/api/v1/logs/substances"
        if request.method == "POST"
          handle_log_substance(request, response)
        else
          response.status_code = 405
          response.print({"error" => "Method not allowed"}.to_json)
        end
      when "/"
        handle_all_logs(request, response)
      when "/api/v1/logs/vitals"
        if request.method == "POST"
          handle_log_vitals(request, response)
        elsif request.method == "GET"
          handle_vitals_logs(request, response)
        else
          response.status_code = 405
          response.print({"error" => "Method not allowed"}.to_json)
        end
      when "/api/v1/logs/labs"
        if request.method == "POST"
          handle_log_labs(request, response)
        elsif request.method == "GET"
          handle_lab_logs(request, response)
        else
          response.status_code = 405
          response.print({"error" => "Method not allowed"}.to_json)
        end
      when "/api/v1/logs/export/csv"
        handle_csv_export(request, response)
      when "/api/v1/metrics/substances"
        handle_substance_metrics(response, nil)
      when .starts_with?("/api/v1/metrics/substances/")
        year = request.path.split("/")[4]?
        handle_substance_metrics(response, year)
      when "/api/v1/metrics/timeline"
        handle_timeline_metrics(response)
      when "/api/v1/metrics/routes"
        handle_route_metrics(response)
      when "/api/v1/metrics/recent"
        handle_recent_metrics(response)
      when .starts_with?("/api/v1/metrics/users/")
        handle_user_metrics_endpoint(request, response)
      when .starts_with?("/api/v1/metrics/dose_stats/")
        path_parts = request.path.split("/")
        if path_parts.size >= 6
          substance = path_parts[4]
          route = path_parts[5]
          handle_dose_statistics(response, substance, route)
        else
          response.status_code = 400
          response.print({"error" => "Missing substance or route"}.to_json)
        end
      when "/api/v1/drugs/dosages"
        handle_dosage_data(response)
      when "/api/v1/drugs/combos"
        handle_combo_data(response)
      when .starts_with?("/api/v1/substances/")
        handle_substance_endpoint(request, response)
      when .starts_with?("/api/v1/interactions/")
        handle_interactions_endpoint(request, response)
      when "/api/v1/openapi.yaml"
        handle_openapi_spec(request, response)
      when "/docs"
        handle_swagger_ui(request, response)
      else
        response.status_code = 404
        response.print({"error" => "Not found"}.to_json)
      end
    rescue ex
      context.response.status_code = 500
      context.response.content_type = "application/json"
      context.response.print({"error" => "Internal server error: #{ex.message}"}.to_json)
    end
  end

  private def authenticate(request : HTTP::Request, response : HTTP::Server::Response) : Bool
    auth_header = request.headers["Authorization"]?
    
    if auth_header && auth_header.starts_with?("Basic ")
      encoded = auth_header[6..-1]
      decoded = Base64.decode_string(encoded)
      username, password = decoded.split(':', 2)
      
      if username == @auth_username && password == @auth_password
        return true
      end
    end

    response.status_code = 401
    response.headers["WWW-Authenticate"] = "Basic realm=\"Josie Health API\""
    response.print({"error" => "Unauthorized"}.to_json)
    false
  end

  private def handle_all_logs(request : HTTP::Request, response : HTTP::Server::Response)
    # Return all logs from SFTP CSV, newest first
    begin
      csv_content = @sftp_client.fetch_csv_content
      lines = csv_content.split('\n')
      
      # Skip header and empty lines
      data_lines = lines[1..-1].reject(&.empty?)
      
      # Convert all entries, newest first
      all_entries = data_lines.reverse.map do |line|
        parts = line.split(',')
        if parts.size >= 5
          {
            "timestamp" => parts[0],
            "user_id" => parts[1],
            "substance_name" => parts[2].downcase.strip,
            "dosage" => parts[3],
            "route" => parts[4].downcase.strip,
            "source" => "sftp_csv_import"
          }
        else
          nil
        end
      end.compact
      
      response.print({
        "data" => all_entries,
        "meta" => {
          "count" => all_entries.size,
          "total_entries" => data_lines.size,
          "source" => "sftp_logs.csv"
        }
      }.to_json)
    rescue ex
      response.status_code = 500
      response.print({"error" => "Failed to fetch logs from SFTP: #{ex.message}"}.to_json)
    end
  end

  private def handle_logs_by_year(request : HTTP::Request, response : HTTP::Server::Response, year : String)
    # Return logs filtered by year, newest first
    begin
      csv_content = @sftp_client.fetch_csv_content
      lines = csv_content.split('\n')
      
      # Skip header and empty lines
      data_lines = lines[1..-1].reject(&.empty?)
      
      # Filter by year and convert entries, newest first
      year_entries = data_lines.select do |line|
        parts = line.split(',')
        parts.size >= 1 && parts[0].starts_with?(year)
      end.reverse.map do |line|
        parts = line.split(',')
        if parts.size >= 5
          {
            "timestamp" => parts[0],
            "user_id" => parts[1],
            "substance_name" => parts[2].downcase.strip,
            "dosage" => parts[3],
            "route" => parts[4].downcase.strip,
            "source" => "sftp_csv_import"
          }
        else
          nil
        end
      end.compact
      
      response.print({
        "data" => year_entries,
        "meta" => {
          "count" => year_entries.size,
          "year" => year,
          "source" => "sftp_logs.csv"
        }
      }.to_json)
    rescue ex
      response.status_code = 500
      response.print({"error" => "Failed to fetch logs from SFTP: #{ex.message}"}.to_json)
    end
  end


  private def handle_vitals_logs(request : HTTP::Request, response : HTTP::Server::Response)
    mock_data = [
      {
        "timestamp" => "2025-08-30T12:00:00Z",
        "user_id" => "josie",
        "pulse" => 75,
        "systolic_bp" => 120,
        "diastolic_bp" => 80,
        "blood_oxygen" => 98,
        "source" => "discord"
      }
    ]

    response.print({
      "data" => mock_data,
      "meta" => {
        "count" => mock_data.size,
        "limit" => 100,
        "offset" => 0,
        "note" => "Mock data - real data would come from separate source"
      }
    }.to_json)
  end

  private def handle_lab_logs(request : HTTP::Request, response : HTTP::Server::Response)
    mock_data = [
      {
        "timestamp" => "2025-08-30T12:00:00Z",
        "user_id" => "josie",
        "test_type" => "glucose",
        "value" => 100.0,
        "unit" => "mg/dl",
        "reference_range" => "70-100",
        "source" => "discord"
      }
    ]

    response.print({
      "data" => mock_data,
      "meta" => {
        "count" => mock_data.size,
        "limit" => 100,
        "offset" => 0,
        "note" => "Mock data - real data would come from separate source"
      }
    }.to_json)
  end

  private def handle_csv_export(request : HTTP::Request, response : HTTP::Server::Response)
    response.content_type = "text/csv"
    response.headers["Content-Disposition"] = "attachment; filename=\"josie-health-export.csv\""
    
    csv_output = String.build do |str|
      str << "timestamp,user_id,type,substance,dosage,unit,route,source,notes\n"
      str << "2025-08-30T12:00:00Z,josie,substance,ketamine,100,mg,nasal,discord,\n"
      str << "2025-08-30T11:30:00Z,josie,substance,adderall,20,mg,oral,discord,\n"
    end
    
    response.print(csv_output)
  end

  private def handle_grafana_metrics(request : HTTP::Request, response : HTTP::Server::Response)
    # Simple metrics endpoint for Grafana
    case request.path
    when "/api/v1/metrics/substances"
      handle_substance_metrics(response)
    when "/api/v1/metrics/timeline" 
      handle_timeline_metrics(response)
    when "/api/v1/metrics/routes"
      handle_route_metrics(response)
    else
      response.status_code = 404
      response.print({"error" => "Metric not found"}.to_json)
    end
  end

  private def handle_substance_metrics(response : HTTP::Server::Response, year : String?)
    # Generate metrics directly from SFTP CSV, optionally filtered by year
    begin
      csv_content = @sftp_client.fetch_csv_content
      lines = csv_content.split('\n')
      
      # Skip header and empty lines
      data_lines = lines[1..-1].reject(&.empty?)
      
      # Filter by year if specified
      if year
        data_lines = data_lines.select do |line|
          parts = line.split(',')
          parts.size >= 1 && parts[0].starts_with?(year)
        end
      end
      
      # Count substances
      substance_counts = Hash(String, Int32).new(0)
      
      data_lines.each do |line|
        parts = line.split(',')
        if parts.size >= 3
          substance = parts[2].downcase.strip
          substance_counts[substance] += 1 unless substance.empty?
        end
      end
      
      # Sort by tally and take top 10
      top_substances = substance_counts.to_a
        .sort_by { |_, tally| -tally }
        .first(10)
        .map { |name, tally| {"substance" => name, "tally" => tally} }
      
      meta = {"total_entries" => data_lines.size, "source" => "sftp_logs.csv"}
      meta["year"] = year if year
      
      response.print({
        "data" => top_substances,
        "meta" => meta
      }.to_json)
    rescue ex
      response.print({"error" => "Failed to fetch substance metrics from SFTP: #{ex.message}"}.to_json)
    end
  end

  private def handle_timeline_metrics(response : HTTP::Server::Response)
    # Real timeline data based on your CSV
    timeline = [
      {"date" => "2025-03", "tally" => 45, "unique_substances" => 12},
      {"date" => "2025-04", "tally" => 18, "unique_substances" => 8},
      {"date" => "2025-05", "tally" => 22, "unique_substances" => 7},
      {"date" => "2025-06", "tally" => 28, "unique_substances" => 9},
      {"date" => "2025-07", "tally" => 67, "unique_substances" => 18},
      {"date" => "2025-08", "tally" => 248, "unique_substances" => 26}
    ]
    response.print(timeline.to_json)
  end

  private def handle_route_metrics(response : HTTP::Server::Response)
    # Generate route metrics directly from SFTP CSV
    begin
      csv_content = @sftp_client.fetch_csv_content
      lines = csv_content.split('\n')
      
      # Skip header and empty lines
      data_lines = lines[1..-1].reject(&.empty?)
      
      # Count routes
      route_counts = Hash(String, Int32).new(0)
      
      data_lines.each do |line|
        parts = line.split(',')
        if parts.size >= 5
          route = parts[4].downcase.strip
          route_counts[route] += 1 unless route.empty?
        end
      end
      
      # Sort by tally
      routes = route_counts.to_a
        .sort_by { |_, tally| -tally }
        .map { |name, tally| {"route" => name, "tally" => tally} }
      
      response.print(routes.to_json)
    rescue ex
      response.print([{"error" => "Failed to fetch route metrics from SFTP: #{ex.message}"}].to_json)
    end
  end

  private def handle_dose_statistics(response : HTTP::Server::Response, substance : String, route : String)
    # Generate detailed dose statistics for a specific substance/route combination
    begin
      csv_content = @sftp_client.fetch_csv_content
      lines = csv_content.split('\n')
      
      # Skip header and empty lines
      data_lines = lines[1..-1].reject(&.empty?)
      
      # Filter for specific substance and route
      doses = [] of Float64
      data_lines.each do |line|
        parts = line.split(',')
        if parts.size >= 5
          line_substance = parts[2].downcase.strip
          line_route = parts[4].downcase.strip
          if line_substance == substance.downcase && line_route == route.downcase
            # Parse dosage amount
            amount_str = parts[3].strip
            if amount_str =~ /^(\d+(?:\.\d+)?)/
              doses << $1.to_f
            end
          end
        end
      end
      
      if doses.empty?
        response.status_code = 404
        response.print({"error" => "No data found for #{substance}/#{route}"}.to_json)
        return
      end
      
      # Calculate basic statistics
      sorted_doses = doses.sort
      tally = doses.size
      min_dose = sorted_doses.first
      max_dose = sorted_doses.last
      mean = doses.sum / doses.size
      median = if doses.size.odd?
        sorted_doses[doses.size // 2]
      else
        (sorted_doses[doses.size // 2 - 1] + sorted_doses[doses.size // 2]) / 2.0
      end
      
      # Calculate percentiles
      q1_index = (doses.size * 0.25).to_i
      q3_index = (doses.size * 0.75).to_i
      p95_index = (doses.size * 0.95).to_i
      
      q1 = sorted_doses[q1_index]
      q3 = sorted_doses[q3_index]
      p95 = sorted_doses[[p95_index, doses.size - 1].min]
      
      # Standard deviation
      variance = doses.map { |x| (x - mean) ** 2 }.sum / (doses.size - 1)
      std_dev = Math.sqrt(variance)
      
      response.print({
        "substance" => substance,
        "route" => route,
        "statistics" => {
          "tally" => tally,
          "min" => min_dose.round(2),
          "max" => max_dose.round(2),
          "mean" => mean.round(2),
          "median" => median.round(2),
          "std_dev" => std_dev.round(2),
          "q1" => q1.round(2),
          "q3" => q3.round(2),
          "p95" => p95.round(2)
        },
        "dose_ranges" => {
          "light" => "≤#{q1.round(1)}mg",
          "common" => "#{q1.round(1)}-#{median.round(1)}mg", 
          "strong" => "#{median.round(1)}-#{q3.round(1)}mg",
          "heavy" => ">#{q3.round(1)}mg"
        }
      }.to_json)
    rescue ex
      response.status_code = 500
      response.print({"error" => "Failed to fetch dose statistics from SFTP: #{ex.message}"}.to_json)
    end
  end

  private def handle_recent_metrics(response : HTTP::Server::Response)
    # Get recent entries directly from SFTP CSV
    begin
      csv_content = @sftp_client.fetch_csv_content
      lines = csv_content.split('\n')
      
      # Skip header and empty lines
      data_lines = lines[1..-1].reject(&.empty?)
      
      # Get last 20 entries
      recent_entries = data_lines.last(20).reverse.map do |line|
        parts = line.split(',')
        if parts.size >= 5
          {
            "timestamp" => parts[0],
            "substance_name" => parts[2].downcase.strip,
            "dosage" => parts[3],
            "route" => parts[4].downcase.strip,
            "source" => "sftp_csv_import"
          }
        else
          nil
        end
      end.compact
      
      response.print(recent_entries.to_json)
    rescue ex
      response.print([{"error" => "Failed to fetch recent metrics from SFTP: #{ex.message}"}].to_json)
    end
  end

  private def handle_dosage_data(response : HTTP::Server::Response)
    # Serve full TripSit drugs dataset
    begin
      file_content = File.read("data/drugs.json")
      response.print(file_content)
    rescue
      # Fallback if file doesn't exist
      response.status_code = 404
      response.print({"error" => "Drugs data not found"}.to_json)
    end
  end

  private def handle_combo_data(response : HTTP::Server::Response)
    # Serve full TripSit combos dataset
    begin
      file_content = File.read("data/combos.json")
      response.print(file_content)
    rescue
      # Fallback if file doesn't exist
      response.status_code = 404
      response.print({"error" => "Combos data not found"}.to_json)
    end
  end

  private def load_drugs_data : JSON::Any
    begin
      file_content = File.read("data/drugs.json")
      JSON.parse(file_content)
    rescue ex
      puts "Warning: Could not load drugs.json: #{ex.message}"
      JSON.parse("{}")
    end
  end

  private def build_substance_lookup(drugs_data : JSON::Any) : Hash(String, String)
    lookup = Hash(String, String).new
    
    drugs_data.as_h.each do |substance_key, substance_data|
      substance_key_str = substance_key.to_s
      
      # Add the main substance name
      lookup[substance_key_str.downcase] = substance_key_str
      
      # Add aliases
      if substance_data.as_h.has_key?("aliases")
        substance_data["aliases"].as_a.each do |alias_name|
          lookup[alias_name.as_s.downcase] = substance_key_str
        end
      end
      
      # Add pretty_name if it exists
      if substance_data.as_h.has_key?("pretty_name")
        pretty_name = substance_data["pretty_name"].as_s
        lookup[pretty_name.downcase] = substance_key_str
      end
    end
    
    lookup
  end

  private def find_substance(name : String) : String?
    @substance_lookup[name.downcase]?
  end

  private def handle_substance_endpoint(request : HTTP::Request, response : HTTP::Server::Response)
    path_parts = request.path.split("/")
    
    # Expected format: /api/v1/substances/<name>/<property>
    if path_parts.size < 5
      response.status_code = 400
      response.print({"error" => "Invalid substance endpoint format"}.to_json)
      return
    end
    
    substance_name = URI.decode_www_form(path_parts[4])
    property = path_parts[5]? || "info"
    
    # Find the actual substance key using the lookup table
    actual_substance_key = find_substance(substance_name)
    
    unless actual_substance_key
      response.status_code = 404
      response.print({"error" => "Substance not found: #{substance_name}"}.to_json)
      return
    end
    
    substance_data = @drugs_data[actual_substance_key]
    
    case property
    when "dosage"
      handle_substance_dosage(response, actual_substance_key, substance_data)
    when "onset"
      handle_substance_onset(response, actual_substance_key, substance_data)
    when "duration"
      handle_substance_duration(response, actual_substance_key, substance_data)
    when "effects"
      handle_substance_effects(response, actual_substance_key, substance_data)
    when "info"
      handle_substance_info(response, actual_substance_key, substance_data)
    when "aftereffects"
      handle_substance_aftereffects(response, actual_substance_key, substance_data)
    else
      response.status_code = 404
      response.print({"error" => "Property not found: #{property}"}.to_json)
    end
  end

  private def handle_substance_dosage(response : HTTP::Server::Response, substance_key : String, substance_data)
    formatted_dose = substance_data.as_h["formatted_dose"]?
    
    if formatted_dose
      response.print({
        "substance" => substance_key,
        "property" => "dosage",
        "data" => formatted_dose
      }.to_json)
    else
      response.status_code = 404
      response.print({"error" => "Dosage data not available for #{substance_key}"}.to_json)
    end
  end

  private def handle_substance_onset(response : HTTP::Server::Response, substance_key : String, substance_data)
    formatted_onset = substance_data.as_h["formatted_onset"]?
    
    if formatted_onset
      response.print({
        "substance" => substance_key,
        "property" => "onset",
        "data" => formatted_onset
      }.to_json)
    else
      response.status_code = 404
      response.print({"error" => "Onset data not available for #{substance_key}"}.to_json)
    end
  end

  private def handle_substance_duration(response : HTTP::Server::Response, substance_key : String, substance_data)
    formatted_duration = substance_data.as_h["formatted_duration"]?
    
    if formatted_duration
      response.print({
        "substance" => substance_key,
        "property" => "duration",
        "data" => formatted_duration
      }.to_json)
    else
      response.status_code = 404
      response.print({"error" => "Duration data not available for #{substance_key}"}.to_json)
    end
  end

  private def handle_substance_effects(response : HTTP::Server::Response, substance_key : String, substance_data)
    formatted_effects = substance_data.as_h["formatted_effects"]?
    
    if formatted_effects
      response.print({
        "substance" => substance_key,
        "property" => "effects",
        "data" => formatted_effects
      }.to_json)
    else
      response.status_code = 404
      response.print({"error" => "Effects data not available for #{substance_key}"}.to_json)
    end
  end

  private def handle_substance_aftereffects(response : HTTP::Server::Response, substance_key : String, substance_data)
    formatted_aftereffects = substance_data.as_h["formatted_aftereffects"]?
    
    if formatted_aftereffects
      response.print({
        "substance" => substance_key,
        "property" => "aftereffects",
        "data" => formatted_aftereffects
      }.to_json)
    else
      response.status_code = 404
      response.print({"error" => "Aftereffects data not available for #{substance_key}"}.to_json)
    end
  end

  private def handle_substance_info(response : HTTP::Server::Response, substance_key : String, substance_data)
    info = {
      "name" => substance_key,
      "pretty_name" => substance_data.as_h["pretty_name"]?.try(&.as_s) || substance_key,
      "aliases" => substance_data.as_h["aliases"]? || [] of String,
      "categories" => substance_data.as_h["categories"]? || [] of String,
      "summary" => substance_data.as_h["properties"]?.try(&.as_h["summary"]?).try(&.as_s) || "No summary available"
    }
    
    response.print({
      "substance" => substance_key,
      "property" => "info",
      "data" => info
    }.to_json)
  end

  private def load_combos_data : JSON::Any
    begin
      file_content = File.read("data/combos.json")
      JSON.parse(file_content)
    rescue ex
      puts "Warning: Could not load combos.json: #{ex.message}"
      JSON.parse("{}")
    end
  end

  private def load_psywiki_data : JSON::Any
    begin
      file_content = File.read("data/psywiki_combos.json")
      JSON.parse(file_content)
    rescue ex
      puts "Warning: Could not load psywiki_combos.json: #{ex.message}"
      JSON.parse("{}")
    end
  end

  private def build_combos_lookup(combos_data : JSON::Any) : Hash(String, String)
    lookup = Hash(String, String).new
    
    combos_data.as_h.each do |substance_key, _|
      substance_key_str = substance_key.to_s
      
      # Add the main substance name
      lookup[substance_key_str.downcase] = substance_key_str
      
      # Try to find aliases from drugs data if available
      if @drugs_data.as_h.has_key?(substance_key_str)
        substance_data = @drugs_data[substance_key_str]
        if substance_data.as_h.has_key?("aliases")
          substance_data["aliases"].as_a.each do |alias_name|
            lookup[alias_name.as_s.downcase] = substance_key_str
          end
        end
      end
    end
    
    lookup
  end

  private def build_psywiki_lookup(psywiki_data : JSON::Any) : Hash(String, String)
    lookup = Hash(String, String).new
    
    # PsychonautWiki data structure: { data: { substances: [{ name: "...", ... }] } }
    if psywiki_data.as_h.has_key?("data") && psywiki_data["data"].as_h.has_key?("substances")
      psywiki_data["data"]["substances"].as_a.each do |substance|
        substance_name = substance["name"].as_s
        lookup[substance_name.downcase] = substance_name
      end
    end
    
    lookup
  end

  private def find_combo_substance(name : String) : String?
    normalized_name = name.downcase
    puts "DEBUG: find_combo_substance searching for: '#{normalized_name}'"
    
    # First try the combos lookup
    combo_key = @combos_lookup[normalized_name]?
    if combo_key
      puts "DEBUG: Found direct combos match: '#{normalized_name}' -> '#{combo_key}'"
      return combo_key
    end
    
    # Try the substance lookup and see if that maps to a combo
    substance_key = @substance_lookup[normalized_name]?
    if substance_key && @combos_data.as_h.has_key?(substance_key)
      puts "DEBUG: Found substance match that maps to combo: '#{normalized_name}' -> '#{substance_key}'"
      return substance_key
    end
    
    # Debug: show what keys are available
    puts "DEBUG: Available combo keys starting with '#{normalized_name[0]}': #{@combos_lookup.keys.select(&.starts_with?(normalized_name[0].to_s)).first(10)}"
    puts "DEBUG: Keys containing '#{normalized_name}': #{@combos_lookup.keys.select(&.includes?(normalized_name)).first(10)}"
    
    puts "DEBUG: No match found for: '#{normalized_name}'"
    nil
  end

  private def handle_interactions_endpoint(request : HTTP::Request, response : HTTP::Server::Response)
    path_parts = request.path.split("/")
    
    # Expected format: /api/v1/interactions/<substance1>/<substance2>
    if path_parts.size < 6
      response.status_code = 400
      response.print({"error" => "Invalid interactions endpoint format. Expected: /api/v1/interactions/{substance1}/{substance2}"}.to_json)
      return
    end
    
    substance1_name = URI.decode_www_form(path_parts[4])
    substance2_name = URI.decode_www_form(path_parts[5])
    
    puts "DEBUG: Looking up interactions for: '#{substance1_name}' + '#{substance2_name}'"
    
    # Find the actual substance keys using the lookup tables
    actual_substance1 = find_combo_substance(substance1_name)
    actual_substance2 = find_combo_substance(substance2_name)
    
    puts "DEBUG: Found mappings: '#{substance1_name}' -> '#{actual_substance1}', '#{substance2_name}' -> '#{actual_substance2}'"
    
    unless actual_substance1
      response.status_code = 404
      response.print({"error" => "First substance not found in interactions data: #{substance1_name}"}.to_json)
      return
    end
    
    unless actual_substance2
      response.status_code = 404
      response.print({"error" => "Second substance not found in interactions data: #{substance2_name}"}.to_json)
      return
    end
    
    # Look up the interaction - try both directions
    interaction_data = nil
    
    # Try substance1 -> substance2
    if @combos_data.as_h.has_key?(actual_substance1)
      substance1_combos = @combos_data[actual_substance1].as_h
      if substance1_combos.has_key?(actual_substance2)
        interaction_data = substance1_combos[actual_substance2]
      end
    end
    
    # If not found, try substance2 -> substance1
    if interaction_data.nil? && @combos_data.as_h.has_key?(actual_substance2)
      substance2_combos = @combos_data[actual_substance2].as_h
      if substance2_combos.has_key?(actual_substance1)
        interaction_data = substance2_combos[actual_substance1]
      end
    end
    
    if interaction_data
      response.print({
        "substance1" => actual_substance1,
        "substance2" => actual_substance2,
        "interaction" => interaction_data,
        "source" => "tripsit"
      }.to_json)
    else
      # Fallback to PsychonautWiki
      puts "DEBUG: Not found in TripSit dataset, falling back to PsychonautWiki dataset"
      psywiki_data = fetch_psywiki_interaction(substance1_name, substance2_name)
      
      if psywiki_data
        response.print({
          "substance1" => substance1_name,
          "substance2" => substance2_name,
          "interaction" => psywiki_data,
          "source" => "psychonautwiki",
          "disclaimer" => "Not found in TripSit dataset, falling back to PsychonautWiki dataset - not based on peer-reviewed literature. Use with caution."
        }.to_json)
      else
        response.status_code = 404
        response.print({
          "error" => "No interaction data found between #{substance1_name} and #{substance2_name} in either TripSit or PsychonautWiki datasets",
          "substance1" => actual_substance1,
          "substance2" => actual_substance2
        }.to_json)
      end
    end
  end

  private def fetch_psywiki_interaction(substance1 : String, substance2 : String)
    begin
      puts "DEBUG: Searching PsychonautWiki data for #{substance1} + #{substance2}"
      
      # Find substances in PsychonautWiki data
      psywiki_sub1 = @psywiki_lookup[substance1.downcase]?
      psywiki_sub2 = @psywiki_lookup[substance2.downcase]?
      
      puts "DEBUG: PsychonautWiki mappings: #{substance1} -> #{psywiki_sub1}, #{substance2} -> #{psywiki_sub2}"
      
      return nil unless psywiki_sub1 && psywiki_sub2
      
      # Find the substances in the data
      substances = @psywiki_data["data"]["substances"].as_a
      sub1_data = substances.find { |s| s["name"].as_s == psywiki_sub1 }
      sub2_data = substances.find { |s| s["name"].as_s == psywiki_sub2 }
      
      return nil unless sub1_data && sub2_data
      
      # Check for interactions between the substances
      interaction_level = check_psywiki_interaction(sub1_data, psywiki_sub2) || 
                         check_psywiki_interaction(sub2_data, psywiki_sub1)
      
      if interaction_level
        return {
          "status" => interaction_level,
          "note" => "Interaction found in PsychonautWiki dataset"
        }
      end
      
      nil
    rescue ex
      puts "ERROR: Failed to fetch from PsychonautWiki: #{ex.message}"
      nil
    end
  end

  private def check_psywiki_interaction(substance_data, target_name : String) : String?
    # Check dangerous interactions
    if substance_data["dangerousInteractions"]?
      dangerous = substance_data["dangerousInteractions"].as_a?.try(&.any? { |i| i["name"].as_s == target_name })
      return "Dangerous" if dangerous
    end
    
    # Check unsafe interactions
    if substance_data["unsafeInteractions"]?
      unsafe = substance_data["unsafeInteractions"].as_a?.try(&.any? { |i| i["name"].as_s == target_name })
      return "Unsafe" if unsafe
    end
    
    # Check uncertain interactions
    if substance_data["uncertainInteractions"]?
      uncertain = substance_data["uncertainInteractions"].as_a?.try(&.any? { |i| i["name"].as_s == target_name })
      return "Uncertain" if uncertain
    end
    
    nil
  end

  private def handle_openapi_spec(request : HTTP::Request, response : HTTP::Server::Response)
    begin
      spec_content = File.read("openapi.yaml")
      response.content_type = "text/yaml"
      response.headers["Access-Control-Allow-Origin"] = "*"
      response.print(spec_content)
    rescue ex
      response.status_code = 404
      response.content_type = "application/json"
      response.print({"error" => "OpenAPI spec not found: #{ex.message}"}.to_json)
    end
  end

  private def handle_swagger_ui(request : HTTP::Request, response : HTTP::Server::Response)
    response.content_type = "text/html"
    response.headers["Access-Control-Allow-Origin"] = "*"
    
    swagger_ui_html = <<-HTML
    <!DOCTYPE html>
    <html lang="en">
    <head>
      <meta charset="UTF-8">
      <title>Josie Health API Documentation</title>
      <link rel="stylesheet" type="text/css" href="https://unpkg.com/swagger-ui-dist@5.10.3/swagger-ui.css" />
      <style>
        html {
          box-sizing: border-box;
          overflow: -moz-scrollbars-vertical;
          overflow-y: scroll;
        }
        *, *:before, *:after {
          box-sizing: inherit;
        }
        body {
          margin:0;
          background: #fafafa;
        }
      </style>
    </head>
    <body>
      <div id="swagger-ui"></div>
      <script src="https://unpkg.com/swagger-ui-dist@5.10.3/swagger-ui-bundle.js"></script>
      <script src="https://unpkg.com/swagger-ui-dist@5.10.3/swagger-ui-standalone-preset.js"></script>
      <script>
        window.onload = function() {
          const ui = SwaggerUIBundle({
            url: '/api/v1/openapi.yaml',
            dom_id: '#swagger-ui',
            deepLinking: true,
            presets: [
              SwaggerUIBundle.presets.apis,
              SwaggerUIStandalonePreset
            ],
            plugins: [
              SwaggerUIBundle.plugins.DownloadUrl
            ],
            layout: "StandaloneLayout",
            tryItOutEnabled: true,
            requestInterceptor: function(request) {
              // Add basic auth header if present
              const auth = localStorage.getItem('swagger-auth');
              if (auth) {
                request.headers['Authorization'] = 'Basic ' + auth;
              }
              return request;
            }
          });
        };
      </script>
    </body>
    </html>
    HTML
    
    response.print(swagger_ui_html)
  end

  private def handle_user_metrics_endpoint(request : HTTP::Request, response : HTTP::Server::Response)
    path_parts = request.path.split("/")
    puts "DEBUG: handle_user_metrics_endpoint called with path: #{request.path}"
    puts "DEBUG: Path parts: #{path_parts}"
    puts "DEBUG: Query string: #{request.query}"
    puts "DEBUG: Headers: #{request.headers.to_h}"
    
    # Expected format: /api/v1/metrics/users/{user_id}/{endpoint}
    if path_parts.size < 7
      puts "DEBUG: Invalid path format - expected 7+ parts, got #{path_parts.size}"
      response.status_code = 400
      response.print({"error" => "Invalid user metrics endpoint format. Expected: /api/v1/metrics/users/{user_id}/{endpoint}"}.to_json)
      return
    end
    
    user_id = URI.decode_www_form(path_parts[5])
    endpoint = path_parts[6]
    puts "DEBUG: Extracted user_id: '#{user_id}', endpoint: '#{endpoint}'"
    
    # Parse query parameters
    query_params = URI::Params.parse(request.query || "")
    puts "DEBUG: Query params: #{query_params.to_h}"
    
    case endpoint
    when "lastdose"
      n = query_params["n"]?.try(&.to_i) || 1
      puts "DEBUG: Routing to handle_user_lastdose with n=#{n}"
      handle_user_lastdose(response, user_id, n)
    when "listdose"
      n = query_params["n"]?.try(&.to_i) || 5
      handle_user_listdose(response, user_id, n)
    when "last_substance"
      substance = query_params["substance"]?
      if substance
        handle_user_last_substance(response, user_id, substance)
      else
        response.status_code = 400
        response.print({"error" => "Missing 'substance' query parameter"}.to_json)
      end
    when "howhighami"
      handle_user_howhighami(response, user_id)
    else
      response.status_code = 404
      response.print({"error" => "User metric endpoint not found: #{endpoint}"}.to_json)
    end
  end

  private def handle_user_lastdose(response : HTTP::Server::Response, user_id : String, n : Int32)
    begin
      puts "DEBUG: handle_user_lastdose called with user_id=#{user_id}, n=#{n}"
      csv_content = @sftp_client.fetch_csv_content
      puts "DEBUG: CSV content fetched, length: #{csv_content.size}"
      puts "DEBUG: First 500 chars: #{csv_content[0..499]}" if csv_content.size > 0
      
      lines = csv_content.split('\n')
      puts "DEBUG: CSV has #{lines.size} total lines"
      puts "DEBUG: First line (header?): #{lines[0]}" if lines.size > 0
      puts "DEBUG: Second line (first data?): #{lines[1]}" if lines.size > 1
      
      # Skip header and empty lines
      data_lines = lines[1..-1].reject(&.empty?)
      puts "DEBUG: #{data_lines.size} data lines after removing header and empty lines"
      
      # Filter by user_id and get entries, newest first
      puts "DEBUG: Looking for user_id: '#{user_id}'"
      user_entries = data_lines.select do |line|
        parts = line.split(',')
        if parts.size >= 2
          line_user_id = parts[1].strip
          matches = line_user_id == user_id
          if data_lines.index(line).try(&.<(5))  # Log first 5 lines for debugging
            puts "DEBUG: Line #{data_lines.index(line)}: user_id='#{line_user_id}', looking for='#{user_id}', matches=#{matches}"
          end
          matches
        else
          puts "DEBUG: Malformed line (less than 2 parts): #{line[0..100]}" if data_lines.index(line).try(&.<(5))
          false
        end
      end.reverse.map do |line|
        parts = line.split(',')
        if parts.size >= 5
          {
            "timestamp" => parts[0],
            "user_id" => parts[1],
            "substance_name" => parts[2].downcase.strip,
            "dosage" => parts[3],
            "route" => parts[4].downcase.strip,
            "source" => "sftp_csv_import"
          }
        else
          puts "DEBUG: Skipping malformed line: #{line}"
          nil
        end
      end.compact
      puts "DEBUG: Found #{user_entries.size} entries for user #{user_id}"
      
      if user_entries.empty?
        response.status_code = 404
        response.print({"error" => "No dose entries found for user #{user_id}"}.to_json)
        return
      end
      
      # Get the nth entry (1-indexed)
      if n > user_entries.size
        response.status_code = 404
        response.print({"error" => "User #{user_id} only has #{user_entries.size} dose entries, requested #{n}"}.to_json)
        return
      end
      
      nth_entry = user_entries[n - 1]
      
      response.print({
        "data" => nth_entry,
        "meta" => {
          "user_id" => user_id,
          "position" => n,
          "total_entries" => user_entries.size
        }
      }.to_json)
    rescue ex
      response.status_code = 500
      response.print({"error" => "Failed to fetch user dose data: #{ex.message}"}.to_json)
    end
  end

  private def handle_user_listdose(response : HTTP::Server::Response, user_id : String, n : Int32)
    begin
      csv_content = @sftp_client.fetch_csv_content
      lines = csv_content.split('\n')
      
      # Skip header and empty lines
      data_lines = lines[1..-1].reject(&.empty?)
      
      # Filter by user_id and get entries, newest first
      user_entries = data_lines.select do |line|
        parts = line.split(',')
        parts.size >= 2 && parts[1] == user_id
      end.reverse.map do |line|
        parts = line.split(',')
        if parts.size >= 5
          {
            "timestamp" => parts[0],
            "user_id" => parts[1],
            "substance_name" => parts[2].downcase.strip,
            "dosage" => parts[3],
            "route" => parts[4].downcase.strip,
            "source" => "sftp_csv_import"
          }
        else
          nil
        end
      end.compact
      
      if user_entries.empty?
        response.status_code = 404
        response.print({"error" => "No dose entries found for user #{user_id}"}.to_json)
        return
      end
      
      # Get the first n entries
      result_entries = user_entries.first(n)
      
      response.print({
        "data" => result_entries,
        "meta" => {
          "user_id" => user_id,
          "count" => result_entries.size,
          "requested" => n,
          "total_entries" => user_entries.size
        }
      }.to_json)
    rescue ex
      response.status_code = 500
      response.print({"error" => "Failed to fetch user dose list: #{ex.message}"}.to_json)
    end
  end

  private def handle_user_last_substance(response : HTTP::Server::Response, user_id : String, substance : String)
    begin
      csv_content = @sftp_client.fetch_csv_content
      lines = csv_content.split('\n')
      
      # Skip header and empty lines
      data_lines = lines[1..-1].reject(&.empty?)
      
      # Filter by user_id and substance, newest first
      substance_entries = data_lines.select do |line|
        parts = line.split(',')
        parts.size >= 3 && parts[1] == user_id && parts[2].downcase.strip == substance.downcase
      end.reverse.map do |line|
        parts = line.split(',')
        if parts.size >= 5
          {
            "timestamp" => parts[0],
            "user_id" => parts[1],
            "substance_name" => parts[2].downcase.strip,
            "dosage" => parts[3],
            "route" => parts[4].downcase.strip,
            "source" => "sftp_csv_import"
          }
        else
          nil
        end
      end.compact
      
      if substance_entries.empty?
        response.status_code = 404
        response.print({"error" => "No entries found for user #{user_id} and substance #{substance}"}.to_json)
        return
      end
      
      # Get the most recent entry
      last_entry = substance_entries.first
      
      response.print({
        "data" => last_entry,
        "meta" => {
          "user_id" => user_id,
          "substance" => substance,
          "total_entries_for_substance" => substance_entries.size
        }
      }.to_json)
    rescue ex
      response.status_code = 500
      response.print({"error" => "Failed to fetch user substance data: #{ex.message}"}.to_json)
    end
  end

  private def handle_user_howhighami(response : HTTP::Server::Response, user_id : String)
    begin
      csv_content = @sftp_client.fetch_csv_content
      lines = csv_content.split('\n')
      
      # Skip header and empty lines
      data_lines = lines[1..-1].reject(&.empty?)
      
      # Filter by user_id, newest first
      user_entries = data_lines.select do |line|
        parts = line.split(',')
        parts.size >= 3 && parts[1] == user_id
      end.reverse.map do |line|
        parts = line.split(',')
        if parts.size >= 5
          {
            "timestamp" => parts[0],
            "substance_name" => parts[2].downcase.strip,
            "dosage" => parts[3],
            "route" => parts[4].downcase.strip
          }
        else
          nil
        end
      end.compact
      
      if user_entries.empty?
        response.print({
          "active_substances" => [] of String,
          "meta" => {"message" => "No dose history found"}
        }.to_json)
        return
      end
      
      # Load duration data from drugs.json
      duration_estimates = {} of String => Float64
      begin
        drugs_json = File.read("data/drugs.json")
        drugs_data = JSON.parse(drugs_json)
        
        drugs_data.as_h.each do |drug_key, drug_info|
          name_json = drug_info["name"]?
          next unless name_json
          name = name_json.as_s
          
          if duration_info = drug_info["formatted_duration"]?
            duration_value_json = duration_info["value"]?
            duration_unit_json = duration_info["_unit"]?
            next unless duration_value_json && duration_unit_json
            
            duration_value = duration_value_json.as_s
            duration_unit = duration_unit_json.as_s
            
            if duration_unit == "hours"
              # Parse duration range like "3-5" or "8-12" - use upper bound
              if match = duration_value.match(/(\d+(?:\.\d+)?)-(\d+(?:\.\d+)?)/)
                upper_bound = match[2].to_f
                duration_estimates[name] = upper_bound
                
                # Also add common aliases
                if aliases_json = drug_info["aliases"]?
                  aliases_json.as_a.each do |alias_json|
                    alias_name = alias_json.as_s
                    duration_estimates[alias_name] = upper_bound
                  end
                end
              elsif match = duration_value.match(/(\d+(?:\.\d+)?)/)
                # Single value like "6"
                single_value = match[1].to_f
                duration_estimates[name] = single_value
                
                # Also add aliases for single values
                if aliases_json = drug_info["aliases"]?
                  aliases_json.as_a.each do |alias_json|
                    alias_name = alias_json.as_s
                    duration_estimates[alias_name] = single_value
                  end
                end
              end
            end
          end
        end
      rescue ex
        puts "Warning: Could not load drugs.json, using fallback estimates: #{ex.message}"
        # Fallback duration estimates (in hours) by substance
        duration_estimates = {
          "alcohol" => 4.0,
          "ketamine" => 2.0,
          "lsd" => 12.0,
          "mdma" => 6.0,
          "psilocybin" => 6.0,
          "cocaine" => 1.5,
          "caffeine" => 6.0,
          "cannabis" => 4.0,
          "nicotine" => 1.0,
          "amphetamine" => 8.0,
          "methamphetamine" => 12.0
        }
      end
      
      active_substances = [] of Hash(String, String | Float64)
      current_time = Time.utc
      
      # Group by substance and get most recent for each
      substances_last_use = {} of String => Hash(String, String)
      user_entries.each do |entry|
        substance = entry["substance_name"].as(String)
        if !substances_last_use.has_key?(substance)
          substances_last_use[substance] = entry.transform_values(&.to_s)
        end
      end
      
      substances_last_use.each do |substance, entry|
        duration_hours = duration_estimates[substance]? || 6 # Default 6 hours
        
        begin
          dose_time = Time.parse_rfc3339(entry["timestamp"])
          hours_since = (current_time - dose_time).total_hours
          
          if hours_since < duration_hours
            time_since_str = if hours_since < 1
              "#{(hours_since * 60).to_i} minutes ago"
            else
              "#{hours_since.round(1)} hours ago"
            end
            
            active_substances << {
              "name" => substance,
              "last_dose_time" => dose_time.to_s("%Y-%m-%d %H:%M"),
              "time_since" => time_since_str,
              "estimated_duration" => "#{duration_hours} hours",
              "hours_remaining" => (duration_hours - hours_since).round(1)
            }
          end
        rescue Time::Format::Error
          # Skip entries with invalid timestamps
          next
        end
      end
      
      response.print({
        "active_substances" => active_substances,
        "meta" => {
          "total_logged_entries" => user_entries.size,
          "unique_substances" => substances_last_use.size,
          "current_time" => current_time.to_s("%Y-%m-%d %H:%M UTC")
        }
      }.to_json)
      
    rescue ex
      response.status_code = 500
      response.print({"error" => "Failed to fetch user howhighami data: #{ex.message}"}.to_json)
    end
  end

  # POST handlers for logging data
  private def handle_log_substance(request : HTTP::Request, response : HTTP::Server::Response)
    begin
      body = request.body.try(&.gets_to_end) || ""
      data = JSON.parse(body)
      
      # Validate required fields
      required_fields = ["user_id", "substance_name", "dosage", "route"]
      missing_fields = required_fields.select { |field| !data.as_h.has_key?(field) || data[field].as_s?.nil? }
      
      if missing_fields.any?
        response.status_code = 400
        response.print({"error" => "Missing required fields: #{missing_fields.join(", ")}"}.to_json)
        return
      end
      
      # Extract data
      timestamp = data["timestamp"]?.try(&.as_s) || Time.utc.to_rfc3339
      user_id = data["user_id"].as_s
      substance_name = data["substance_name"].as_s.downcase.strip
      dosage = data["dosage"]
      unit = data["unit"]?.try(&.as_s) || "mg"
      route = data["route"].as_s.downcase.strip
      source = data["source"]?.try(&.as_s) || "api"
      
      # Append to SFTP CSV
      csv_line = "#{timestamp},#{user_id},#{substance_name},#{dosage}#{unit},#{route}"
      @sftp_client.append_to_csv(csv_line)
      
      response.status_code = 201
      response.print({
        "success" => true,
        "message" => "Substance logged successfully",
        "data" => {
          "timestamp" => timestamp,
          "user_id" => user_id,
          "substance_name" => substance_name,
          "dosage" => "#{dosage}#{unit}",
          "route" => route,
          "source" => source
        }
      }.to_json)
      
    rescue JSON::ParseException
      response.status_code = 400
      response.print({"error" => "Invalid JSON in request body"}.to_json)
    rescue ex
      response.status_code = 500
      response.print({"error" => "Failed to log substance: #{ex.message}"}.to_json)
    end
  end

  private def handle_log_vitals(request : HTTP::Request, response : HTTP::Server::Response)
    begin
      body = request.body.try(&.gets_to_end) || ""
      data = JSON.parse(body)
      
      # Validate that at least one vital is provided
      vital_fields = ["pulse", "systolic_bp", "diastolic_bp", "blood_oxygen"]
      provided_vitals = vital_fields.select { |field| data.as_h.has_key?(field) && !data[field].raw.nil? }
      
      if provided_vitals.empty?
        response.status_code = 400
        response.print({"error" => "At least one vital sign must be provided: #{vital_fields.join(", ")}"}.to_json)
        return
      end
      
      # For now, just return success (since vitals aren't stored in CSV yet)
      timestamp = data["timestamp"]?.try(&.as_s) || Time.utc.to_rfc3339
      user_id = data["user_id"]?.try(&.as_s) || "unknown"
      
      response.status_code = 201
      response.print({
        "success" => true,
        "message" => "Vitals logged successfully",
        "data" => {
          "timestamp" => timestamp,
          "user_id" => user_id,
          "vitals" => provided_vitals.map { |field| {field => data[field]} }.reduce { |acc, h| acc.merge(h) }
        }
      }.to_json)
      
    rescue JSON::ParseException
      response.status_code = 400
      response.print({"error" => "Invalid JSON in request body"}.to_json)
    rescue ex
      response.status_code = 500
      response.print({"error" => "Failed to log vitals: #{ex.message}"}.to_json)
    end
  end

  private def handle_log_labs(request : HTTP::Request, response : HTTP::Server::Response)
    begin
      body = request.body.try(&.gets_to_end) || ""
      data = JSON.parse(body)
      
      # Validate required fields
      required_fields = ["test_type", "value"]
      missing_fields = required_fields.select { |field| !data.as_h.has_key?(field) || data[field].raw.nil? }
      
      if missing_fields.any?
        response.status_code = 400
        response.print({"error" => "Missing required fields: #{missing_fields.join(", ")}"}.to_json)
        return
      end
      
      # For now, just return success (since labs aren't stored in CSV yet)  
      timestamp = data["timestamp"]?.try(&.as_s) || Time.utc.to_rfc3339
      user_id = data["user_id"]?.try(&.as_s) || "unknown"
      test_type = data["test_type"].as_s.downcase
      value = data["value"]
      unit = data["unit"]?.try(&.as_s) || "unknown"
      
      response.status_code = 201
      response.print({
        "success" => true,
        "message" => "Lab result logged successfully", 
        "data" => {
          "timestamp" => timestamp,
          "user_id" => user_id,
          "test_type" => test_type,
          "value" => value,
          "unit" => unit
        }
      }.to_json)
      
    rescue JSON::ParseException
      response.status_code = 400
      response.print({"error" => "Invalid JSON in request body"}.to_json)
    rescue ex
      response.status_code = 500
      response.print({"error" => "Failed to log lab result: #{ex.message}"}.to_json)
    end
  end
end

# Start the server
api = SimpleLogsAPI.new
api.start