class RepositoriesController < ApplicationController
  include RepositoriesHelper
  include ApplicationHelper
  include OperateProjectAbilityAble
  include Repository::LanguagesPercentagable

  before_action :require_login, only: %i[edit update create_file update_file delete_file sync_mirror]
  before_action :require_profile_completed, only: [:create_file]
  before_action :load_repository
  before_action :require_operate_above, only: %i[create_file update_file replace_file delete_file]
  before_action :authorizate!, except: [:sync_mirror, :tags, :commit, :archive]
  before_action :authorizate_user_can_edit_repo!, only: %i[sync_mirror]
  before_action :get_ref, only: %i[entries sub_entries top_counts files archive]
  before_action :get_latest_commit, only: %i[entries sub_entries top_counts]
  before_action :get_statistics, only: %i[top_counts]
  before_action :require_referer, only: [:raw]
  # before_action :request_limit, only: [:raw]
  before_action :request_raw_limit, only: [:raw]

  def files
    result = @project.educoder? ? nil : Gitea::Repository::Files::GetService.call(@owner, @project.identifier, @ref, params[:search], @owner.gitea_token)
    render json: result
  end

  # 新版项目详情
  def detail
    @user = current_user
    # UserAction.find_or_create_by!(action_id: @user.id, action_type: "ViewProject-#{@project.id}", user_id: @user.id) if @project.actionable
    @result = Repositories::DetailService.call(@owner, @repository, @user)
    cache_total_forks = $redis_cache.get("ProjectSpecialForks:#{@project.id}")
    if cache_total_forks.present?
      @project_forked_count = cache_total_forks.to_i
    else
      @project_forked_count = @project.forked_count.to_i
    end
    @project_fork_id = @project.try(:forked_from_project_id)
    if @project_fork_id.present?
      @fork_project = Project.find_by(id: @project_fork_id)
      @fork_project_user = @fork_project.owner
    end
    # 修正默认分支
    if @result[:repo].present? && @result[:repo]['default_branch'].present? && @result[:repo]['default_branch'] != @project.default_branch
      @project.update_column('default_branch', @result[:repo]['default_branch'])
    end
  rescue Exception => e
    uid_logger_error(e.message)
    tip_exception(e.message)
  end

  def show
    @user = current_user
    @repo = @project.repository
    @result = @project.forge? ? Gitea::Repository::GetService.new(@owner, @project.identifier).call : nil
    @project_fork_id = @project.try(:forked_from_project_id)
    if @project_fork_id.present?
      @fork_project = Project.find_by(id: @project_fork_id)
      @fork_project_user = @fork_project.owner
    end
  rescue Exception => e
    uid_logger_error(e.message)
    tip_exception(e.message)
  end

  def entries
    @week_project_visit_record, @month_project_visit_record = TimeableVisitRecord.build(@project.id)
    if  @week_project_visit_record.visits < 300 &&  @month_project_visit_record.visits < 1000
      @week_project_visit_record.increment!(:visits)
      @month_project_visit_record.increment!(:visits)
      @project.increment!(:visits)
      CacheAsyncSetJob.perform_later("project_common_service", {visits: 1}, @project.id)
    end
    if @project.educoder?
      @entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder.repo_name)
    else
      @entries = Gitea::Repository::Entries::ListService.new(@owner, @project.identifier, ref: @ref).call
      return render_not_found if @entries.is_a?(Array) && @entries.blank?  && params[:ref].to_s != @project&.default_branch
      @entries = @entries.present? ? @entries.sort_by{ |hash| hash['type'] } : []
      @path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
    end
  end

  def top_counts
    @result = @project.educoder? ? nil : Gitea::Repository::GetService.new(@project.owner, @project.identifier).call
  end

  def sub_entries
    file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
    tip_exception('不可访问') if params[:filepath].to_s.include?("chinese_dictionary.txt")
    if @project.educoder?
      if params[:type] === 'file'
        @sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri)
        logger.info "######### sub_entries: #{@sub_entries}"
        return render_error('该文件暂未开放，敬请期待.') if @sub_entries['status'].to_i === -1

        tmp_entries = {
            "content" =>  @sub_entries['data']['content'],
            "type"    => "blob"
          }
        @sub_entries = {
          "trees"=>tmp_entries,
          "commits" => [{}]
        }
      else
        begin
          @sub_entries = Educoder::Repository::Entries::ListService.call(@project&.project_educoder&.repo_name, {path: file_path_uri})
          if @sub_entries.blank? || @sub_entries['status'].to_i === -1
            @sub_entries = Educoder::Repository::Entries::GetService.call(@project&.project_educoder&.repo_name, file_path_uri)
            return render_error('该文件暂未开放，敬请期待.') if @sub_entries['status'].to_i === -1
            tmp_entries = {
              "content" =>  @sub_entries['data']['content'],
              "type"    => "blob"
            }
            @sub_entries = {
              "trees"=>tmp_entries,
              "commits" => [{}]
            }
          end
        rescue 
          return render_error('该文件暂未开放，敬请期待.')
        end
      end
    else
      @path = GiteaService.gitea_config[:domain]+"/#{@project.owner.login}/#{@project.identifier}/raw/branch/#{@ref}/"
      interactor = Repositories::EntriesInteractor.call(@owner, @project.identifier, file_path_uri, ref: @ref)
      if interactor.success?
        result = interactor.result
        @sub_entries = result.is_a?(Array) ? result.sort_by{ |hash| hash['type'] } : result
      else
        status = interactor.error == "你访问的文件不存在"? -2 : -1
        render_error(interactor.error,status)
      end
    end
  end

  def commits
    if @project.educoder?
      @commits = Educoder::Repository::Commits::ListService.call(@project&.project_educoder&.repo_name)
    else
      if params[:filepath].present?
        file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
        @hash_commit = Gitea::Repository::Commits::FileListService.new(@owner.login, @project.identifier, file_path_uri,
                                                                       sha: params[:sha], page: params[:page], limit: params[:limit], token: @owner&.gitea_token).call
      else
        @hash_commit = Gitea::Repository::Commits::ListService.new(@owner.login, @project.identifier,
                                                                   sha: params[:sha], page: params[:page], limit: params[:limit], token: @owner&.gitea_token).call
      end
    end
  end

  def commits_slice 
    @hash_commit = Gitea::Repository::Commits::ListSliceService.call(@owner.login, @project.identifier,
      sha: params[:sha], page: params[:page], limit: params[:limit], token: current_user&.gitea_token)
  end 

  def commit
    @sha         = params[:sha]
    if @project.educoder?
      return render_error('暂未开放，敬请期待.')
    else
      @commit      = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, @owner&.gitea_token)
      # @commit_diff = Gitea::Repository::Commits::GetService.call(@owner.login, @repository.identifier, @sha, @owner&.gitea_token, {diff: true})
      render_error(@commit[:message], @commit[:status]) if @commit.has_key?(:status)#|| @commit_diff.has_key?(:status)
    end
  end

  def tags
    if params[:only_name].present? 
      result = Gitea::Repository::Tags::ListNameService.call(@owner, @project.identifier, params[:name])

      @tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
    else
      name_result = Gitea::Repository::Tags::ListNameService.call(@owner, @project.identifier, params[:name])

      @tag_names = result.is_a?(Hash) && result.key?(:status) ? [] : name_result

      result = Gitea::Repository::Tags::ListService.call(@owner&.gitea_token, @owner.login, @project.identifier, {page: params[:page], limit: params[:limit]})

      @tags = result.is_a?(Hash) && result.key?(:status) ? [] : result
    end
  end

  def contributors
    if params[:filepath].present? || @project.educoder?
      @contributors = []
    else
      result = Gitea::Repository::Contributors::GetService.call(@owner, @repository.identifier, {page: params[:page], limit: params[:limit]})
      @total_count = result[:total_count]
      @contributors = result.is_a?(Hash) ? result[:body] : []

      add_contributors_count = EduSetting.get("ProjectAddContributors-#{@project.id}")
      @total_count = @total_count + add_contributors_count.to_i
    end
  rescue
    @contributors = []
  end

  def edit
    return render_forbidden if !@project.manager?(current_user) && !current_user.admin?
  end

  def create_file
    interactor = Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params)
    if interactor.success?
      @file = interactor.result
      # create_new_pr(params)
      #如果是更新流水线文件
      if params[:pipeline_id]
        update_pipeline(params[:pipeline_id])
      end
    else
      render_error(interactor.error)
    end
  end

  def update_pipeline(pipeline_id)
    pipeline = Ci::Pipeline.find(pipeline_id)
    if pipeline
      pipeline.update!(sync: 1)
    end
  end

  def update_file
    interactor = Gitea::UpdateFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier))
    if interactor.success?
      @file = interactor.result
      # TODO: 是否创建pr
      # create_new_pr(params)
      render_result(1, "更新成功")
    else
      render_error(interactor.error)
    end
  end

  def replace_file
    #删除
    delete_interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, params[:delete_file].merge(identifier: @project.identifier))
    return render_error(delete_interactor.error) unless delete_interactor.success?
    #新建
    interactor = Gitea::CreateFileInteractor.call(current_user.gitea_token, @owner.login, content_params)
    if interactor.success?
      @file = interactor.result
      render_result(0, "替换成功")
    else
      render_error(interactor.error)
    end

  end

  def delete_file
    interactor = Gitea::DeleteFileInteractor.call(current_user.gitea_token, @owner.login, params.merge(identifier: @project.identifier))
    if interactor.success?
      @file = interactor.result
      render_result(1, "文件删除成功")
    else
      render_error(interactor.error)
    end
  end

  def repo_hook

  end

  def sync_mirror
    return render_error("正在镜像中..") if  @repository.mirror.waiting?

    @repository.sync_mirror!
    SyncMirroredRepositoryJob.perform_later(@repository.id, current_user.id)
    render_ok
  end

  def readme
    if params[:filepath].present?
      result = Gitea::Repository::Readme::DirService.call(@owner.login, @repository.identifier, params[:filepath], params[:ref], @owner&.gitea_token)
    else
      result = Gitea::Repository::Readme::GetService.call(@owner.login, @repository.identifier, params[:ref], @owner&.gitea_token)
    end
    @path = GiteaService.gitea_config[:domain]+"/#{@owner.login}/#{@repository.identifier}/raw/branch/#{params[:ref]}/"
    @readme = result[:status] === :success ? result[:body] : nil
    # replace_content 前置，防止被content改写
    @readme['replace_content'] = readme_decode64_content(@readme, @owner, @repository, params[:ref], @path)
    @readme['content'] = decode64_content(@readme, @owner, @repository, params[:ref], @path)
    render json: @readme.slice("type", "encoding", "size", "name", "path", "content", "sha", "replace_content")
  rescue 
    render json: nil
  end

  def languages
    if @project.educoder? 
      render json: {}
    else
      render json: languages_precentagable
    end
  end

  def archive
    domain  = GiteaService.gitea_config[:domain]
    api_url = GiteaService.gitea_config[:base_url]
    archive_url = "/repos/#{@owner.login}/#{@repository.identifier}/archive/#{Addressable::URI.escape(params[:archive])}"

    file_path = [domain, api_url, archive_url].join
    file_path = [file_path, "access_token=#{@owner&.gitea_token}"].join("?")

    return render_not_found if !request.format.zip? && !request.format.gzip?

    redirect_to file_path
  end
  
  def raw
    domain  = GiteaService.gitea_config[:domain]
    api_url = GiteaService.gitea_config[:base_url]
    tip_exception('不可访问') if params[:filepath].to_s.include?("chinese_dictionary.txt")

    url = "/repos/#{@owner.login}/#{@repository.identifier}/raw/#{CGI.escape(params[:filepath])}?ref=#{CGI.escape(params[:ref])}"
    file_path = [domain, api_url, url].join
    file_path = [file_path, "access_token=#{@owner&.gitea_token}"].join("&")

    redirect_to file_path
  end

  private

  def find_project
    @project = Project.find params[:id]
    render_not_found("未找到相关的仓库") unless @project
  end

  def find_project_with_includes
    @project = Project.includes(:repository, :owner, :watchers, :praise_treads).find params[:id]
  end

  def authorizate!
    return if current_user && current_user.admin?
    if @project.repository.hidden? && !@project.member?(current_user) && !@project.is_secret
      render_forbidden
    end
  end

  # TODO 获取最新commit信息
  def project_commits
    if params[:filepath].present?
      file_path_uri = URI.parse(URI.encode(params[:filepath].to_s.strip))
      Gitea::Repository::Commits::FileListService.new(@project.owner.login, @project.identifier, file_path_uri,
        sha: get_ref, page: 1, limit: 1, token: @project&.owner&.gitea_token).call
    else
      Gitea::Repository::Commits::ListService.new(@project.owner.login, @project.identifier,
        sha: get_ref, page: 1, limit: 1, token: @project&.owner&.gitea_token).call
    end
  end

  def get_statistics
    @branches_count = @project.educoder? ? 0 : Gitea::Repository::Branches::ListService.new(@project.owner, @project.identifier).call&.size
    @tags_count = @project.educoder? ? 0 : Gitea::Repository::Tags::ListService.new(@project&.owner&.gitea_token, @project.owner.login, @project.identifier).call&.size
  end

  def get_ref
    @ref = params[:ref] || @project&.default_branch
  end

  def get_latest_commit
    latest_commit = @project.educoder? ? nil : project_commits
    @latest_commit = latest_commit.present? ? latest_commit[:body][0] : nil
    cache_total_commits = $redis_cache.get("ProjectSpecialCommit:#{@project.id}")
    if cache_total_commits.present?
      @commits_count = cache_total_commits.to_i
    else
      @commits_count = latest_commit.present? ? latest_commit[:total_count] : 0
    end
  end

  def content_params
    {
      filepath: params[:filepath],
      branch: params[:branch],
      new_branch: params[:new_branch],
      content: params[:content],
      message: params[:message],
      committer: {
        email: current_user.mail,
        name: current_user.login
      },
      identifier: @project.identifier
    }
  end

  def hook_params(hook_type, params)
    # if hook_type == "push"
    #   # TODO hook返回的记录中，暂时没有文件代码数量的增减，暂时根据 commits数量来计算
    #   uploadPushInfo = {
    #     "sha": params["commits"].present? ? params["commits"].last : "",
    #     "branch": params["ref"].to_s.split("/").last,
    #     "modification_lines": params["commits"].length
    #     }
    # elsif hook_type == "pull_request" && params["action"].to_s == "closed"  #合并请求合并后才会有上链操作
    #   uploadPushInfo = {
    #     "branch": params["base"]["ref"].to_s.split("/").last,
    #     "sha": params["pull_request"]["merge_base"],
    #     "modification_lines": 1  #pull_request中没有commits数量
    #     }
    # else
    #     uploadPushInfo = {}
    # end

    # uploadPushInfo
  end
  
  def create_new_pr(params)
    if params[:new_branch].present? && params[:new_branch] != params[:branch]
      local_params = {
        title: params[:message],  #标题
        body:	params[:content],  #内容
        head: params[:new_branch],  #源分支
        base: params[:branch],  #目标分支
        milestone: 0  #里程碑,未与本地的里程碑关联

      }
      requests_params = local_params.merge({
                                             assignee: current_user.try(:login),
                                             assignees: [],
                                             labels: [],
                                             due_date: Time.now
                                           })

      issue_params = {
        author_id: current_user.id,
        project_id: @project.id,
        subject: params[:message],
        description: params[:content],
        assigned_to_id: nil,
        fixed_version_id: nil,
        issue_tags_value: nil,
        issue_classify: "pull_request",
        issue_type: "1",
        tracker_id: 2,
        status_id: 1,
        priority_id: params[:priority_id] || "2"
      }
      @pull_issue = Issue.new(issue_params)
      if @pull_issue.save!
        local_requests = PullRequest.new(local_params.merge(user_id: current_user.try(:id), project_id: @project.id, issue_id: @pull_issue.id))
        if local_requests.save
          gitea_request = Gitea::PullRequest::CreateService.new(current_user.try(:gitea_token), @owner.login, @project.try(:identifier), requests_params).call
          if gitea_request[:status] == :success && local_requests.update_attributes(gpid: gitea_request["body"]["number"])
            local_requests.project_trends.create(user_id: current_user.id, project_id: @project.id, action_type: "create")
          end
        end
      end
    end
  end

  def require_operate_above 
    return render_forbidden if !current_user.admin? && !@project.operator?(current_user)
  end

end
