require_relative "code_hosting/git_hub_service"
require_relative "config_data_sources/json_project_data_source"
require_relative "../shared/models/github_provider_credential"
require_relative "../shared/logging_module"

module FastlaneCI
  # Provides access to the fastlane.ci configuration, like which projects we're hosting
  class ConfigService
    include FastlaneCI::Logging

    attr_reader :project_service
    attr_reader :ci_user
    attr_reader :active_code_hosting_services # dictionary of active_code_hosting_service_key to CodeHosting

    def initialize(project_service: FastlaneCI::Services.project_service, ci_user: nil)
      @project_service = project_service
      @ci_user = ci_user
      @active_code_hosting_services = {}
    end

    # if the provider_credential is for user B, but the service was initialized using user A,
    # that means user A is doing things on behalf of user B
    def process_on_behalf?(provider_credential: nil)
      return provider_credential.ci_user != ci_user
    end

    def active_code_hosting_service_key(provider_credential: nil)
      return "#{provider_credential.provider_name}_#{ci_user.id}"
    end

    # Find the active code host for the provider_credential/user combination
    # or instantiate one if none are available
    def code_hosting_service(provider_credential: nil)
      code_hosting_service_key = active_code_hosting_service_key(provider_credential: provider_credential)
      code_hosting_service = active_code_hosting_services[code_hosting_service_key]
      return code_hosting_service unless code_hosting_service.nil?

      # TODO: not a big deal right now, but we should have a way of automatically generating the correct
      # CodeHostingService subclass based on the provider_credential type and maybe not have it right here.
      # A Java-style factory might be the right move here.
      case provider_credential.type
      when FastlaneCI::ProviderCredential::PROVIDER_CREDENTIAL_TYPES[:github]
        code_hosting_service = GitHubService.new(provider_credential: provider_credential)
        active_code_hosting_services[code_hosting_service_key] = code_hosting_service
      else
        raise "Unrecognized provider_credential #{provider_credential.type}"
      end

      return code_hosting_service
    end

    def octokit_projects(provider_credential: nil)
      # Get a list of all the repos `provider` has access to
      if provider_credential.ci_user
        logger.debug("Getting code host for #{provider_credential.ci_user.email}, #{provider_credential.type}")
      end
      current_code_hosting_service = code_hosting_service(provider_credential: provider_credential)

      if provider_credential.ci_user
        logger.debug(
          "Finding projects we have access to with #{provider_credential.ci_user.email}, #{provider_credential.type}"
        )
      end

      projects = project_service.projects.select do |project|
        current_code_hosting_service.access_to_repo?(repo_url: project.repo_config.git_url)
      end

      # return all projects that are the union of this current user's provider_credential, and the passed in
      # provider_credential
      return projects
    end

    def project(id: nil, provider_credential: nil)
      current_ci_user_projects = projects(provider_credential: provider_credential)
      current_project = current_ci_user_projects.detect { |project| project.id == id }
      return current_project
    end

    def projects(provider_credential: nil)
      if provider_credential.type == FastlaneCI::ProviderCredential::PROVIDER_CREDENTIAL_TYPES[:github]
        return octokit_projects(provider_credential: provider_credential)
      else
        raise "Unrecognized provider_credential #{provider_credential.type}"
      end
    end
  end
end
