# typed: false
require "rails_helper"
require_relative "page_spec_helper"

describe Domain::Fa::Parser::Page do
  include Domain::Fa::Parser::PageSpecHelper

  it "sees_not_logged_in" do
    parser = get_parser("login_page.html", require_logged_in: false)
    refute parser.logged_in?
    assert_nil parser.logged_in_user
  end

  it "throws_if_not_logged_in_by_default" do
    assert_raises(Domain::Fa::Parser::NotLoggedInError) do
      get_parser("login_page.html")
    end
  end

  it "submission_not_found_does_not_throw" do
    parser = get_parser("submission_not_found_in_db.html")
    refute parser.logged_in?
    assert parser.submission_not_found?
  end

  it "sees_logged_in_correctly" do
    parser = get_parser("index_logged_in_zzreg.html")
    assert parser.logged_in?
    assert_equal parser.logged_in_user, "zzreg"
    refute parser.submission_not_found?
  end

  it "user_page_is_correct" do
    parser = get_parser("user_page_miles_df.html")
    assert parser.logged_in?
    assert_page_type parser, :probably_user_page?
    assert parser.probably_user_page?
    assert_equal "-08:00", parser.logged_in_user_tz.formatted_offset
    up = parser.user_page

    assert_equal "Miles-DF", up.name
    assert up.profile_html, /COMMISSIONS: CLOSED/

    assert_equal 592, up.num_submissions
    assert_equal 2_005_424, up.num_pageviews
    assert_equal 40_192, up.num_comments_recieved
    assert_equal 7_227, up.num_comments_given
    assert_equal 6, up.num_journals
    assert_equal 1_236_200, up.num_favorites
    assert_equal Time.parse("Jan 12th, 2006 07:52 -08:00"), up.registered_since
    assert_equal "//a.furaffinity.net/1556545516/miles-df.gif",
                 up.profile_thumb_url
  end

  it "can parse admin user page" do
    parser = get_parser "user_page_dragoneer.html"
    assert parser.logged_in?
    assert_page_type parser, :probably_user_page?
    up = parser.user_page
    assert_equal "Dragoneer", up.name
    assert_equal 1_460_919, up.num_pageviews
    assert_match /Site Owner/, up.profile_html
  end

  it "correctly parses a user when there's an admin elsewhere on the page" do
    parser = get_parser "user_page_childofpeace.html"
    assert parser.logged_in?
    assert_page_type parser, :probably_user_page?
    up = parser.user_page
    assert_equal "Childofpeace2004", up.name
    assert_equal 419, up.num_pageviews
    assert_match /Not Available/, up.profile_html
  end

  it "parses special accounts with spaces in the name" do
    parser = get_parser "user_page_fa_staff.html"
    assert parser.logged_in?
    assert_page_type parser, :probably_user_page?
    up = parser.user_page
    assert_equal "Fur Affinity Staff", up.name
    assert_equal 15_962, up.num_pageviews
    assert_match /placeholder for sending system notices/, up.profile_html
  end

  it "gallery_is_correct" do
    parser = get_parser "gallery_page_miles_df.html"
    assert parser.logged_in?
    assert_page_type parser, :probably_listings_page?

    listings = parser.submissions_parsed
    assert_equal 48, listings.length
    assert_equal 51_039_083, listings.first.id

    first_listing = listings.first
    assert_equal "Miles-DF", first_listing.artist
    assert_equal "/user/miles-df/", first_listing.artist_user_page_path
    assert_equal "booty", first_listing.title
    assert_equal "/view/51039083/", first_listing.view_path
    assert_equal "//t.furaffinity.net/51039083@300-1676577120.jpg",
                 first_listing.thumb_path

    assert_equal 2, parser.submission_folders.length

    parser_empty = get_parser "gallery_page_empty_miles_df.html"
    assert parser_empty.probably_listings_page?
    assert_equal 0, parser_empty.submissions_parsed.length
  end

  it "gallery_is_correct_with_folders" do
    parser = get_parser "gallery_page_with_folders_feretta.html"
    assert_page_type parser, :probably_listings_page?

    listings = parser.submissions_parsed
    assert_equal 48, listings.length

    first_listing = listings.first
    assert_equal 51_066_954, first_listing.id
    assert_equal "Feretta", first_listing.artist
    assert_equal "/user/feretta/", first_listing.artist_user_page_path
    assert_equal "[Comm] Size and property - 2/3", first_listing.title
    assert_equal "/view/51066954/", first_listing.view_path
    assert_equal "//t.furaffinity.net/51066954@200-1676761927.jpg",
                 first_listing.thumb_path

    last_listing = listings.last
    assert_equal 50_320_568, last_listing.id
    assert_equal "Feretta", last_listing.artist
    assert_equal "/user/feretta/", last_listing.artist_user_page_path
    assert_equal "[Comm] Sizable date - 1/3", last_listing.title
    assert_equal "/view/50320568/", last_listing.view_path
    assert_equal "//t.furaffinity.net/50320568@200-1671750577.jpg",
                 last_listing.thumb_path

    folders = parser.submission_folders
    assert_equal({ href: "/scraps/feretta/", title: "Scraps" }, folders[0])
    assert_equal(
      {
        href: "/gallery/feretta/folder/60236/Tale-of-Tails",
        title: "Tale of Tails",
      },
      folders[1],
    )
    assert_equal(
      {
        href: "/gallery/feretta/folder/60234/Illustrations",
        title: "Illustrations",
      },
      folders[2],
    )
    assert_equal(
      { href: "/gallery/feretta/folder/229520/Marketing", title: "Marketing" },
      folders[9],
    )
    assert_equal(
      { href: "/gallery/feretta/folder/820310/3D", title: "3D" },
      folders[13],
    )
  end

  it "browse_page" do
    # https://www.furaffinity.net/browse/

    parser = get_parser "browse_page.html"
    assert_page_type parser, :probably_listings_page?

    listings = parser.submissions_parsed
    # FA appears to respond with only 47?
    assert_equal 47, listings.length

    first = listings.first
    assert_equal 51_067_352, first.id
    assert_equal "CleverDerpy", first.artist
    assert_equal "/user/cleverderpy/", first.artist_user_page_path
    assert_equal "The Second Kind of Tarpit", first.title
    assert_equal "/view/51067352/", first.view_path
    assert_equal "//t.furaffinity.net/51067352@400-1676764142.jpg",
                 first.thumb_path
  end

  it "adult_submission" do
    parser = get_parser("submission_51067352_adult.html")
    assert_page_type parser, :probably_submission?
    sub = parser.submission
    assert_equal 51_067_352, sub.id
    assert_equal "CleverDerpy", sub.artist
    assert_equal "/user/cleverderpy/", sub.artist_user_page_path
    assert_equal "The Second Kind of Tarpit", sub.title
    assert_equal :adult, sub.rating
    assert_equal "//d.furaffinity.net/art/cleverderpy/1676764142/1676764142.cleverderpy_tarryn_and_rosie_shaymin_tar_cv_00001.png",
                 sub.full_res_img
    assert_equal Time.parse("Feb 18, 2023 06:49 PM -08:00"), sub.posted_date
    assert_equal "Artwork (Digital)", sub.category
    assert_equal "Vore", sub.theme
    assert_equal "Unspecified / Any", sub.species
    assert_equal "Multiple characters", sub.gender
    assert_equal 201, sub.num_views
    assert_equal 0, sub.num_comments
    assert_equal 15, sub.num_favorites
    assert_equal "1920x1080", sub.resolution_str
    assert sub.description_html =~ /belongs to Nintendo/
  end

  it "submission_is_correct" do
    parser = get_parser("submission_51067333_blauhaher.html")
    assert_page_type parser, :probably_submission?

    sub = parser.submission
    assert_equal 51_067_333, sub.id
    assert_equal "BlauHaher", sub.artist
    assert_equal "/user/blauhaher/", sub.artist_user_page_path
    assert_equal "//a.furaffinity.net/1676066883/blauhaher.gif",
                 sub.artist_avatar_url
    assert_equal '"CUTIE!"', sub.title
    assert_equal :general, sub.rating
    assert_equal "//d.furaffinity.net/art/blauhaher/1676764049/1676764049.blauhaher_хорнь.png",
                 sub.small_img
    assert_equal "//d.furaffinity.net/art/blauhaher/1676764049/1676764049.blauhaher_хорнь.png",
                 sub.full_res_img
    assert_equal Time.parse("Feb 18, 2023 06:47 PM -08:00"), sub.posted_date
    assert_equal "Artwork (Digital)", sub.category
    assert_equal "Doodle", sub.theme
    assert_equal "Unspecified / Any", sub.species
    assert_equal "Any", sub.gender
    assert_equal 1, sub.num_favorites
    assert_equal 0, sub.num_comments
    assert_equal 17, sub.num_views
    assert_equal "1024x1024", sub.resolution_str
    assert_equal [], sub.keywords_array
    assert sub.description_html =~ /really adore at the way this/
  end

  it "submission_with_tags" do
    parser = get_parser("submission_19190013_korichi.html")
    assert_page_type parser, :probably_submission?

    sub = parser.submission
    assert_equal 19_190_013, sub.id
    assert_equal "Korichi", sub.artist
    assert_equal "General Furry Art", sub.theme
    assert_equal "Artwork (Digital)", sub.category
    assert_equal :general, sub.rating
    assert_equal "1280x914", sub.resolution_str
    assert_equal 6272, sub.num_views
    assert_equal "Male", sub.gender
    assert_equal "Dragon (Other)", sub.species
    assert_equal Time.parse("Feb 24, 2016 12:51 AM -08:00"), sub.posted_date
    assert_equal %w[kobold frog hunting male frog mighty hunter],
                 sub.keywords_array
  end

  it "text_only_submission" do
    parser = get_parser("submission_10327196_icyfoxy_text_only.html")
    assert_page_type parser, :probably_submission?

    sub = parser.submission
    assert_equal 10_327_196, sub.id
    assert_equal "icyfoxy", sub.artist
    assert_equal "/user/icyfoxy/", sub.artist_user_page_path
    assert_equal "Ancient Awakening- Wereyena herm TF (By Wyraachur)", sub.title
    assert_equal "//d.furaffinity.net/download/art/icyfoxy/stories/1488278723/1365480658.icyfoxy_sonya_the_wereyena.txt",
                 sub.full_res_img
    assert_equal :adult, sub.rating
    assert_equal 819, sub.num_views
    assert_equal 0, sub.num_comments
    assert_equal 12, sub.num_favorites
    assert_equal "Hyena", sub.species
    assert_equal "Story", sub.category
    assert_equal "General Furry Art", sub.theme
    assert_equal "50x50", sub.resolution_str
    assert_equal %w[
                   Transformation
                   TF
                   Change
                   Male
                   Herm
                   TFTG
                   Transgender
                   Growth
                   Fang
                   Breasts
                   Ass
                   Cock
                   Balls
                   Icyfoxy
                   Wyraachur
                   wereyena
                   Sonya
                 ],
                 sub.keywords_array
    assert_equal "Other / Not Specified", sub.gender
    expect(Time.parse("Apr 9, 2013 12:10 AM -07:00")).to be_within(1.second).of(
      sub.posted_date,
    )
  end

  it "submission_id_is_correct_20340491" do
    parser = get_parser("submission_20340491.html")
    assert_page_type parser, :probably_submission?

    sub = parser.submission
    assert_equal 20_340_491, sub.id
    assert_equal "Skye", sub.title
    assert_equal :general, sub.rating
  end

  it "flash_submission" do
    parser = get_parser("submission_21956745_flash.html")
    assert_page_type parser, :probably_submission?

    sub = parser.submission
    assert_equal 21_956_745, sub.id
    assert_equal "Drake Test: Take One", sub.title
    assert_equal :mature, sub.rating
    assert_equal "Tasuric", sub.artist
    assert_equal "Flash", sub.category
    assert_equal "Animal related (non-anthro)", sub.theme
    assert (/A closeup of the drake g/) =~ sub.description_html
    assert_equal "//d.furaffinity.net/download/art/tasuric/1481384060/1481384060.tasuric_tasflash_camnom.swf",
                 sub.full_res_img
  end

  it "submission_id_16750893_category" do
    parser = get_parser("submission_16750893.html")
    assert_page_type parser, :probably_submission?

    sub = parser.submission
    assert_equal 16_750_893, sub.id
    assert_equal "Day at the range with kagura", sub.title
    assert_equal :mature, sub.rating
    assert_equal "Soulhunt3r", sub.artist
    # this post in particular is special - it has no category nor theme (??)
    assert_nil sub.category
    assert_nil sub.theme
    assert_equal "Western Dragon", sub.species
    assert (/Today was a good day kagura/) =~ sub.description_html
    assert_equal "//d.furaffinity.net/art/soulhunt3r/1433541729/1433541729.soulhunt3r_image.jpg",
                 sub.full_res_img
  end

  it "watchlist_zzreg" do
    parser = get_parser("watchlist_zzreg.html", require_logged_in: false)
    user_list = parser.user_list
    assert_equal 10, user_list.length
    assert_equal "-creeps", user_list[0].url_name
    assert_equal "-creeps", user_list[0].name

    assert_equal "~nicky~", user_list.last.url_name
    assert_equal "~Nicky~", user_list.last.name
  end

  it "parses home page correctly" do
    parser = get_parser("home.html", require_logged_in: true)
    assert_page_type parser, :probably_listings_page?

    listings = parser.submissions_parsed
    assert_equal 112, listings.length
    assert_equal 52_807_274, listings.first.id

    first_listing = listings.first
    assert_equal "LemontasticTobster", first_listing.artist
    assert_equal "/user/lemontastictobster/",
                 first_listing.artist_user_page_path
    assert_equal "FREE OC Animatronic Raffle! (READ DESC)", first_listing.title
    assert_equal "/view/52807274/", first_listing.view_path
    assert_equal "//t.furaffinity.net/52807274@200-1688750295.jpg",
                 first_listing.thumb_path
  end

  it "has the right recent favorites and users" do
    parser = get_parser("user_page_tenzing.html")
    assert_page_type parser, :probably_user_page?
    up = parser.user_page
    assert_equal up.recent_fav_fa_ids.length, 20
    assert_equal up.recent_fav_fa_ids,
                 [
                   53_255_068,
                   53_140_782,
                   31_417_552,
                   53_050_580,
                   53_050_352,
                   52_961_865,
                   52_916_591,
                   34_833_220,
                   49_050_098,
                   52_875_656,
                   52_826_012,
                   52_807_234,
                   35_678_896,
                   52_773_671,
                   52_750_513,
                   38_721_736,
                   28_430_551,
                   50_976_208,
                   51_785_387,
                   52_449_028,
                 ]

    assert_equal up.recent_watchers.map(&:to_a),
                 [
                   %w[Boxstuff boxstuff],
                   %w[Shuffl3 shuffl3],
                   %w[Fervidus fervidus],
                   %w[SargentTNT sargenttnt],
                   %w[asitanneko asitanneko],
                   %w[overfuck overfuck],
                   %w[LuciaTheCelestialVixen luciathecelestialvixen],
                   %w[goodcabinet goodcabinet],
                   %w[Lethal_Dose1 lethaldose1],
                   %w[Kingtiger2101 kingtiger2101],
                   %w[Nilla_Arts nillaarts],
                   %w[Riku_Anita rikuanita],
                 ]

    assert_equal up.recent_watching.map(&:to_a),
                 [
                   %w[Seyorrol seyorrol],
                   %w[Glopossum glopossum],
                   %w[Jeniak jeniak],
                   %w[rajii rajii],
                   %w[Kenjomik kenjomik],
                   %w[fluff-kevlar fluff-kevlar],
                   %w[Fisk fisk],
                   %w[Crinz crinz],
                   %w[Tabuley tabuley],
                   %w[Braeburned braeburned],
                   %w[knifeDragon knifedragon],
                   %w[LotusGarden lotusgarden],
                 ]
  end

  it "works with FA+ users" do
    parser = get_parser("marzimoo_user_page.html")
    assert_page_type parser, :probably_user_page?
    up = parser.user_page
    assert_equal up.name, "MarziMoo"
    assert_equal up.registered_since, Time.parse("Sep 7, 2018 06:19 -0700")
  end

  it "handles pages with no favorites" do
    parser = get_parser("azurathefox_user_page.html")
    assert_page_type parser, :probably_user_page?
    up = parser.user_page
    assert_equal up.name, "AzuraTheFox"
    assert_equal up.recent_fav_fa_ids, []
    assert_equal up.recent_watchers.first.to_a, %w[SN214549 sn214549]
    assert_equal up.recent_watchers.last.to_a, %w[Duckyou7464 duckyou7464]
    assert_equal up.recent_watching, []
  end

  it "parses older format for favs pages" do
    parser = get_parser("favs_zzreg_page_0_first.html")
    assert_page_type parser, :probably_listings_page?
    assert_equal parser.submissions_parsed.length, 2
    assert_equal parser.favorites_next_button_id, "1074627373"
  end

  it "parses 2024 format for favs pages with a next button" do
    parser = get_parser("favs_zzreg_page_1_2024_format.html")
    assert_page_type parser, :probably_listings_page?
    assert_equal parser.submissions_parsed.length, 48
    assert_equal parser.favorites_next_button_id, "1436907409"
  end

  it "parses 2024 format for favs pages with no next button" do
    parser = get_parser("favs_zzreg_page_last_2024_format.html")
    assert_page_type parser, :probably_listings_page?
    assert_equal parser.submissions_parsed.length, 37
    assert_nil parser.favorites_next_button_id
  end

  it "parses 2025 format" do
    parser = get_parser("submission_59584979_2025_style_keywords.html")
    assert_page_type parser, :probably_submission?
    sub = parser.submission

    assert_equal 59_584_979, sub.id
    assert_equal "Felureii", sub.artist
    assert_equal "/user/felureii/", sub.artist_user_page_path
    assert_equal "//a.furaffinity.net/1733967392/felureii.gif",
                 sub.artist_avatar_url
    assert_equal "Practice", sub.title
    assert_equal :general, sub.rating
    assert_equal "//d.furaffinity.net/art/felureii/1737390216/1737390216.felureii_0f3gn7ifbl8.jpg",
                 sub.small_img
    assert_equal "//d.furaffinity.net/art/felureii/1737390216/1737390216.felureii_0f3gn7ifbl8.jpg",
                 sub.full_res_img
    assert_equal Time.parse("Jan 20, 2025 11:23 AM -08:00"), sub.posted_date
    assert_equal "Artwork (Digital)", sub.category
    assert_equal "All", sub.theme
    assert_equal "Unspecified / Any", sub.species
    assert_equal "Any", sub.gender
    assert_equal 0, sub.num_favorites
    assert_equal 0, sub.num_comments
    assert_equal 16, sub.num_views
    assert_equal "677x672", sub.resolution_str
    assert_equal %w[art troll fantasy], sub.keywords_array
    assert_equal "Nothing special, just practice.", sub.description_html.strip
  end

  it "parses num_watched_by and num_watching" do
    parser =
      get_parser_at(
        Rails.root.join(
          "test/fixtures/files/domain/fa/user_page/user_page_koul_over_threshold_watchers.html",
        ),
      )
    assert_page_type parser, :probably_user_page?
    up = parser.user_page
    assert_equal 25, up.num_watched_by
    assert_equal 14, up.num_watching
  end

  context "recent gallery submission parsing" do
    it "works when the user has recent gallery submissions" do
      parser =
        get_parser_at(
          Rails.root.join(
            "test/fixtures/files/domain/fa/user_page/user_page_kutua_one_recent_one_total_gallery.html",
          ),
        )
      assert_page_type parser, :probably_user_page?
      up = parser.user_page
      expect(up.recent_gallery_fa_ids).to eq([60_073_062])
    end

    it "works when the user has no submissions" do
      parser =
        get_parser_at(
          Rails.root.join(
            "test/fixtures/files/domain/fa/user_page/user_page_angelpawqt.html",
          ),
        )
      assert_page_type parser, :probably_user_page?
      up = parser.user_page
      expect(up.recent_gallery_fa_ids).to eq([])
    end
  end

  context "march 2025 user page layout updates" do
    it "gets the right page fields" do
      parser =
        get_parser_at(
          Rails.root.join(
            "test/fixtures/files/domain/fa/user_page/user_page_gnilica_03_2025_update.html",
          ),
        )
      assert_page_type parser, :probably_user_page?
      assert_equal "-05:00", parser.logged_in_user_tz.formatted_offset

      up = parser.user_page
      assert_equal 89, up.num_pageviews
      assert_equal 1, up.num_watched_by
      assert_equal 2, up.num_watching
      assert_equal 3, up.num_favorites
      assert_equal 1, up.num_comments_recieved
      assert_equal 0, up.num_comments_given
      assert_equal 1, up.num_submissions
      assert_equal "gnilica", up.name
      assert_equal :active, up.account_status

      assert_equal ["WildSteppe"], up.recent_watchers.map(&:name)
      assert_equal %w[WildSteppe just_dosha], up.recent_watching.map(&:name)

      assert_equal ["wildsteppe"], up.recent_watchers.map(&:url_name)
      assert_equal %w[wildsteppe justdosha], up.recent_watching.map(&:url_name)
      assert_equal Time.parse("Nov 8, 2023 12:16 PM -0500"), up.registered_since

      assert_equal [55_315_013], up.recent_gallery_fa_ids
      assert_equal [], up.recent_fav_fa_ids
    end

    it "works with deceased account" do
      parser =
        get_parser_at(
          Rails.root.join(
            "test/fixtures/files/domain/fa/user_page/user_page_dragoneer_03_2025_update.html",
          ),
        )
      assert_page_type parser, :probably_user_page?
      up = parser.user_page
      assert_equal "Dragoneer", up.name
      assert_equal :deceased, up.account_status
      assert_equal [
                     57_640_158,
                     57_631_759,
                     57_574_876,
                     57_573_028,
                     57_557_857,
                     57_550_264,
                     57_536_644,
                     57_414_032,
                     57_268_996,
                     57_253_476,
                     57_058_401,
                     56_515_114,
                     57_056_700,
                     56_527_550,
                     56_984_570,
                     56_891_225,
                     56_975_323,
                     56_943_559,
                     56_912_979,
                     56_912_982,
                   ],
                   up.recent_fav_fa_ids

      assert_equal [
                     52_163_102,
                     52_151_943,
                     52_141_347,
                     52_138_550,
                     52_130_147,
                     52_082_293,
                     52_082_271,
                     51_908_294,
                     51_894_361,
                     51_881_908,
                     51_857_730,
                     51_799_409,
                     51_785_417,
                     51_783_907,
                     51_746_087,
                     51_746_065,
                     51_362_239,
                     51_301_232,
                     51_277_956,
                     51_264_723,
                   ],
                   up.recent_gallery_fa_ids

      assert_equal %w[
                     kingexcalibur
                     drembonaarkrah
                     chakat-jaggerfrost
                     neo-kitsune
                     wolfiezwolf
                     idididid
                     uddebo86
                     wyq
                     eternalkrush
                     sabredragon
                     honeycumb
                     anthroperson18
                   ],
                   up.recent_watchers.map(&:url_name)

      assert_equal %w[
                     augurynite
                     inkchubs
                     larru-larru
                     zekeobsidian
                     appleofmyshampoo
                     vaspi
                     dissimulated
                     omeome
                     mazaku
                     lamor
                     grasseater98
                     videlthewusky
                   ],
                   up.recent_watching.map(&:url_name)
    end

    it "works with a staff account" do
      parser =
        get_parser_at(
          Rails.root.join(
            "test/fixtures/files/domain/fa/user_page/user_page_ash_staff_03_2025_update.html",
          ),
        )
      assert_page_type parser, :probably_user_page?
      up = parser.user_page
      assert_equal "Ash", up.name
      assert_equal :admin, up.account_status
    end
  end

  it "works with new submission page format without gender field" do
    parser =
      get_parser_at(
        Rails.root.join(
          "test/fixtures/files/domain/fa/parser/redux/submission_48178881_no_gender_fields.html",
        ),
      )
    assert_page_type parser, :probably_submission?
    sub = parser.submission
    assert_equal "Artwork (Digital)", sub.category
    assert_equal "Unspecified / Any", sub.species
    assert_equal "Animal related (non-anthro)", sub.theme
    assert_equal 128, sub.num_favorites
    assert_equal 3, sub.num_comments
    assert_equal 1706, sub.num_views
    assert_equal Time.parse("Jul 20, 2022 01:34 PM -07:00"), sub.posted_date
    assert_nil sub.gender
    assert_equal :adult, sub.rating
  end

  describe "an old old user page" do
    let(:parser) do
      get_fa_parser(
        "user_page/user_page_koraksinatra_old_old.html",
        version: Domain::Fa::Parser::Page::VERSION_0,
      )
    end

    it "has the right submission_json_data" do
      up = parser.user_page
      expect(up.submissions_json_data.length).to eq(28)
      sub_21828172 = up.submissions_json_data.find { |f| f.fa_id == 21_828_172 }
      expect(sub_21828172).to be_present
      expect(sub_21828172.title).to eq("Turkey Lady")
      expect(sub_21828172.user_full_name).to eq("Korak_Sinatra")
      expect(sub_21828172.url_name).to eq("koraksinatra")
      expect(sub_21828172.date_full).to eq("Nov 26th, 2016 05:04 PM")
      expect(sub_21828172.faved_at).to eq(
        Time.parse("Nov 26th, 2016 05:04 PM -08:00"),
      )

      sub_21822027 = up.submissions_json_data.find { |f| f.fa_id == 21_822_027 }
      expect(sub_21822027).to be_present
      expect(sub_21822027.title).to eq("Qhala - Commission")
      expect(sub_21822027.user_full_name).to eq("discoverychannelofficial")
      expect(sub_21822027.url_name).to eq("discoverychannelofficial")
      expect(sub_21822027.date_full).to eq("Nov 25th, 2016 09:03 PM")
    end

    it "has the right recent_fav_fa_ids" do
      up = parser.user_page
      expect(up.recent_fav_fa_ids).to eq(
        [
          20_027_473,
          20_027_473,
          21_828_538,
          21_828_379,
          21_827_826,
          21_822_027,
          21_821_169,
          21_820_661,
          21_815_021,
          21_810_631,
          21_803_422,
          21_803_299,
          21_803_014,
          21_802_133,
          21_796_639,
        ],
      )
    end
  end

  describe "an old old admin user page" do
    let(:parser) do
      get_fa_parser(
        "user_page/user_page_chase_old_old.html",
        version: Domain::Fa::Parser::Page::VERSION_0,
      )
    end

    it "is detected as a user page" do
      expect(parser.probably_user_page?).to eq(true)
    end

    it "has the right recent_fav_fa_ids" do
      up = parser.user_page
      expect(up.recent_fav_fa_ids).to eq([])
    end
  end

  describe "a favs page with duplicate favs on it" do
    let(:parser) do
      get_fa_parser(
        "favorites/favs_narse_2023_duplicate_ids.html",
        version: Domain::Fa::Parser::Page::VERSION_2,
      )
    end

    it "has the right recent_fav_fa_ids" do
      up = parser.user_page
      expect(up.recent_fav_fa_ids).to eq([])
      expect(up.recent_gallery_fa_ids).to eq([])
    end
  end

  describe "parsing a user page" do
    describe "#submissions_json_data" do
      it "works with a user page with old embedded json format" do
        parser =
          get_fa_parser("user_page/user_page_zzreg_one_scrap_submission.html")
        up = parser.user_page
        expect(up.submissions_json_data.length).to eq(20)

        fav0 = up.submissions_json_data.find { |f| f.fa_id == 59_785_748 }
        expect(fav0).to be_present
        expect(fav0.fa_id).to eq(59_785_748)
        expect(fav0.user_full_name).to eq("-creeps")
        expect(fav0.url_name).to eq("-creeps")
        expect(fav0.title).to eq("Monty")
        date_full = "Feb 7, 2025 01:32 AM"
        expect(fav0.date_full).to eq(date_full)
        faved_at = Time.parse(date_full + " -08:00")
        expect(fav0.faved_at).to eq(faved_at)
      end

      it "works with a user page with new json in its own tag" do
        parser =
          get_fa_parser(
            "user_page/user_page_zzreg_new_submission_data_elem.html",
          )
        up = parser.user_page
        expect(up.submissions_json_data.length).to eq(20)

        fav0 = up.submissions_json_data.find { |f| f.fa_id == 60_272_845 }
        expect(fav0).to be_present
        expect(fav0.fa_id).to eq(60_272_845)
        expect(fav0.user_full_name).to eq("MuskyDusky")
        expect(fav0.url_name).to eq("muskydusky")
        expect(fav0.title).to eq("Breeding Season in the Mountains")
        date_full = "Jul 8, 2025 01:04 PM"
        expect(fav0.date_full).to eq(date_full)
        faved_at = Time.parse(date_full + " -07:00")
        expect(fav0.faved_at).to eq(faved_at)

        fav19 = up.submissions_json_data.find { |f| f.fa_id == 59_860_095 }
        expect(fav19.title).to eq("🔞Outdoor Cookie")
        expect(fav19.user_full_name).to eq("KeaveMind")
        expect(fav19.url_name).to eq("keavemind")
        date_full = "Mar 11, 2025 08:32 PM"
        expect(fav19.date_full).to eq(date_full)
        faved_at = Time.parse(date_full + " -07:00")
        expect(fav19.faved_at).to eq(faved_at)
      end
    end
  end

  describe "parsing a favorites page" do
    describe "#most_recent_faved_at_time" do
      it "works with a favorites page with old embedded json format" do
        parser = get_fa_parser("favorites/favs_zzreg_page_0_first.html")
        expect(parser.most_recent_faved_at_time).to eq(
          Time.parse("May 11, 2023 10:54 AM -07:00"),
        )
      end

      it "works with a favorites page with new json format" do
        parser =
          get_fa_parser("favorites/favs_zzreg_page_0_new_data_format.html")
        expect(parser.most_recent_faved_at_time).to eq(
          Time.parse("Jul 8, 2025 01:04 PM -07:00"),
        )
      end
    end
  end

  describe "#server_local_time" do
    it "works with a user page from 2016" do
      parser =
        get_fa_parser(
          "user_page/user_page_fenford_11_2016.html",
          version: Domain::Fa::Parser::Page::VERSION_0,
        )

      expect(parser.server_local_time_string).to eq("Nov 26th, 2016 01:34 PM")
      expect(parser.logged_in_user_tz.formatted_offset).to eq("-08:00")
      expect(parser.server_local_time).to eq(
        Time.parse("Nov 26th, 2016 01:34 PM -0800"),
      )
    end

    it "works with a user page from 2025" do
      parser =
        get_fa_parser(
          "user_page/user_page_fenford_07_2025.html",
          version: Domain::Fa::Parser::Page::VERSION_2,
        )

      expect(parser.server_local_time_string).to eq("Jul 8, 2025 03:34 PM")
      expect(parser.logged_in_user_tz.formatted_offset).to eq("-08:00")
      expect(parser.server_local_time).to eq(
        Time.parse("Jul 8th, 2025 3:34 PM -0700"),
      )
    end
  end

  describe "#compute_server_time_zone_offset" do
    it "works with a user page from 2016" do
      # HttpLogEntry id=19
      parser =
        get_fa_parser(
          "user_page/user_page_fenford_11_2016.html",
          version: Domain::Fa::Parser::Page::VERSION_0,
        )
      expect(parser.logged_in_user_tz.formatted_offset).to eq("-08:00")
    end

    it "works with a user page from 2025" do
      parser =
        get_fa_parser(
          "user_page/user_page_fenford_07_2025.html",
          version: Domain::Fa::Parser::Page::VERSION_2,
        )
      expect(parser.logged_in_user_tz.formatted_offset).to eq("-08:00")
    end
  end

  def get_fa_parser(
    file,
    require_logged_in: true,
    version: Domain::Fa::Parser::Page::VERSION_2
  )
    path = File.join("domain/fa", file)
    get_parser_at(path, require_logged_in:, version:)
  end

  def get_parser(file, require_logged_in: true)
    path = File.join("domain/fa/parser/redux", file)
    get_parser_at(path, require_logged_in:)
  end

  def get_parser_at(
    path,
    require_logged_in: true,
    version: Domain::Fa::Parser::Page::VERSION_2
  )
    path = path.to_s
    contents =
      SpecUtil.read_fixture_file(path) || raise("Couldn't open #{path}")
    parser =
      Domain::Fa::Parser::Page.new(
        contents,
        require_logged_in: require_logged_in,
      )
    assert_equal version,
                 parser.page_version,
                 "parser version mismatch for file #{path}"
    parser
  end
end
