require 'rails_helper'

RSpec.describe BroadcastsController, type: :controller do
  render_views

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

  before do
    sign_in user
  end

  describe "#index" do
    before do
      allow(MuxLiveStream).to receive(:new).and_return(double(id: "id", key: "key", playback_id: "playback_id"))
    end

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

      expect(response).to be_successful
    end

    it "renders content" do
      create(:broadcast, project: project, name: "Another Broadcast")

      get :index, params: { project_id: project }

      expect(response.body).to have_content "Another Broadcast"
      expect(response.body).to have_content "Created"
      expect(response.body).to have_content "less than a minute ago"
      expect(response.body).to have_link "Create New Live Stream"
    end

    context "when there are no active broadcasts" do
      it "renders an empty message" do
        Broadcast.destroy_all

        get :index, params: { project_id: project }

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

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

        get :index, params: { project_id: project }

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

    context "for xhr request" do
      it "filters the broadcasts by a query param" do
        appearance_releases = [
          create(:broadcast, skip_create_callback: true, name: "Stream by Adam"),
          create(:broadcast, skip_create_callback: true, name: "Stream by Zoe"),
        ]

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

        expect(response.body).not_to have_content("Adam")
        expect(response.body).to have_content("Zoe")
      end
    end
  end

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

      expect(response).to be_successful
      expect(assigns(:broadcast)).to be_a_new(Broadcast)
      expect(response).to render_template(:new)
    end
  end

  describe "#create" do
    before do
      allow(MuxLiveStream).to receive(:new).and_return(double(id: "id", key: "key", playback_id: "playback_id"))
    end

    it "responds with a redirect" do
      post :create, params: { project_id: project.id, broadcast: broadcast_params }

      expect(response).to be_redirect
      expect(flash.notice).not_to be_nil
    end

    it "does create a new record" do
      expect {
        post :create, params: { project_id: project.id, broadcast: broadcast_params }
      }.to change(Broadcast, :count)
    end

    it "logs an event" do
      expect {
        post :create, params: { project_id: project.id, broadcast: broadcast_params }
      }.to have_enqueued_job(TrackAnalyticsJob).with(user,  account, :track_create_live_stream, user_agent: "Rails Testing", user_ip: "0.0.0.0")
    end

    context "with invalid data" do
      it "does not create a new record" do
        expect {
          post :create, params: { project_id: project.id, broadcast: { name: "" } }
        }.not_to change(Broadcast, :count)
      end
    end
  end

  describe "#show" do
    let(:broadcast) { create(:broadcast, project: project, name: "Another Broadcast") }

    before do
      allow(MuxLiveStream).to receive(:new).and_return(double(id: "id", key: "key", playback_id: "playback_id"))
    end

    it "responds successfully" do
      get :show, params: { project_id: project.id, id: broadcast.id }

      expect(response).to be_successful
      expect(assigns(:broadcast)).to eq(broadcast)
    end

    it "renders readonly share url" do
      get :show, params: { project_id: project.id, id: broadcast.id }
      
      expect(response.body).to have_button "Copy URL"
      expect(response.body).to have_xpath "//input[@readonly][@value='#{broadcast_url(broadcast.token)}']"
    end

    it "displays zoom meeting button" do
      get :show, params: { project_id: project.id, id: broadcast.id }

      expect(response.body).to have_link("Video Conference", href: project_broadcast_zoom_meeting_url(project, broadcast))
    end

    it "assigns required variables" do
      get :show, params: { project_id: project.id, id: broadcast.id }

      expect(assigns(:conference_url)).to eq project_broadcast_zoom_meeting_url(project, broadcast)
      expect(assigns(:broadcast)).to eq broadcast
    end

    context "when there are no multi-view broadcasts" do
      it "renders the view dropdown with just the current broadcast" do
        get :show, params: { project_id: project, id: broadcast }

        expect(response.body).to have_content "Switch View"
        expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Another Broadcast")
      end
    end

    context "when there are multi-view broadcasts" do
      it "renders the view dropdown with all the broadcasts" do
        other_broadcast = create(:broadcast, project: project, name: "Some Other Broadcast")

        get :show, params: { project_id: project, id: broadcast, multi_view_ids: [broadcast.id, other_broadcast.id] }

        expect(response.body).to have_content "Switch View"
        expect(response.body).to have_selector(".dropdown-menu .dropdown-item.active", text: "Another Broadcast")
        expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: "Some Other Broadcast")
      end
    end

    context "when there are no recordings for the current broadcast" do
      it "renders the view dropdown with a message" do
        get :show, params: { project_id: project, id: broadcast }

        expect(response.body).to have_content "Switch View"
        expect(response.body).to have_selector(".dropdown-menu .dropdown-item", text: "Recordings will appear here")
      end
    end

    context "when there are recordings available" do
      it "renders the view dropdown with the recordings" do
        recording = create(:broadcast_recording, broadcast: broadcast)

        get :show, params: { project_id: project, id: broadcast }

        expect(response.body).to have_content "Switch View"
        expect(response.body).to have_selector(".dropdown-menu a.dropdown-item", text: recording.download_file_name)
      end
    end
  end

  describe "#update" do
    let!(:broadcast) { create(:broadcast, :with_stream, skip_create_callback: true, project: project ) }

    it "uploads files to broadcast" do
      patch :update, params: { project_id: project.id, id: broadcast.id, broadcast: file_params }, xhr: true

      expect(broadcast.files.count).to eq(1)
      expect(broadcast.files.first.filename).to eq("contract.pdf")
    end

    it "sends an update to the broadcasts channel" do
      allow(BroadcastsChannel).to receive(:broadcast_file_upload_updates)

      patch :update, params: { project_id: project.id, id: broadcast.id, broadcast: file_params }, xhr: true

      expect(BroadcastsChannel).to have_received(:broadcast_file_upload_updates)
    end
  end

  describe "#destroy" do
    let!(:broadcast_2) { create(:broadcast, :with_stream, skip_create_callback: true, project: project, name: "Another Broadcast") }

    before do
      allow_any_instance_of(MuxLiveStream).to receive(:destroy_stream).with(broadcast_2.stream_uid)
    end

    it "responds with redirect" do
      delete :destroy, params: { project_id: project.id, id: broadcast_2.id }

      expect(response).to be_redirect
      expect(response).to redirect_to(project_broadcasts_path(project))
      expect(flash.alert).not_to be_nil
    end

    it "destroys the record" do
      expect {
        delete :destroy, params: { project_id: project.id, id: broadcast_2.id }
      }.to change(Broadcast, :count).by(-1)
    end
  end

  after do
    # Set the callback again or it will affect other test cases where the callback is required
    Broadcast.set_callback(:create, :after, :create_mux_live_stream)
  end

  private

  def broadcast_params
    attributes_for(:broadcast)
  end

  def file_params
    path = Rails.root.join("spec", "fixtures", "files", "contract.pdf")
    contract_file = Rack::Test::UploadedFile.new(path, "application/pdf")

    { files: [contract_file] }
  end
end
