require 'fastlane_core'
require 'fastlane_core/helper'
require_relative 'git'

module Fastlane
  class Github
    ENV = {
      # General workflow information
      :workflow => 'GITHUB_WORKFLOW',                            # Name of the workflow
      :run_id => 'GITHUB_RUN_ID',                                # Unique number for each workflow run within the repository
      :run_number => 'GITHUB_RUN_NUMBER',                        # Unique number for each run of a particular workflow in a repository
      :run_attempt => 'GITHUB_RUN_ATTEMPT',                      # Unique number for each attempt of a particular workflow run
      :job => 'GITHUB_JOB',                                      # Job ID of the current job
      :action => 'GITHUB_ACTION',                                # Unique identifier of the action currently running
      :action_path => 'GITHUB_ACTION_PATH',                      # Path where an action is located
      :action_repository => 'GITHUB_ACTION_REPOSITORY',          # Owner and repository name of the action
      :actions => 'GITHUB_ACTIONS',                              # Always set to true when GitHub Actions is running
      :actor => 'GITHUB_ACTOR',                                  # Name of the person or app that initiated the workflow
      :actor_id => 'GITHUB_ACTOR_ID',                            # Account ID of the person or app that triggered the initial workflow run
      :triggering_actor => 'GITHUB_TRIGGERING_ACTOR',             # Username of the user that initiated the workflow run

      # Repository information
      :repository => 'GITHUB_REPOSITORY',                        # Owner and repository name (e.g., octocat/Hello-World)
      :repository_id => 'GITHUB_REPOSITORY_ID',                  # ID of the repository
      :repository_owner => 'GITHUB_REPOSITORY_OWNER',            # Repository owner name
      :repository_owner_id => 'GITHUB_REPOSITORY_OWNER_ID',      # Account ID of the repository owner

      # Git reference information
      :ref => 'GITHUB_REF',                                      # Fully-formed ref of the branch or tag that triggered the workflow run
      :ref_name => 'GITHUB_REF_NAME',                            # Short ref name of the branch or tag that triggered the workflow run
      :ref_protected => 'GITHUB_REF_PROTECTED',                  # true if branch protections are configured for the ref
      :ref_type => 'GITHUB_REF_TYPE',                            # Type of ref that triggered the workflow run (branch or tag)
      :sha => 'GITHUB_SHA',                                      # Commit SHA that triggered the workflow

      # Pull request information
      :head_ref => 'GITHUB_HEAD_REF',                             # Head ref or source branch of the pull request in a workflow run
      :base_ref => 'GITHUB_BASE_REF',                             # Base ref or target branch of the pull request in a workflow run

      # Event information
      :event_name => 'GITHUB_EVENT_NAME',                         # Name of the event that triggered the workflow
      :event_path => 'GITHUB_EVENT_PATH',                         # Path to the complete webhook event payload on the runner

      # Server and API information
      :server_url => 'GITHUB_SERVER_URL',                          # URL of the GitHub server (default: https://github.com)
      :api_url => 'GITHUB_API_URL',                                # API URL (default: https://api.github.com)
      :graphql_url => 'GITHUB_GRAPHQL_URL',                        # GraphQL API URL (default: https://api.github.com/graphql)

      # Workspace and paths
      :workspace => 'GITHUB_WORKSPACE',                            # Default working directory on the runner for steps
      :path => 'GITHUB_PATH',                                      # System PATH variable
      :env => 'GITHUB_ENV',                                        # Path to file that contains environment variables

      # Output and step summary
      :output => 'GITHUB_OUTPUT',                                  # Path to file that contains job outputs from steps
      :step_summary => 'GITHUB_STEP_SUMMARY',                      # Path to file that contains step summary

      # Runner information
      :runner_name => 'RUNNER_NAME',                               # Name of the runner executing the job
      :runner_os => 'RUNNER_OS',                                   # Operating system of the runner executing the job
      :runner_arch => 'RUNNER_ARCH',                               # Architecture of the runner executing the job
      :runner_temp => 'RUNNER_TEMP',                               # Path to a temporary directory on the runner
      :runner_tool_cache => 'RUNNER_TOOL_CACHE',                   # Path to the directory containing preinstalled tools
      :runner_debug => 'RUNNER_DEBUG',                             # Set to 1 if debug logging is enabled

    }.map { |key, envvar| [key, ENV[envvar]] }.to_h

    def self.env
      ENV
    end

    def self.org
      "suno-ai"
    end

    def self.repo(url: nil)
      urlOrDefault = url || `git remote get-url origin`.strip
      # Handle various GitHub URL formats
      match = urlOrDefault.match(/(?:https:\/\/github\.com\/|git@github\.com:)suno-ai\/([^\.]*)(?:\.git)?/)
      if match
        match[1]
      else
        raise "Invalid repo URL format: #{urlOrDefault}"
      end
    end

    def self.repo_url(repo: self.repo)
      "https://github.com/#{org}/#{repo}.git"
    end

    def self.diff_range(remote_name: "origin")
      github_event_name = Github.env[:event_name]
      github_base_ref = Github.env[:base_ref]
      github_head_ref = Github.env[:head_ref]

      diff_range = if github_event_name == 'pull_request' && github_base_ref && github_head_ref
        "#{remote_name}/#{github_base_ref}...#{remote_name}/#{github_head_ref}"
      elsif github_event_name == 'push'
        "#{Git.previous_sha} #{Git.current_sha}"
      else
        "#{Git.default_branch_ref(remote_name: remote_name)}...HEAD"
      end
    end

    def self.branch_sha(branch:, repo: self.repo)
      Actions.sh "gh api /repos/#{org}/#{repo}/git/refs/heads/#{branch} --jq .object.sha" do |status, result, command|
        unless status.success?
          UI.user_error!("Command #{command} (pid #{status.pid}) failed with status #{status.exitstatus}")
        end
        sha = result.strip
        sha.empty? ? nil : sha
      end
    end

    def self.lookup_pr_by_number(number:, repo: self.repo, fields: "number,title,body,headRefName,url,author")
      Actions.sh "gh --repo #{org}/#{repo} pr view #{number} --json #{fields} 2>/dev/null || echo \"\"" do |status, result, command|
        unless status.success?
          UI.user_error!("Command #{command} (pid #{status.pid}) failed with status #{status.exitstatus}")
        end
        pr = result.strip
        pr.empty? ? nil : JSON.parse(pr)
      rescue JSON::ParserError => e
        UI.user_error!("Failed to parse PR JSON: '#{pr}' - #{e.message}")
      end
    end

    def self.lookup_pr_by_head_ref(head_ref:, repo: self.repo, state: nil, fields: "number,title,body,headRefName,url,author")
      Actions.sh "gh --repo #{org}/#{repo} pr list #{state ? "--state #{state}" : ""} --head #{head_ref} --json #{fields} --jq .[0] 2>/dev/null" do |status, result, command|
        unless status.success?
          UI.user_error!("Command #{command} (pid #{status.pid}) failed with status #{status.exitstatus}")
        end
        pr = result.strip
        pr.empty? ? nil : JSON.parse(pr)
      rescue JSON::ParserError => e
        UI.user_error!("Failed to parse PR JSON: '#{pr}' - #{e.message}")
      end
    end

    def self.label_exists?(label:, repo: self.repo)
      Actions.sh "gh --repo #{org}/#{repo} label list --json name --jq .[] | grep -q \"#{label}\"" do |status, result, command|
        unless status.success?
          UI.user_error!("Command #{command} (pid #{status.pid}) failed with status #{status.exitstatus}")
        end
        result.strip.empty?
      end
    end

    def self.save_output(key:, value:)
      unless Github.env[:output]
        UI.important("GITHUB_OUTPUT is not set. Unable to save: #{key}=#{value}")
        return
      end
      File.open(Github.env[:output], "a") do |file|
        file.puts("#{key}=#{value}")
      end
    end

    def self.log_error(message)
      if Github.env[:step_summary]
        # Print error to the console with annotations
        puts "::error::#{message}"
      else
        UI.message(message.red)
      end
    end

    def self.save_summary(summary)
      unless Github.env[:step_summary]
        UI.important("GITHUB_STEP_SUMMARY is not set. Unable to save summary.")
        return
      end
      File.open(Github.env[:step_summary], "a") do |file|
        summary.each_line do |line|
          file.puts(line)
        end
      end
    end

    def self.post_comment(repo: self.repo, pr_number:, body:, edit_last: false)
      Tempfile.open("comment.md") do |file|
        file.write(body)
        file.flush

        # Create new comment or edit last one
        UI.message("Creating new comment or editing last one")
        comment_url = `gh --repo #{org}/#{repo} pr comment #{pr_number} -F #{file.path} #{edit_last ? " --create-if-none --edit-last" : ""} 2>/dev/null || echo ''`.strip
        if comment_url.empty?
          UI.user_error!("Failed to extract comment ID from output")
          nil
        end
        UI.success("Successfully created/updated comment (URL: #{comment_url})")
        comment_url
      end
      save_summary(body)
    end

    def self.post_status(repo: self.repo, sha:, context:, state:, description:, target_url: nil)
      unless ["pending", "success", "failure", "error"].include?(state)
        UI.user_error!("Invalid state: #{state}")
      end

      description = Shellwords.escape(description)
      target_url = Shellwords.escape(target_url)

      cmd = "gh api /repos/#{org}/#{repo}/statuses/#{sha} --method POST --field state=#{state} --field context=#{Shellwords.escape(context)}"
        cmd += " --field description=#{description.length >= 137 ? "#{description[0..136]}..." : description}" if description
      cmd += " --field target_url=#{target_url}" if target_url

      Actions.sh(cmd, log: true)
    end

    def self.workflow_id(repo: self.repo, workflow:)
      Actions.sh "gh api /repos/#{org}/#{repo}/actions/workflows --jq '.workflows[] | select(.path | endswith(\"/#{workflow}\")) | .id'" do |status, result, command|
        unless status.success?
          UI.user_error!("Command #{command} (pid #{status.pid}) failed with status #{status.exitstatus}")
        end
        workflow_id = result.strip
        workflow_id.empty? ? nil : workflow_id
      end
    end

    def self.workflow_last_run(repo: self.repo, workflow_name:, branch:, sha:, created_after:)
      Actions.sh "gh run list --repo #{org}/#{repo} --json databaseId,headBranch,headSha,status,createdAt,updatedAt --workflow #{workflow_name} --branch #{branch} --commit #{sha} --created '>=#{Time.at(created_after).utc.iso8601}' --limit 1" do |status, result, command|
        unless status.success?
          UI.user_error!("Command #{command} (pid #{status.pid}) failed with status #{status.exitstatus}")
        end
        runs = JSON.parse(result.strip)
        if runs.empty?
          return nil
        end
        run = runs.first
        run['duration'] = run['status'] == 'completed' ? (Time.parse(run['updatedAt']) - Time.parse(run['createdAt'])).to_i : 0
        run['url'] = "https://github.com/#{org}/#{repo}/actions/runs/#{run['databaseId']}"
        run
      rescue JSON::ParserError => e
        UI.user_error!("Failed to parse run JSON: '#{result}' - #{e.message}")
      end
    end

    def self.workflow_run(repo: self.repo, run_id:)
      Actions.sh "gh run view #{run_id} --repo #{org}/#{repo} --json databaseId,headBranch,headSha,status,createdAt,updatedAt 2>/dev/null || echo \"\"" do |status, result, command|
        unless status.success?
          UI.user_error!("Command #{command} (pid #{status.pid}) failed with status #{status.exitstatus}")
        end
        run_str = result.strip
        if run_str.empty?
          return nil
        end
        begin
          run = JSON.parse(run_str)
          run['duration'] = run['status'] == 'completed' ? (Time.parse(run['updatedAt']) - Time.parse(run['createdAt'])).to_i : 0
          run['url'] = "https://github.com/#{org}/#{repo}/actions/runs/#{run['databaseId']}"
          run
        rescue JSON::ParserError
          UI.user_error!("Failed to parse run JSON: '#{run_str}'")
        end
      end
    end

  end
end
