require "spec"
require "../src/josie-health-lut"

describe JosieHealth::LUT::Fuzzy do
  describe ".damerau_distance" do
    it "returns 0 for identical strings" do
      JosieHealth::LUT::Fuzzy.damerau_distance("ketamine", "ketamine").should eq(0)
    end

    it "handles substitution" do
      JosieHealth::LUT::Fuzzy.damerau_distance("ketamine", "ketamina").should eq(1)
    end

    it "handles deletion" do
      JosieHealth::LUT::Fuzzy.damerau_distance("ketamine", "ketamin").should eq(1)
    end

    it "handles insertion" do
      JosieHealth::LUT::Fuzzy.damerau_distance("ketamin", "ketamine").should eq(1)
    end

    it "handles transposition" do
      JosieHealth::LUT::Fuzzy.damerau_distance("ketamien", "ketamine").should eq(1)
    end

    it "handles multiple edits" do
      JosieHealth::LUT::Fuzzy.damerau_distance("clonazpam", "clonazepam").should eq(1)
    end

    it "returns correct distance for completely different strings" do
      JosieHealth::LUT::Fuzzy.damerau_distance("abc", "xyz").should eq(3)
    end

    it "handles empty strings" do
      JosieHealth::LUT::Fuzzy.damerau_distance("", "abc").should eq(3)
      JosieHealth::LUT::Fuzzy.damerau_distance("abc", "").should eq(3)
      JosieHealth::LUT::Fuzzy.damerau_distance("", "").should eq(0)
    end
  end

  describe ".max_distance" do
    it "returns 0 for very short strings" do
      JosieHealth::LUT::Fuzzy.max_distance(1).should eq(0)
      JosieHealth::LUT::Fuzzy.max_distance(2).should eq(0)
    end

    it "returns 1 for short strings" do
      JosieHealth::LUT::Fuzzy.max_distance(3).should eq(1)
      JosieHealth::LUT::Fuzzy.max_distance(5).should eq(1)
    end

    it "returns 2 for medium strings" do
      JosieHealth::LUT::Fuzzy.max_distance(6).should eq(2)
      JosieHealth::LUT::Fuzzy.max_distance(8).should eq(2)
    end

    it "returns 3 for long strings" do
      JosieHealth::LUT::Fuzzy.max_distance(9).should eq(3)
      JosieHealth::LUT::Fuzzy.max_distance(15).should eq(3)
    end
  end

  describe ".auto_correct?" do
    it "never auto-corrects short strings" do
      JosieHealth::LUT::Fuzzy.auto_correct?(2, 1).should be_false
    end

    it "auto-corrects distance 1 for 3-5 char strings" do
      JosieHealth::LUT::Fuzzy.auto_correct?(4, 1).should be_true
    end

    it "auto-corrects distance 1 for 6-8 char strings" do
      JosieHealth::LUT::Fuzzy.auto_correct?(7, 1).should be_true
    end

    it "does not auto-correct distance 2 for 6-8 char strings" do
      JosieHealth::LUT::Fuzzy.auto_correct?(7, 2).should be_false
    end

    it "auto-corrects distance 2 for 9+ char strings" do
      JosieHealth::LUT::Fuzzy.auto_correct?(10, 2).should be_true
    end

    it "does not auto-correct distance 3 for 9+ char strings" do
      JosieHealth::LUT::Fuzzy.auto_correct?(10, 3).should be_false
    end
  end
end

describe JosieHealth::LUT::DrugLUT do
  describe ".fuzzy_normalize" do
    it "returns exact match with distance 0" do
      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("ketamine")
      result.should_not be_nil
      result = result.not_nil!
      result.canonical.should eq("ketamine")
      result.distance.should eq(0)
      result.auto_correct.should be_true
    end

    it "finds ketamine from typo 'ketanine'" do
      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("ketanine")
      result.should_not be_nil
      result = result.not_nil!
      result.canonical.should eq("ketamine")
      result.distance.should eq(1)
      result.auto_correct.should be_true
    end

    it "finds ibuprofen from 'ibuprofn'" do
      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("ibuprofn")
      result.should_not be_nil
      result = result.not_nil!
      result.canonical.should eq("ibuprofen")
      result.auto_correct.should be_true
    end

    it "finds amphetamine from 'amphetamin'" do
      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("amphetamin")
      result.should_not be_nil
      result = result.not_nil!
      result.canonical.should eq("amphetamine")
      result.distance.should eq(1)
      result.auto_correct.should be_true
    end

    it "finds clonazepam from 'clonazpam' (already an alias)" do
      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("clonazpam")
      result.should_not be_nil
      result = result.not_nil!
      result.canonical.should eq("clonazepam")
      result.distance.should eq(0) # it's a known alias
    end

    it "rejects strings <= 2 chars" do
      JosieHealth::LUT::DrugLUT.fuzzy_normalize("k").should be_nil
      JosieHealth::LUT::DrugLUT.fuzzy_normalize("lsd").should_not be_nil # 3 chars exact match is ok
    end

    it "rejects completely unknown substances" do
      JosieHealth::LUT::DrugLUT.fuzzy_normalize("zzzzzzzzz").should be_nil
    end

    it "does not cross-match lsd and lsa" do
      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("lsd")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("lsd")

      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("lsa")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("lsa")
    end

    it "does not cross-match mda and mdma at distance 1" do
      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("mda")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("mda")
    end

    it "does not fuzzy-match 3-cmc to 3-mmc (distance 1 but different substances)" do
      # "3cmc" is an exact alias for 3-cmc, and "3mmc" is an exact alias for 3-mmc
      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("3cmc")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("3-cmc")

      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("3mmc")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("3-mmc")
    end

    it "handles case-insensitive input" do
      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("KETAMINE")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("ketamine")
    end

    it "finds pregabalin from 'pregabaln'" do
      result = JosieHealth::LUT::DrugLUT.fuzzy_normalize("pregabaln")
      result.should_not be_nil
      result = result.not_nil!
      result.canonical.should eq("pregabalin")
      result.auto_correct.should be_true
    end
  end
end

describe JosieHealth::LUT::RouteLUT do
  describe ".fuzzy_normalize" do
    it "returns exact match with distance 0" do
      result = JosieHealth::LUT::RouteLUT.fuzzy_normalize("oral")
      result.should_not be_nil
      result = result.not_nil!
      result.canonical.should eq("oral")
      result.distance.should eq(0)
    end

    it "finds insufflated from 'insuflated' (now an alias)" do
      result = JosieHealth::LUT::RouteLUT.fuzzy_normalize("insuflated")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("insufflated")
      result.not_nil!.distance.should eq(0) # it's a known alias now
    end

    it "finds smoked from 'smoled' (now an alias)" do
      result = JosieHealth::LUT::RouteLUT.fuzzy_normalize("smoled")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("smoked")
      result.not_nil!.distance.should eq(0)
    end

    it "finds sublingual from 'subligual' (now an alias)" do
      result = JosieHealth::LUT::RouteLUT.fuzzy_normalize("subligual")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("sublingual")
      result.not_nil!.distance.should eq(0)
    end

    it "finds oral from 'orla' (now an alias)" do
      result = JosieHealth::LUT::RouteLUT.fuzzy_normalize("orla")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("oral")
      result.not_nil!.distance.should eq(0)
    end

    it "finds vaped from 'vapd' (now an alias)" do
      result = JosieHealth::LUT::RouteLUT.fuzzy_normalize("vapd")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("vaped")
      result.not_nil!.distance.should eq(0)
    end

    it "finds subcutaneous from 'subcutanous' via fuzzy" do
      result = JosieHealth::LUT::RouteLUT.fuzzy_normalize("subcutanous")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("subcutaneous")
    end

    it "rejects strings <= 2 chars" do
      JosieHealth::LUT::RouteLUT.fuzzy_normalize("iv").should be_nil
      JosieHealth::LUT::RouteLUT.fuzzy_normalize("im").should be_nil
    end

    it "rejects unknown routes" do
      JosieHealth::LUT::RouteLUT.fuzzy_normalize("zzzzz").should be_nil
    end
  end
end

describe JosieHealth::LUT do
  describe ".fuzzy_normalize_substance" do
    it "delegates to DrugLUT.fuzzy_normalize" do
      result = JosieHealth::LUT.fuzzy_normalize_substance("ketamine")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("ketamine")
    end
  end

  describe ".fuzzy_normalize_route" do
    it "delegates to RouteLUT.fuzzy_normalize" do
      result = JosieHealth::LUT.fuzzy_normalize_route("oral")
      result.should_not be_nil
      result.not_nil!.canonical.should eq("oral")
    end
  end
end
