module Recordings
  class UploadFromSession
    class Error < StandardError; end

    def initialize(session)
      @session = session
    end

    def call
      recording = Recording.find_by(stream_session: @session)
      return unless recording&.status == "processing"

      source_files = local_source_files
      if source_files.empty?
        fail_recording!(recording, "Nessun file di registrazione trovato")
        cleanup_mediamtx_path
        return recording
      end

      merged_path = merge_source_files(source_files)
      duration_secs = probe_duration(merged_path)
      storage_key = object_key(recording)
      storage = Recordings::Storage.new
      byte_size = storage.upload(local_path: merged_path, key: storage_key)

      Recordings::GenerateThumbnail.new(recording, video_path: merged_path).call

      recording.update!(
        status: "ready",
        storage_key: storage_key,
        storage_bucket: MatchLiveTv.replay_storage_bucket,
        storage_backend: storage.backend_name,
        byte_size: byte_size,
        duration_secs: duration_secs,
        error_message: nil
      )

      cleanup_local_sources(source_files, merged_path)
      cleanup_mediamtx_path
      Recordings::PostProcessJob.perform_async(recording.id)
      recording
    rescue Error, Recordings::Storage::Error => e
      recording = Recording.find_by(stream_session: @session)
      fail_recording!(recording, e.message) if recording
      raise
    ensure
      FileUtils.rm_f(@merged_temp_path) if @merged_temp_path && File.exist?(@merged_temp_path)
    end

    private

    def local_source_files
      base = File.join(MatchLiveTv.recordings_local_path, @session.mediamtx_path_name)
      return [] unless Dir.exist?(base)

      Dir.glob(File.join(base, "**", "*"))
         .select { |path| File.file?(path) && path.match?(/\.(mp4|fmp4|m4s|ts)$/i) }
         .sort_by { |path| File.basename(path) }
    end

    def merge_source_files(source_files)
      return source_files.first if source_files.one?

      @merged_temp_path = File.join(Dir.tmpdir, "replay-#{@session.id}-#{SecureRandom.hex(4)}.mp4")
      list_path = "#{@merged_temp_path}.txt"
      File.write(list_path, source_files.map { |f| "file '#{f.gsub("'", "'\\''")}'" }.join("\n"))
      success = system("ffmpeg", "-y", "-f", "concat", "-safe", "0", "-i", list_path, "-c", "copy", @merged_temp_path,
                       out: File::NULL, err: File::NULL)
      FileUtils.rm_f(list_path)
      raise Error, "Impossibile unire i segmenti video" unless success && File.exist?(@merged_temp_path)

      @merged_temp_path
    end

    def probe_duration(path)
      output = `ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 "#{path}" 2>/dev/null`
      output.to_f.round
    rescue StandardError
      @session.total_duration_secs.to_i
    end

    def object_key(recording)
      "teams/#{recording.team_id}/sessions/#{@session.id}/replay.mp4"
    end

    def cleanup_local_sources(source_files, merged_path)
      source_files.each { |path| FileUtils.rm_f(path) }
      base = File.join(MatchLiveTv.recordings_local_path, @session.mediamtx_path_name)
      FileUtils.rm_rf(base) if Dir.exist?(base)
      FileUtils.rm_f(merged_path) if merged_path != source_files.first
    end

    def cleanup_mediamtx_path
      Mediamtx::Client.new.delete_path(@session)
    rescue Mediamtx::Client::Error => e
      Rails.logger.warn("[Recordings::UploadFromSession] delete_path: #{e.message}")
    end

    def fail_recording!(recording, message)
      recording.update!(status: "failed", error_message: message)
      cleanup_mediamtx_path
    end
  end
end
