# frozen_string_literal: true

require 'rails_helper'

RSpec.describe Import, type: :model do
  let(:user) { create(:user) }
  subject(:import) { create(:import, user:) }

  describe 'associations' do
    it { is_expected.to have_many(:points).dependent(:destroy) }
    it 'belongs to a user' do
      expect(user).to be_present
      expect(import.user).to eq(user)
    end
  end

  describe 'validations' do
    it { is_expected.to validate_presence_of(:name) }

    it 'validates uniqueness of name scoped to user_id' do
      create(:import, name: 'test_name', user: user)

      duplicate_import = build(:import, name: 'test_name', user: user)
      expect(duplicate_import).not_to be_valid
      expect(duplicate_import.errors[:name]).to include('has already been taken')

      other_user = create(:user)
      different_user_import = build(:import, name: 'test_name', user: other_user)
      expect(different_user_import).to be_valid
    end

    describe 'file size validation' do
      context 'when user is a trial user' do
        let(:user) do
          user = create(:user)
          user.update!(status: :trial)
          user
        end

        it 'validates file size limit for large files' do
          import = build(:import, user: user)
          mock_file = double(attached?: true, blob: double(byte_size: 12.megabytes))
          allow(import).to receive(:file).and_return(mock_file)

          expect(import).not_to be_valid
          expect(import.errors[:file]).to include('is too large. Trial users can only upload files up to 10MB.')
        end

        it 'allows files under the size limit' do
          import = build(:import, user: user)
          mock_file = double(attached?: true, blob: double(byte_size: 5.megabytes))
          allow(import).to receive(:file).and_return(mock_file)

          expect(import).to be_valid
        end
      end

      context 'when user is a paid user' do
        let(:user) { create(:user, status: :active) }
        let(:import) { build(:import, user: user) }

        it 'does not validate file size limit' do
          allow(import).to receive(:file).and_return(double(attached?: true, blob: double(byte_size: 12.megabytes)))

          expect(import).to be_valid
        end
      end
    end

    describe 'import count validation' do
      context 'when user is a trial user' do
        let(:user) do
          user = create(:user)
          user.update!(status: :trial)
          user
        end

        it 'allows imports when under the limit' do
          3.times { |i| create(:import, user: user, name: "import_#{i}") }
          new_import = build(:import, user: user, name: 'new_import')

          expect(new_import).to be_valid
        end

        it 'prevents creating more than 5 imports' do
          5.times { |i| create(:import, user: user, name: "import_#{i}") }
          new_import = build(:import, user: user, name: 'import_6')

          expect(new_import).not_to be_valid
          expect(new_import.errors[:base]).to include('Trial users can only create up to 5 imports. Please subscribe to import more files.')
        end
      end

      context 'when user is an active user' do
        let(:user) { create(:user, status: :active) }

        it 'does not validate import count limit' do
          7.times { |i| create(:import, user: user, name: "import_#{i}") }
          new_import = build(:import, user: user, name: 'import_8')

          expect(new_import).to be_valid
        end
      end
    end
  end

  describe 'enums' do
    it do
      is_expected.to define_enum_for(:source).with_values(
        google_semantic_history: 0,
        owntracks: 1,
        google_records: 2,
        google_phone_takeout: 3,
        gpx: 4,
        immich_api: 5,
        geojson: 6,
        photoprism_api: 7,
        user_data_archive: 8,
        kml: 9
      )
    end
  end

  describe '#years_and_months_tracked' do
    let(:import) { create(:import) }
    let(:timestamp) { Time.zone.local(2024, 11, 1) }
    let!(:points) do
      (1..3).map do |i|
        create(:point, import:, timestamp: timestamp + i.minutes)
      end
    end

    it 'returns years and months tracked' do
      expect(import.years_and_months_tracked).to eq([[2024, 11]])
    end
  end

  describe '#migrate_to_new_storage' do
    let(:raw_data) { Rails.root.join('spec/fixtures/files/geojson/export.json') }
    let(:import) { create(:import, source: 'geojson', raw_data:) }

    it 'attaches the file to the import' do
      import.migrate_to_new_storage

      expect(import.file.attached?).to be_truthy
    end

    context 'when file is attached' do
      it 'is a importable file' do
        import.migrate_to_new_storage

        expect { import.process! }.to change(Point, :count).by(10)
      end
    end
  end

  describe '#recalculate_stats' do
    let(:import) { create(:import, user:) }
    let!(:point1) { create(:point, import:, user:, timestamp: Time.zone.local(2024, 11, 15).to_i) }
    let!(:point2) { create(:point, import:, user:, timestamp: Time.zone.local(2024, 12, 5).to_i) }

    it 'enqueues stats calculation jobs for each tracked month' do
      expect do
        import.send(:recalculate_stats)
      end.to have_enqueued_job(Stats::CalculatingJob)
        .with(user.id, 2024, 11)
        .and have_enqueued_job(Stats::CalculatingJob).with(user.id, 2024, 12)
    end

    context 'when import has no points' do
      let(:empty_import) { create(:import, user:) }

      it 'does not enqueue any jobs' do
        expect do
          empty_import.send(:recalculate_stats)
        end.not_to have_enqueued_job(Stats::CalculatingJob)
      end
    end
  end
end
