require "rails_helper"

RSpec.describe VideosController, type: :controller do
  render_views

  let(:user) { create(:user) }
  let(:account) { user.primary_account }
  let(:project) { create(:project, account: user.primary_account) }
  let(:video) { create(:video, project: project) }

  before do
    sign_in(user)
  end

  describe "#index" do
    it "responds successfully" do
      get :index, params: { project_id: project }

      expect(response).to be_successful
    end

    it "has a search form" do
      get :index, params: { project_id: project }

      expect(response.body).to have_button("search-button")
    end

    it "filters the videos by a query param" do
      create(:video, project: project, name: "First Video")
      create(:video, project: project, name: "Second Video")

      get :index, params: { project_id: project, query: "Second"}, xhr: true

      expect(response.body).not_to have_content("First Video")
      expect(response.body).to have_content("Second Video")
    end

    it "paginates the videos" do
      create_list(:video, 20, project: project)

      get :index, params: { project_id: project }
      expect(response.body).to have_link("2", href: project_videos_path(project, page: 2))
    end


    it "renders content" do
      video = create(:video, project: project, name: "My Video", number: "001", created_at: 1.day.ago)

      get :index, params: { project_id: project }

      expect(response.body).to have_content("1 day ago")
      expect(response.body).to have_content("My Video")
      expect(response.body).to have_content("001")
      expect(response.body).to have_content("video_file.mp4")
      expect(response.body).to have_content("Generating...")
      expect(response.body).to have_link("Upload New Video", href: landing_project_videos_path(project))
      expect(response.body).to have_link("Edit", href: edit_video_path(video))
    end

    context "when there are no records" do
      it "renders an empty message" do
        get :index, params: { project_id: project }

        expect(response.body).to have_content("Videos will appear here")
      end
    end

    context "when there are many records" do
      it "paginates the table" do
        create_list(:video, 20, project: project)

        get :index, params: { project_id: project }

        expect(response.body).to have_link("2", href: project_videos_path(project, page: 2))
      end
    end
  end

  describe "#new" do
    it "responds successfully" do
      get :new, params: { project_id: project }

      expect(response).to be_successful
    end
  end

  describe "#landing" do
    it "responds successfully" do
      get :landing, params: { project_id: project }

      expect(response).to be_successful
    end
  end

  describe "#create" do
    it "responds with redirect" do
      post :create, params: { project_id: project, video: video_create_params }

      expect(response).to redirect_to(project_videos_path(project))
    end

    it "creates a new video" do
      expect {
        post :create, params: { project_id: project, video: video_create_params }
      }.to change(Video, "count").by(1)

      expect(Video.last.video_editing_system).to eq "avid"
    end

    it "begins analyzing the video" do
      expect {
        post :create, params: { project_id: project, video: video_create_params }
      }.to have_enqueued_job(AnalyzeVideoJob).with(Video.last).and have_enqueued_job(AnalyzeAudioJob).with(Video.last)
    end

    it "delivers an admin notification" do
      assert_enqueued_emails 1 do
        post :create, params: { project_id: project, video: video_create_params }
      end
    end

    it "sets the flash" do
      post :create, params: { project_id: project, video: video_create_params }

      expect(flash.notice).to eq t("videos.create.notice")
    end

    it "logs analytics" do
      expect {
        post :create, params: { project_id: project, video: video_create_params }
      }.to have_enqueued_job(TrackAnalyticsJob).with(user, account,  :track_video_upload, user_agent: "Rails Testing", user_ip: "0.0.0.0")
    end

    context "when the record is invalid" do
      it "responds successfully" do
        post :create, params: { project_id: project, video: video_create_params(valid: false) }

        expect(response).to be_successful
      end
    end
  end
  
  describe "#edit" do
    it "responds successfully" do
      get :edit, params: { id: video }

      expect(response).to be_successful
    end
  end

  describe "#update" do
    it "redirects to the DeliverME page" do
      patch :update, params: { id: video, video: video_update_params }

      expect(response).to redirect_to(project_videos_path(project))
    end

    it "updates the video attributes" do
      patch :update, params: { id: video, video: { name: "New Name" } }

      expect(video.reload.name).to eq "New Name"
    end

    it "sets the flash" do
      patch :update, params: { id: video, video: video_update_params }

      expect(flash.notice).to eq t("videos.update.notice")
    end

    context "when params are invalid" do
      it "re-renders the edit view" do
        allow_any_instance_of(Video).to receive(:update).and_return(false)

        patch :update, params: { id: video, video: video_update_params }

        expect(response).to be_successful
      end
    end

    context "with unpermitted file param" do
      it "does not update the record" do
        new_file = Rack::Test::UploadedFile.new file_fixture("video_file.mp4")

        expect {
          patch :update, params: { id: video, video: { name: "New Name", file: new_file } }
        }.to raise_error ActionController::UnpermittedParameters
      end
    end

    context "when edl_file changes" do
      let(:new_edl_file) do
        Rack::Test::UploadedFile.new file_fixture("sample-edl.edl"), "application/octet-stream"
      end

      it "delivers an admin notification" do
        assert_enqueued_email_with AdminMailer, :updated_video_edl_file, args: video do
          patch :update, params: { id: video, video: { edl_file: new_edl_file } }
        end
      end

      it "unpublishes the video" do
        video.publish_report!

        patch :update, params: { id: video, video: { edl_file: new_edl_file } }

        expect(video.reload).not_to be_report_published
      end
    end

    context "when graphics_only_edl_file changes" do
      let(:new_graphics_only_edl_file) do
        Rack::Test::UploadedFile.new file_fixture("sample-edl.edl"), "application/octet-stream"
      end

      it "delivers an admin notification" do
        assert_enqueued_email_with AdminMailer, :updated_video_graphics_only_edl_file, args: video do
          patch :update, params: { id: video, video: { graphics_only_edl_file: new_graphics_only_edl_file } }
        end
      end

      it "unpublishes the video" do
        video.publish_report!

        patch :update, params: { id: video, video: { graphics_only_edl_file: new_graphics_only_edl_file } }

        expect(video.reload).not_to be_report_published
      end
    end

    context "when audio_only_edl_file changes" do
      let(:new_audio_only_edl_file) do
        Rack::Test::UploadedFile.new file_fixture("sample-edl.edl"), "application/octet-stream"
      end

      it "delivers an admin notification" do
        assert_enqueued_email_with AdminMailer, :updated_video_audio_only_edl_file, args: video do
          patch :update, params: { id: video, video: { audio_only_edl_file: new_audio_only_edl_file } }
        end
      end

      it "unpublishes the video" do
        video.publish_report!

        patch :update, params: { id: video, video: { audio_only_edl_file: new_audio_only_edl_file } }

        expect(video.reload).not_to be_report_published
      end
    end

    context "when edl_file and graphics_only_edl_file and audio_only_edl_file does not change" do
      it "does not deliver an admin notification" do
        assert_no_enqueued_emails do
          patch :update, params: { id: video, video: { name: "New Name" } }
        end
      end
      
      it "does not unpublish the video" do
        video.publish_report!

        patch :update, params: { id: video, video: { name: "New Name" } }
        
        expect(video.reload).to be_report_published
      end
    end
  end

  private

  def video_create_params(valid: true)
    if valid
      attributes_for(:video, :with_graphics_only_edl_file, :with_audio_only_edl_file, name: "Test Video", video_editing_system: "avid")
    else
      attributes_for(:video, :with_graphics_only_edl_file, :with_audio_only_edl_file, name: "Test Video").except(:file)
    end
  end

  def video_update_params
    attributes_for(:video, :with_graphics_only_edl_file, :with_audio_only_edl_file, name: "Test Video").except(:file)
  end
end
