fastlane_version "2.229.0"

default_platform(:mac)

require 'kramdown'
require 'time'

import "Sparkle"
import "Linter"

platform :mac do
  before_all do

    ENV["LC_ALL"] = "en_US.UTF-8"
    ENV["LANG"] = "en_US.UTF-8"
    ENV["PROJECT_ROOT"] = File.expand_path("..", __dir__)

    # derived variables
    ENV["APP_PATH"] = File.join(ENV["PROJECT_ROOT"], ENV['EXPORT_PATH'], "#{ENV['APP_NAME']}.app")
    ENV["APP_VERSION"] = get_version_number(xcodeproj: ENV["PROJECT_PATH"], target: ENV["TARGET"])
    ENV["ZIP_NAME"] = "#{ENV['APP_NAME']}-#{ENV["APP_VERSION"]}.zip"
    ENV["ZIP_PATH"] = File.join(ENV["PROJECT_ROOT"], ENV["EXPORT_PATH"], ENV['ZIP_NAME'])
    ENV["IS_PRERELEASE"] = ENV["APP_VERSION"].match?(/(alpha|beta|rc)/i) ? "true" : "false"

    UI.message("🏗️ Environment loaded: #{ENV['FASTLANE_ENVIRONMENT'] || 'default'}")
  end

  desc "Create release, build project and create the zip file"
  lane :release do
    release_precondition
    build_archive_app
    verify_codesign

    notarize_app if ENV["NOTARIZE_ENABLED"] == "true"
    zip_app
    github if ENV["GITHUB_ENABLED"] == "true"
    sparkle if ENV["SPARKLE_ENABLED"] == "true" && ENV["IS_PRERELEASE"] != "true"
    UI.success("🎉 Release completed")
  end

  private_lane :check_release_version do
    app_version = Gem::Version.new(ENV["APP_VERSION"])
    last_version = Gem::Version.new(last_git_tag.gsub(/^v/, ''))
    
    UI.user_error!("App version must be greater than the last tag. You must increase the version before creating a new release.") if app_version <= last_version
  end

  private_lane :check_current_branch do
    build_branch = ENV['BUILD_BRANCH']
    current_branch = git_branch
    UI.user_error!("Build must run from '#{build_branch}' branch but the current is #{current_branch}") unless current_branch == build_branch
  end

  private_lane :build_archive_app do
    UI.header("🧱 Build & Archive")
    clean_build_artifacts
    gym(
      project: ENV["PROJECT_PATH"],
      scheme: find_scheme,
      silent: true,
      archive_path: ENV["ARCHIVE_PATH"],
      export_method: "developer-id",
      export_options: {
        signingStyle: "manual",
        signingCertificate: "Developer ID Application",
        teamID: ENV["TEAM_ID"]
      },
      output_directory: ENV["EXPORT_PATH"]
    )
  end

  lane :tests do
    run_tests(scheme: ENV["SCHEME"])
  end  

  private_lane :release_precondition do
    check_current_branch unless ENV["IGNORE_BUILD_BRANCH"] == "true"
    check_release_version if ENV["CHECK_VERSION"] == "true"
    lint_app
    format_app(
      check: true
    )

    note_file_name = File.join(ENV["BUILD_PATH"], ENV["RELEASE_NOTE_PATH"], ENV["APP_VERSION"])
    notes_full_path = File.join(ENV["PROJECT_ROOT"], "#{note_file_name}.md")
    UI.user_error!("Unable to find #{note_file_name}") unless File.exist?(notes_full_path)
  end

  private_lane :verify_codesign do
    UI.header("🔏 Verify sign")
    result = sh("codesign --verify --deep --strict --verbose=2 '#{ENV["APP_PATH"]}'", log: true)
    if result.include?('satisfies its Designated Requirement')
      UI.success("✅ Sign is valid")
    else
      UI.user_error!("Cosign output does not contain 'satisfies its Designated Requirement'")
    end
  end

  private_lane :zip_app do
    UI.header("📦 Build ZIP")
    sh("ditto -c -k --sequesterRsrc --keepParent #{ENV["APP_PATH"]} #{ENV["ZIP_PATH"]}")
    UI.message("✅ ZIP created: #{ENV["ZIP_PATH"]}")
  end

  private_lane :notarize_app do
    UI.header("🧾 Notarization")

    key_filepath = File.expand_path(ENV['NOTARIZE_API_KEY_PATH'])

    api_key = app_store_connect_api_key(
      key_id: ENV['NOTARIZE_API_KEY_ID'],
      issuer_id: ENV['NOTARIZE_API_ISSUER_ID'],
      key_filepath: key_filepath
    )
    
    notarize(
      package: ENV["APP_PATH"],
      api_key: api_key
    )
    UI.success("✅ App notarized and stapled")
  end

  desc "Create and upload release to GitHub"
  lane :github do
    UI.header("🚀 GitHub distribution")
    note_file_name = File.join(ENV["BUILD_PATH"], ENV["RELEASE_NOTE_PATH"], ENV["APP_VERSION"])
    notes_full_path = File.join(ENV["PROJECT_ROOT"], "#{note_file_name}.md")
    UI.user_error!("Unable to find #{note_file_name}") unless File.exist?(notes_full_path)
    notes = File.read(notes_full_path)

    result = set_github_release(
      repository_name: ENV["GITHUB_REPO"],
      api_token: ENV["GITHUB_API_TOKEN"],
      tag_name: "v#{ENV["APP_VERSION"]}",
      name: "#{ENV['APP_NAME']} v#{ENV["APP_VERSION"]}",
      description: notes,
      is_prerelease: ENV["IS_PRERELEASE"] == "true",
      upload_assets: [ENV["ZIP_PATH"]]
    )

    # GitHub creates the remote tag, so ensures local tags are up to date with remote ones
    sh('git fetch --tags')

    UI.success("✅ GitHub release completed")
  end

  def find_scheme
    if ENV["SPARKLE_ENABLED"] == "true"
      scheme = ENV["SPARKLE_SCHEME"] 

      UI.user_error!("Scheme for sparkle not defined") if scheme.nil? || scheme.empty?
      return scheme
    end

    scheme = ENV["SCHEME"]
    UI.user_error!("Default Scheme not defined") if scheme.nil? || scheme.empty?

    return scheme
  end

end
