require 'json'
require 'yaml'
require 'fileutils'
require 'shellwords'

KEYSTORE_APK    = "/keys/apk-release/juanitobananas-release-key.jks"
KEYSTORE_BUNDLE = "/keys/apk-release/juanitobananas-upload-keystore-for-bundles.jks"

def gradle_file
  @gradle_file ||= sh("[ -f ../app/build.gradle.kts ] && echo ../app/build.gradle.kts || echo ../app/build.gradle", log: false).strip
end

def app_name
  # Derive a name that's stable regardless of where the repo is checked out.
  # The fastlane Docker mounts it at /workdir, so the directory name is useless.
  url = sh("git config --get remote.origin.url || true", log: false).strip
  unless url.empty?
    name = File.basename(url, ".git")   # strips path + trailing .git in one go
    return name unless name.empty?
  end
  # Fallback: last segment of the applicationId, e.g. ...waveup -> waveup
  base_package_name.split(".").last
end

def base_package_name
  sh(
    "awk '/^[[:space:]]*applicationId[[:space:]]*=/ {sub(/^[^=]*=[[:space:]]*\"/, \"\"); sub(/\".*/, \"\"); print; exit}' #{gradle_file}",
    log: false
  ).strip
end

def package_name(flavor = "")
  return (flavor == "" || flavor == "standard" || flavor == "googleSucks") ? base_package_name : base_package_name + "." + flavor
end

def version_name
  return sh("grep \"versionName \" #{gradle_file} | awk '{print $NF}' | tr -d \"'\\\"\"").strip
end

def version_code
  return sh("grep \"versionCode \" #{gradle_file} | awk '{print $NF}' | tr -d \"'\\\"\"").strip
end

def last_tag
  return sh("git describe --abbrev=0").strip
end

def keystore_password_valid?(keystore, pw)
  return false if pw.nil? || pw.empty?
  # keytool exits non-zero if the password is wrong; -list is read-only and cheap
  system("keytool -list -keystore #{keystore.shellescape} -storepass #{pw.shellescape} > /dev/null 2>&1")
end

before_all do |lane, options|
  if !lane.to_s.start_with?('update_metadata')
    if options[:check_version] != false
      puts("Checking that latest commit is tagged like I like it. If you want to disable this, run with 'fastlane [lane] check_version:no'")
      check_version # This exits if something goes wrong
    end
    keystore = lane.to_s.include?("bundle") ? KEYSTORE_BUNDLE : KEYSTORE_APK
    prompt_password_once(keystore: keystore)
    gradle(task: "clean")
    # gradle(task: "spotlessCheck")
  end
end

def check_version
  ensure_git_status_clean

  changelog = "metadata/android/en-US/changelogs/#{version_code}.txt"
  if !File.file?(changelog)
    UI.user_error!("#{changelog} file doesn't exist")
  end

  last_commit_hash = last_git_commit[:commit_hash]
  last_tag_hash = sh("git rev-parse #{last_tag}^{}", log:false).strip

  if last_commit_hash != last_tag_hash
    UI.user_error!("Last commit not tagged with version (last git tag: #{last_tag}). Aborting.")
  end

  if "v#{version_name}" != last_tag
    UI.user_error!("Tag (#{last_tag}) and versionName in build-gradle (#{version_name}) differ. Aborting.")
  end

  if !last_tag.match(/v[0-9]\.[0-9](\.[0-9])?/)
    UI.user_error!("Tag doesn't match \"vX.Y.Z\" format")
  end
end

password = ""
private_lane :prompt_password_once do |values|
  # Pass the keystore that's actually about to be used so we validate against the right one.
  keystore = values[:keystore] || KEYSTORE_APK

  loop do
    if password.empty?
      password = prompt(
        text: "Please enter keystore password:",
        secure_text: true
      )
    end

    if keystore_password_valid?(keystore, password)
      break
    else
      UI.message("❌ Wrong password for #{File.basename(keystore)}, try again.")
      password = ""
    end
  end
end

private_lane :build_flavor do |values|
  flavor = values[:flavor]
  prompt_password_once(keystore: KEYSTORE_APK)
  gradle(
    task: "assemble",
    build_type: "Release",
    flavor: flavor,
    print_command: false,
    properties: {
      "android.injected.signing.store.file" => KEYSTORE_APK,
      "android.injected.signing.store.password" => password,
      "android.injected.signing.key.alias" => "app",
      "android.injected.signing.key.password" => password,
    }
  )

  # Rename the freshly built APK to something nicer than app-release.apk.
  # We move (not copy) and update lane_context so every downstream consumer
  # (GitLab upload, upload_to_play_store) picks up the new path automatically.
  apk = lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]
  if apk && !apk.empty? && File.file?(apk)
    parts = [app_name, (flavor.to_s.empty? ? nil : flavor), "release", version_name].compact
    new_path = File.join(File.dirname(apk), parts.join("-") + ".apk")
    FileUtils.mv(apk, new_path) unless File.expand_path(apk) == File.expand_path(new_path)
    lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH] = new_path
    puts("📦 Renamed APK to #{File.basename(new_path)}")
  end
end

private_lane :bundle_flavor do |values|
  flavor = values[:flavor]
  prompt_password_once(keystore: KEYSTORE_BUNDLE)
  gradle(
    task: "bundle",
    build_type: "Release",
    flavor: flavor,
    print_command: false,
    properties: {
      "android.injected.signing.store.file" => KEYSTORE_BUNDLE,
      "android.injected.signing.store.password" => password,
      "android.injected.signing.key.alias" => "upload",
      "android.injected.signing.key.password" => password,
    }
  )
end

private_lane :deploy_flavor do |values|
  flavor = values[:flavor]
  build_flavor(flavor: flavor)
  upload_to_play_store(
    track: "production",
    package_name: package_name(flavor),
    metadata_path: "fastlane/#{flavor}/metadata/android",
    skip_upload_images: true,
    skip_upload_screenshots: true
  )
  gradle(task: "clean")
end

private_lane :deploy_bundle_flavor do |values|
  flavor = values[:flavor]
  bundle_flavor(flavor: flavor)
  upload_to_play_store(
    track: "production",
    package_name: package_name(flavor),
    metadata_path: "fastlane/#{flavor}/metadata/android",
    skip_upload_images: true,
    skip_upload_screenshots: true
  )
  gradle(task: "clean")
end

private_lane :update_metadata_flavor do |values|
  flavor = values[:flavor]
  upload_to_play_store(
    track: "production",
    package_name: package_name(flavor),
    metadata_path: "fastlane/" + flavor + "/metadata/android",
    skip_upload_apk: true
  )
end

private_lane :fetch_metadata_flavor do |values|
  flavor = values[:flavor]
  package_name = flavor == "standard" ? base_package_name : base_package_name + "." + flavor
  sh("fastlane supply init --metadata_path ./#{flavor}/metadata/android --package_name #{package_name}")
end

private_lane :build_standard do
  standard_grep = sh("grep \"standard\" #{gradle_file} || true").strip
  if (standard_grep == "")
    puts("Didn't find standard flavor. Not using flavor at all")
    build_flavor
  else
    puts("Building 'standard' flavor")
    build_flavor(flavor: "standard")
  end
end

private_lane :gitlab_release_for_project_id do |values|# only release standard release for now
  gitlab_project_id = values[:project_id]

  if values[:check_version] != false
    puts("Checking that latest commit is tagged like I like it. If you want to disable this, run with 'fastlane gitlab_release check_version:no'")
    check_version # This exits if something goes wrong
  end

  puts("Releasing tag: #{last_tag}")

  build_standard

  gitlab_config = YAML::load_file('/keys/gitlab-api-token')
  gitlab_auth_token = gitlab_config["gitlab_auth_token"]

  upload_result_json = sh("curl \
      --silent \
      --header 'PRIVATE-TOKEN: #{gitlab_auth_token}' \
      --form 'file=@#{lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH]}' \
      --request POST https://gitlab.com/api/v4/projects/#{gitlab_project_id}/uploads", log:false)

  upload_result = JSON.parse(upload_result_json)
  markdown_upload_link = upload_result["markdown"]
  uploaded_url = upload_result["url"]

  tag_name = last_tag
  release_name = "Version #{version_name} (#{version_code})"
  description = "Download #{markdown_upload_link}\n\nChangelog [#{version_code}.txt](fastlane/metadata/android/en-US/changelogs/#{version_code}.txt)"
  data = {
      name: release_name,
      tag_name: tag_name,
      description: description
  }.to_json

  release_result_json = sh("curl \
          --silent \
          --header 'Content-Type: application/json' \
          --header 'PRIVATE-TOKEN: #{gitlab_auth_token}' \
          --data '#{data}' \
          --request POST https://gitlab.com/api/v4/projects/#{gitlab_project_id}/releases", log:false)

  release_result = JSON.parse(release_result_json)
  if !release_result.has_key? 'tag_name'
    UI.user_error!("Failed to create release: #{release_result}")
  end

  asset_name = File.basename(lane_context[SharedValues::GRADLE_APK_OUTPUT_PATH])
  asset_link_data = {
    name: asset_name,
    url: "https://gitlab.com/-/project/#{gitlab_project_id}#{uploaded_url}",
    filepath: "/#{asset_name}",
    link_type: "package"
  }.to_json

  asset_link_result_json = sh("curl \
          --silent \
          --header 'Content-Type: application/json' \
          --header 'PRIVATE-TOKEN: #{gitlab_auth_token}' \
          --data '#{asset_link_data}' \
          --request POST https://gitlab.com/api/v4/projects/#{gitlab_project_id}/releases/#{tag_name}/assets/links", log:false)

  asset_link_result = JSON.parse(asset_link_result_json)
  if !asset_link_result.has_key? 'direct_asset_url'
    UI.user_error!("Failed to create predictable release asset link: #{asset_link_result}")
  end

  puts("✅ Stable APK link: #{asset_link_result['direct_asset_url']}")
end
