require "shellwords"

module Fastlane
  module Actions
    module SharedValues
      TAPCRAFT_VIDEO_PATH = :TAPCRAFT_VIDEO_PATH
    end

    # Re-renders a saved TapCraft video with a new screen recording, by calling the `tapcraft` CLI.
    #
    #   tapcraft(project: "Launch video", video: "fastlane/screenshots/flow.mp4", output: "build/launch.mp4")
    #
    # Needs Node 18+ and the CLI (`npm install -g tapcraft`, or leave `cli` at its default to run it
    # through npx). The API key comes from TAPCRAFT_API_KEY; it is passed to the CLI through the
    # environment, never on the command line, so it does not show up in fastlane's logs.
    class TapcraftAction < Action
      def self.run(params)
        command = Shellwords.split(params[:cli])
        command += ["render", params[:project], "--video", File.expand_path(params[:video])]
        command += ["--out", File.expand_path(params[:output])] if params[:output]
        command += ["--resolution", params[:resolution].to_s] if params[:resolution]
        command += ["--trim-start", params[:trim_start].to_s] if params[:trim_start]
        command += ["--duration", params[:duration].to_s] if params[:duration]
        command += ["--max-wait", params[:max_wait].to_s] if params[:max_wait]

        env = {}
        env["TAPCRAFT_API_KEY"] = params[:api_key] if params[:api_key]
        env["TAPCRAFT_API_URL"] = params[:api_url] if params[:api_url]

        output = with_env(env) { Actions.sh(*command, log: true) }
        # The CLI prints progress to stderr and only the finished file to stdout, as its last line.
        path = output.to_s.lines.map(&:strip).reject(&:empty?).last
        UI.user_error!("tapcraft did not report an output file") unless path && File.exist?(path)

        UI.success("TapCraft video: #{path}")
        Actions.lane_context[SharedValues::TAPCRAFT_VIDEO_PATH] = path
        path
      end

      def self.with_env(env)
        saved = env.keys.map { |k| [k, ENV[k]] }.to_h
        env.each { |k, v| ENV[k] = v }
        yield
      ensure
        saved&.each { |k, v| ENV[k] = v }
      end

      def self.description
        "Re-render a saved TapCraft video with a new screen recording"
      end

      def self.details
        "Uploads the recording, renders it on TapCraft's servers with the saved project's frame, " \
          "background, camera and taps, and downloads the MP4. Needs a TapCraft Pro API key."
      end

      def self.available_options
        [
          FastlaneCore::ConfigItem.new(key: :project, description: "Project id or exact name, from `tapcraft projects`", type: String),
          FastlaneCore::ConfigItem.new(key: :video, description: "The new screen recording (MP4 or MOV, up to 3 minutes)", type: String,
                                       verify_block: proc { |v| UI.user_error!("No recording at #{v}") unless File.exist?(v) }),
          FastlaneCore::ConfigItem.new(key: :output, description: "Where to save the rendered MP4", type: String, optional: true),
          FastlaneCore::ConfigItem.new(key: :resolution, description: "1080, 2k or 4k (default: the project's own)", type: String, optional: true,
                                       verify_block: proc { |v| UI.user_error!("resolution must be 1080, 2k or 4k") unless %w[1080 2k 4k].include?(v.to_s) }),
          FastlaneCore::ConfigItem.new(key: :trim_start, description: "Milliseconds to skip at the start (default: the project's trim)", type: Integer, optional: true),
          FastlaneCore::ConfigItem.new(key: :duration, description: "Milliseconds of the recording to use (default: all of it)", type: Integer, optional: true),
          FastlaneCore::ConfigItem.new(key: :max_wait, description: "Minutes to wait for a free render server (default: 30)", type: Integer, optional: true),
          FastlaneCore::ConfigItem.new(key: :api_key, env_name: "TAPCRAFT_API_KEY", description: "TapCraft API key (tc_live_…)", type: String,
                                       optional: true, sensitive: true),
          FastlaneCore::ConfigItem.new(key: :api_url, env_name: "TAPCRAFT_API_URL", description: "API address (default: production)", type: String, optional: true),
          FastlaneCore::ConfigItem.new(key: :cli, env_name: "TAPCRAFT_CLI", description: "How to run the CLI", type: String, default_value: "npx --yes --package=https://tapcraft.app/cli/tapcraft-0.1.0.tgz tapcraft")
        ]
      end

      def self.output
        [["TAPCRAFT_VIDEO_PATH", "Path of the rendered MP4"]]
      end

      def self.return_value
        "Path of the rendered MP4"
      end

      def self.authors
        ["TapCraft"]
      end

      def self.is_supported?(platform)
        true
      end

      def self.category
        :screenshots
      end
    end
  end
end
