# typed: false
require "rails_helper"

RSpec.describe HttpLogEntry, type: :model do
  describe "validations" do
    subject { build(:http_log_entry) }

    it { should validate_presence_of(:uri_scheme) }
    it { should validate_presence_of(:uri_host) }
    it { should validate_presence_of(:uri_path) }
    it { should validate_presence_of(:verb) }
    it { should validate_presence_of(:performed_by) }
    it { should validate_presence_of(:status_code) }
    it { should validate_presence_of(:response_time_ms) }
    it { should validate_presence_of(:content_type) }
    it { should validate_presence_of(:requested_at) }

    it { should validate_length_of(:response_sha256).is_equal_to(32) }
  end

  describe "associations" do
    it { should belong_to(:response).class_name("::BlobFile") }
    it { should belong_to(:request_headers).class_name("::HttpLogEntryHeader") }
    it do
      should belong_to(:response_headers).class_name("::HttpLogEntryHeader")
    end
    it do
      should belong_to(:caused_by_entry).class_name("::HttpLogEntry").optional
    end
    it { should have_many(:triggered_entries).class_name("::HttpLogEntry") }
  end

  describe "immutability" do
    let(:entry) { create(:http_log_entry) }

    it "prevents updates" do
      expect { entry.update!(verb: :post) }.to raise_error(
        ActiveRecord::ReadOnlyRecord,
      )
    end

    it "prevents deletion" do
      expect { entry.destroy }.to raise_error(ActiveRecord::ReadOnlyRecord)
    end
  end

  describe "#uri=" do
    let(:entry) { build(:http_log_entry) }

    it "parses and sets URI components" do
      entry.uri = "https://example.com/path?query=value#fragment"

      expect(entry.uri_scheme).to eq("https")
      expect(entry.uri_host).to eq("example.com")
      expect(entry.uri_path).to eq("/path")
      expect(entry.uri_query).to eq("query=value")
      expect(entry.uri_hash).to eq("fragment")
    end

    it "handles URIs without optional components" do
      entry.uri = "https://example.com/path"

      expect(entry.uri_scheme).to eq("https")
      expect(entry.uri_host).to eq("example.com")
      expect(entry.uri_path).to eq("/path")
      expect(entry.uri_query).to be_nil
      expect(entry.uri_hash).to be_nil
    end

    it "parses complex URLs with multiple query parameters" do
      entry.uri =
        "https://www.example.com/big/path/here?and=query&other=query2#smaz"

      expect(entry.uri_scheme).to eq("https")
      expect(entry.uri_host).to eq("www.example.com")
      expect(entry.uri_path).to eq("/big/path/here")
      expect(entry.uri_query).to eq("and=query&other=query2")
      expect(entry.uri_hash).to eq("smaz")
    end
  end

  describe "#uri" do
    let(:entry) { build(:http_log_entry, :with_query, :with_fragment) }

    it "reconstructs the full URI" do
      expect(entry.uri).to be_a(Addressable::URI)
      expect(entry.uri.to_s).to eq(
        "https://#{entry.uri_host}/path?foo=bar&baz=qux#section1",
      )
    end
  end

  describe "#uri_str" do
    it "returns full URI string" do
      entry = build(:http_log_entry, :with_query, :with_fragment)
      expect(entry.uri_str).to eq(
        "https://#{entry.uri_host}/path?foo=bar&baz=qux#section1",
      )
    end

    it "handles URI without optional components" do
      entry = build(:http_log_entry)
      expect(entry.uri_str).to eq("https://#{entry.uri_host}/path")
    end
  end

  describe "#uri_str_without_host" do
    it "returns path and query components" do
      entry = build(:http_log_entry, :with_query, :with_fragment)
      expect(entry.uri_str_without_host).to eq("/path?foo=bar&baz=qux#section1")
    end

    it "handles path only" do
      entry = build(:http_log_entry)
      expect(entry.uri_str_without_host).to eq("/path")
    end
  end

  describe ".find_by_uri_host_path" do
    let!(:entry) { create(:http_log_entry) }

    it "finds entry by URI string" do
      found =
        described_class.find_by_uri_host_path("https://#{entry.uri_host}/path")
      expect(found).to eq(entry)
    end

    it "finds entry by Addressable::URI" do
      uri = Addressable::URI.parse("https://#{entry.uri_host}/path")
      found = described_class.find_by_uri_host_path(uri)
      expect(found).to eq(entry)
    end

    it "returns nil for non-existent URI" do
      found =
        described_class.find_by_uri_host_path("https://nonexistent.com/path")
      expect(found).to be_nil
    end
  end

  describe "#response_size" do
    let(:entry) { create(:http_log_entry) }

    context "when response association is loaded" do
      it "returns size from response object" do
        test_content = "test content"
        entry.response = build(:blob_file, contents: test_content)
        expect(entry.response_size).to eq(test_content.bytesize)
      end
    end

    context "when response association is not loaded" do
      it "queries size directly from database" do
        size = entry.response.size_bytes
        entry.association(:response).reset
        expect(entry.response_size).to eq(size)
      end
    end
  end
end
