class GithubReposController < ApplicationController
  before_action :authenticate_user!
  after_action :verify_authorized

  def index
    authorize GithubRepo
    known_repositories = current_user.github_repos.featured.distinct.to_a

    # NOTE: this will invoke autopaging, by issuing multiple calls to GitHub
    # to fetch all of the user's repositories. This could eventually become slow
    @repos = fetch_repositories_from_github(known_repositories)
  rescue Github::Errors::Unauthorized => e
    render json: { error: I18n.t("github_repos_controller.github_unauthorized", e_message: e.message), status: 401 },
           status: :unauthorized
  end

  def update_or_create
    authorize GithubRepo

    params[:github_repo] = JSON.parse(params[:github_repo])

    fetched_repo = fetch_repository_from_github(repo_params[:github_id_code])
    unless fetched_repo
      render json: { error: I18n.t("github_repos_controller.repo_not_found"), status: 404 },
             status: :not_found
      return
    end

    repo = GithubRepo.upsert(current_user, **fetched_repo_params(fetched_repo))

    current_user.touch(:github_repos_updated_at)

    if repo.valid?
      render json: { featured: repo.featured }
    else
      render json: { error: repo.errors.full_messages, status: 422 }, status: :unprocessable_entity
    end
  end

  private

  def fetch_repositories_from_github(known_repositories)
    client = Github::OauthClient.for_user(current_user)

    repos = client.repositories(visibility: :public).map do |repo|
      if (known_index = known_repositories.find_index { |known| known.github_id_code == repo.id })
        repo.featured = true
        known_repositories.delete_at(known_index)
      end
      repo
    end

    # Remove pinned repositories that were removed from GH or are now private,
    # since the user will not be able to remove them by themselves.
    known_repositories.each(&:destroy)

    repos.sort_by(&:name)
  end

  def fetch_repository_from_github(repository_id)
    client = Github::OauthClient.for_user(current_user)

    client.repository(repository_id)
  rescue Github::Errors::NotFound
    nil
  end

  def fetched_repo_params(fetched_repo)
    {
      github_id_code: fetched_repo.id,
      name: fetched_repo.name,
      description: fetched_repo.description,
      language: fetched_repo.language,
      fork: fetched_repo.fork,
      url: fetched_repo.html_url,
      bytes_size: fetched_repo.size,
      watchers_count: fetched_repo.watchers,
      stargazers_count: fetched_repo.stargazers_count,
      featured: repo_params[:featured],
      info_hash: fetched_repo.to_hash
    }
  end

  def repo_params
    permitted_attributes(GithubRepo)
  end
end
