require "json"
require "./data_files"

module JosieHealth::LUT
  module MedsLUT
    @@medications : Hash(String, NamedTuple(
      name: String,
      pretty_name: String,
      default_concentration: Int32,
      category: String,
      subcategory: String))?
    @@alias_map : Hash(String, String)?
    @@loaded = false

    # Load medication data from embedded JSON
    def self.load_data
      return if @@loaded

      begin
        json = DataFiles.meds

        @@medications = {} of String => NamedTuple(name: String, pretty_name: String, default_concentration: Int32, category: String, subcategory: String)
        @@alias_map = {} of String => String

        if meds_json = json["medications"]?
          meds_json.as_h.each do |name, data|
            canonical_name = name.to_s
            @@medications.not_nil![canonical_name] = {
              name:                  data["name"]?.try(&.as_s) || canonical_name,
              pretty_name:           data["pretty_name"]?.try(&.as_s) || canonical_name,
              default_concentration: data["default_concentration_mg_ml"].as_i,
              category:              data["category"]?.try(&.as_s) || "medication",
              subcategory:           data["subcategory"]?.try(&.as_s) || "",
            }

            # Register aliases
            if aliases = data["aliases"]?
              aliases.as_a.each do |alias_name|
                @@alias_map.not_nil![alias_name.as_s.downcase] = canonical_name
              end
            end
          end
        end

        puts "Loaded #{@@medications.not_nil!.size} medications with #{@@alias_map.not_nil!.size} aliases"
        @@loaded = true
      rescue ex
        puts "Error loading meds.json: #{ex.message}"
        @@medications = {} of String => NamedTuple(name: String, pretty_name: String, default_concentration: Int32, category: String, subcategory: String)
        @@alias_map = {} of String => String
        @@loaded = true
      end
    end

    # Resolve alias to canonical name
    def self.resolve_alias(name : String) : String
      load_data
      normalized = name.downcase.gsub(/[\s\-_]/, "")
      @@alias_map.not_nil![normalized]? || normalized
    end

    # Lookup a medication by name (handles aliases)
    def self.lookup_medication(name : String) : NamedTuple(name: String, pretty_name: String, default_concentration: Int32, category: String, subcategory: String)?
      load_data
      # Handle @concentration suffix
      base_name = name.split("@").first
      canonical = resolve_alias(base_name)
      @@medications.not_nil![canonical]?
    end

    # Check if a substance is an injectable medication
    def self.is_injectable_med?(name : String) : Bool
      lookup_medication(name) != nil
    end

    # Parse substance name with optional concentration override
    # Returns {substance, concentration} or nil
    # Examples: "een" -> {een, nil}, "een@20" -> {een, 20}
    def self.parse_substance_concentration(name : String) : NamedTuple(substance: String, concentration: Int32?)?
      parts = name.split("@", 2)
      substance = parts[0]

      return nil unless is_injectable_med?(substance)

      concentration = if parts.size > 1 && !parts[1].empty?
                        parts[1].to_i?
                      else
                        nil
                      end

      {substance: substance, concentration: concentration}
    end

    # Calculate mg from ml and concentration
    def self.calculate_mg(volume_ml : Float64, concentration_mg_ml : Int32) : Float64
      (volume_ml * concentration_mg_ml).round(1)
    end

    # Full medication dose calculation
    # Input: substance name (possibly with @concentration), dosage string, unit
    # Returns: {mg, canonical_name, annotation} or nil
    def self.calculate_dose(substance : String, dosage : String, unit : String) : NamedTuple(mg: Float64, canonical_name: String, annotation: String)?
      # Only process if unit is ml
      return nil unless unit.downcase == "ml"

      parsed = parse_substance_concentration(substance)
      return nil unless parsed

      med_info = lookup_medication(parsed[:substance])
      return nil unless med_info

      # Parse dosage as float
      volume_ml = dosage.to_f?
      return nil unless volume_ml

      # Use override concentration or default
      concentration = parsed[:concentration] || med_info[:default_concentration]

      mg = calculate_mg(volume_ml, concentration)

      details = "#{volume_ml}ml @ #{concentration}mg/ml"

      {mg: mg, canonical_name: med_info[:name], annotation: details}
    end

    # Get all medication names
    def self.all_medications : Array(String)
      load_data
      @@medications.not_nil!.keys
    end
  end
end
