require "json"
require "./data_files"

module JosieHealth::LUT
  module CannabisLUT
    @@sources : Hash(String, NamedTuple(default_amount: Float64, unit: String, category: String))?
    @@alias_map : Hash(String, String)?
    @@serving_units : Hash(String, Int32)?
    @@loaded = false

    def self.load_data
      return if @@loaded

      begin
        json = DataFiles.cannabis

        @@sources = {} of String => NamedTuple(default_amount: Float64, unit: String, category: String)
        @@alias_map = {} of String => String
        @@serving_units = {} of String => Int32

        if sources_json = json["sources"]?
          sources_json.as_h.each do |name, data|
            canonical_name = name.to_s
            unit = data["unit"].as_s
            default_amount = if unit == "g"
                               data["default_g"].as_f
                             else
                               data["default_mg"].as_f
                             end

            @@sources.not_nil![canonical_name] = {
              default_amount: default_amount,
              unit:           unit,
              category:       data["category"].as_s,
            }

            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

        if serving_json = json["serving_units"]?
          serving_json.as_h.each do |name, multiplier|
            @@serving_units.not_nil![name.to_s] = multiplier.as_i
          end
        end

        puts "Loaded #{@@sources.not_nil!.size} cannabis sources with #{@@alias_map.not_nil!.size} aliases"
        @@loaded = true
      rescue ex
        puts "Error loading cannabis.json: #{ex.message}"
        @@sources = {} of String => NamedTuple(default_amount: Float64, unit: String, category: String)
        @@alias_map = {} of String => String
        @@serving_units = {} of String => Int32
        @@loaded = true
      end
    end

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

    def self.lookup_source(name : String) : NamedTuple(default_amount: Float64, unit: String, category: String)?
      load_data
      canonical = resolve_alias(name)
      @@sources.not_nil![canonical]?
    end

    def self.is_cannabis_source?(name : String) : Bool
      lookup_source(name) != nil
    end

    def self.parse_dosage(dosage : String) : NamedTuple(count: Float64, serving: String?)?
      normalized = dosage.downcase.strip
      load_data

      if match = normalized.match(/^(\d+(?:\/\d+)?(?:\.\d+)?)(puff|puffs|hit|hits|rip|rips|toke|tokes)?$/)
        count_str = match[1]
        serving = match[2]?

        count = if count_str.includes?("/")
                  parts = count_str.split("/")
                  parts[0].to_f / parts[1].to_f
                else
                  count_str.to_f
                end

        return {count: count, serving: serving}
      end

      nil
    end

    def self.calculate_dose(dosage : String, source : String) : NamedTuple(amount: Float64, unit: String, details: String)?
      parsed = parse_dosage(dosage)
      return nil unless parsed

      source_info = lookup_source(source)
      return nil unless source_info

      count = parsed[:count]

      if parsed[:serving]
        count = count * (@@serving_units.not_nil![parsed[:serving]]? || 1)
      end

      amount = (source_info[:default_amount] * count).round(2)
      canonical = resolve_alias(source)

      count_str = if parsed[:count] == parsed[:count].to_i
                    parsed[:count].to_i.to_s
                  else
                    parsed[:count].round(2).to_s
                  end

      serving_str = parsed[:serving] ? "#{count_str}#{parsed[:serving]}" : count_str
      details = "#{canonical} #{serving_str} (#{amount}#{source_info[:unit]})"

      {amount: amount, unit: source_info[:unit], details: details}
    end

    def self.all_sources : Array(String)
      load_data
      @@sources.not_nil!.keys
    end
  end
end
