require 'rails_helper'

RSpec.describe PostMetadataFetcher do
  Response = Struct.new(:body)

  def fetch_with_published_time(value, url: 'https://example.com/video')
    html = <<~HTML
      <html><head>
        <meta property="article:published_time" content="#{ value }">
      </head></html>
    HTML
    uri = URI.parse(url)
    allow(Preview::UrlSafety).to receive(:validate).with(url).and_return([uri, ['8.8.8.8']])
    allow(Preview::HttpFetcher).to receive(:fetch).and_return(Response.new(html))
    allow(Preview::HtmlMetadataExtractor).to receive(:extract)
        .and_return(title: 'title', image_url: nil)

    described_class.fetch(url)
  end

  it 'builds ranges explicitly for year, month, and day precision' do
    year = fetch_with_published_time('2024')
    month = fetch_with_published_time('2024-02')
    day = fetch_with_published_time('2024-02-03')

    expect(year).to include(
      original_created_from: Time.zone.local(2024, 1, 1).iso8601,
      original_created_before: Time.zone.local(2025, 1, 1).iso8601
    )
    expect(month).to include(
      original_created_from: Time.zone.local(2024, 2, 1).iso8601,
      original_created_before: Time.zone.local(2024, 3, 1).iso8601
    )
    expect(day).to include(
      original_created_from: Time.zone.local(2024, 2, 3).iso8601,
      original_created_before: Time.zone.local(2024, 2, 4).iso8601
    )
  end

  it 'rounds second and fractional-second timestamps down to one-minute ranges' do
    result = fetch_with_published_time('2024-02-03T12:34:56.123+02:30')

    expect(Time.iso8601(result.fetch(:original_created_from)))
        .to eq(Time.iso8601('2024-02-03T12:34:00+02:30'))
    expect(
      Time.iso8601(result.fetch(:original_created_before)) -
      Time.iso8601(result.fetch(:original_created_from))
    ).to eq(60)
    expect(result.fetch(:original_created_from)).not_to include('.')
  end

  it 'treats hour-precision timestamps as one-hour ranges with and without offsets' do
    offset_result = fetch_with_published_time('2024-02-03T12+02:30')
    local_result = fetch_with_published_time('2024-02-03T12')

    expect(Time.iso8601(offset_result.fetch(:original_created_from)))
        .to eq(Time.iso8601('2024-02-03T12:00:00+02:30'))
    expect(
      Time.iso8601(offset_result.fetch(:original_created_before)) -
      Time.iso8601(offset_result.fetch(:original_created_from))
    ).to eq(3600)
    expect(local_result.fetch(:original_created_from)).to eq('2024-02-03T12:00:00Z')
    expect(local_result.fetch(:original_created_before)).to eq('2024-02-03T13:00:00Z')
  end

  it 'adds platform tags for known video URLs' do
    result = fetch_with_published_time('2024', url: 'https://youtu.be/abc123')

    expect(result.fetch(:tags)).to eq('動画 YouTube')
  end

  it 'returns nil dates for an invalid timestamp' do
    result = fetch_with_published_time('not-a-time')

    expect(result.fetch(:original_created_from)).to be_nil
    expect(result.fetch(:original_created_before)).to be_nil
  end

  it 'returns nil dates for an invalid timestamp second value' do
    result = fetch_with_published_time('2024-02-03T12:34:99Z')

    expect(result.fetch(:original_created_from)).to be_nil
    expect(result.fetch(:original_created_before)).to be_nil
  end

  it 'returns nil dates for invalid calendar dates and invalid hour values' do
    invalid_day = fetch_with_published_time('2024-02-31T12:00Z')
    invalid_leap = fetch_with_published_time('2023-02-29T12:00Z')
    invalid_hour = fetch_with_published_time('2024-02-29T24:00Z')

    [invalid_day, invalid_leap, invalid_hour].each do |result|
      expect(result.fetch(:original_created_from)).to be_nil
      expect(result.fetch(:original_created_before)).to be_nil
    end
  end

  it 'accepts a valid leap-day timestamp at minute precision' do
    result = fetch_with_published_time('2024-02-29T12:34Z')

    expect(result.fetch(:original_created_from)).to eq('2024-02-29T12:34:00Z')
    expect(result.fetch(:original_created_before)).to eq('2024-02-29T12:35:00Z')
  end

  it 'serialises metadata duration as the same seconds string contract used by forms' do
    html = <<~HTML
      <html><head>
        <meta property="article:published_time" content="2024-02-03T12:34:56.123+02:30">
        <meta property="og:video:duration" content="2.5">
      </head></html>
    HTML
    uri = URI.parse('https://example.com/video')
    allow(Preview::UrlSafety).to receive(:validate)
        .with('https://example.com/video')
        .and_return([uri, ['8.8.8.8']])
    allow(Preview::HttpFetcher).to receive(:fetch).and_return(Response.new(html))
    allow(Preview::HtmlMetadataExtractor).to receive(:extract)
        .and_return(title: 'title', image_url: nil)

    result = described_class.fetch('https://example.com/video')

    expect(result.fetch(:duration)).to eq('2.5')
  end
end
