#!/usr/bin/env ruby
require 'csv'
require 'date'
require 'net/http'
require 'uri'
require 'nokogiri'
require 'digest'

# Load helper modules
require_relative 'helpers/lut_helper'
require_relative 'helpers/file_helper'
require_relative 'helpers/data_analysis_helper'
require_relative 'helpers/text_generation_helper'
require_relative 'helpers/shared_constants'
require_relative 'helpers/data_cleaner'
require_relative 'helpers/dose_processor'
require_relative 'helpers/output_formatter'
require_relative 'helpers/substance_categorizer'
require_relative 'helpers/graph_generator'

class SubstanceWrapped
  # Substances to exclude from wrapped reports
  EXCLUDED_SUBSTANCES = [
    "Ibuprofen",
    "Paracetamol", 
    "Estradiol",
    "Estradiol Enanthate",
    "Estradiol Hemihydrate", 
    "Estradiol Undecylate",
    "Estradiol Valerate",
    "Ivermectin",
    "Melatonin"
  ].freeze

  def initialize(csv_data, user, year = Date.today.year)
    @year = year
    @user = user
    @data = parse_csv(csv_data)
  end

  def parse_csv(csv_data)
    entries = []
    csv_data.each do |row|
      next if row.length < 5 || row[0].nil? || row[0].empty?
      next if row[1].nil? || row[2].nil? # Skip if user or substance is nil
      begin
        next unless row[1] == @user
        timestamp = DateTime.parse(row[0])
        next unless timestamp.year == @year

        normalized_substance = LutHelper.normalize_substance_with_lut(row[2])
        # Skip excluded substances
        next if EXCLUDED_SUBSTANCES.include?(normalized_substance)

        entries << {
          timestamp: timestamp,
          user: row[1],
          substance: normalized_substance,
          dose: row[3],
          route: LutHelper.normalize_route_with_lut(row[4])
        }
      rescue => e
        # Skip malformed entries
      end
    end
    entries
  end

  def monthly_summary
    DoseProcessor.process_monthly_doses(@data)
  end

  def time_of_day_analysis
    analysis = {}
    SharedConstants::TIME_PERIODS.each { |period, _| analysis[period] = {} }

    @data.each do |entry|
      hour = entry[:timestamp].hour
      period = SharedConstants::TIME_PERIODS.find { |_, range| range.include?(hour) }&.first
      next unless period

      analysis[period][entry[:substance]] ||= 0
      analysis[period][entry[:substance]] += 1
    end

    analysis
  end

  def top_substances(limit = SharedConstants::Analysis::TOP_SUBSTANCES_LIMIT)
    substance_counts.sort_by { |_, count| -count }.take(limit)
  end

  def route_analysis
    routes = Hash.new { |h, k| h[k] = Hash.new(0) }
    @data.each do |entry|
      routes[entry[:substance]][entry[:route]] += 1
    end
    routes
  end

  def day_of_week_analysis
    days = %w[Sunday Monday Tuesday Wednesday Thursday Friday Saturday]
    analysis = Hash.new { |h, k| h[k] = 0 }

    @data.each do |entry|
      day = days[entry[:timestamp].wday]
      analysis[day] += 1
    end

    analysis
  end

  def peak_month
    monthly = monthly_summary
    month_totals = {}
    monthly.each do |month, substances|
      month_totals[month] = substances.values.sum { |data| data[:count] }
    end
    month_totals.max_by { |_, total| total }
  end

  def one_time_substances
    substance_counts.select { |_, count| count == 1 }.keys.sort
  end

  def streak_analysis
    # Only consider entries up to yesterday to ignore future streaks
    yesterday = Date.today - 1
    past_data = @data.select { |entry| entry[:timestamp].to_date <= yesterday }
    
    return {} if past_data.empty?
    
    # Group by substance and date
    substance_dates = {}
    past_data.each do |entry|
      substance = entry[:substance]
      date = entry[:timestamp].to_date
      substance_dates[substance] ||= []
      substance_dates[substance] << date unless substance_dates[substance].include?(date)
    end
    
    streaks = {}
    substance_dates.each do |substance, dates|
      dates.sort!
      current_streak = 1
      max_streak = 1
      
      (1...dates.length).each do |i|
        if dates[i] == dates[i-1] + 1
          current_streak += 1
          max_streak = [max_streak, current_streak].max
        else
          current_streak = 1
        end
      end
      
      streaks[substance] = max_streak if max_streak > 1
    end
    
    streaks
  end

  def hardest_day
    DataAnalysisHelper.find_hardest_day(@data)
  end

  def doses_on_day(date)
    DataAnalysisHelper.get_doses_on_day(@data, date)
  end

  def longest_sober_streak
    DataAnalysisHelper.calculate_longest_sober_streak(@data, @year)
  end

  def generate_wrapped
    return early_exit_message if @data.empty?
    
    stats = gather_statistics
    sections = build_sections(stats)
    format_output(sections)
  end

  private
  
  def early_exit_message
    "**#{@user}'s Substance Wrapped #{@year}**\n\nNo data found for #{@year}."
  end

  def gather_statistics
    {
      monthly: monthly_summary,
      time_analysis: time_of_day_analysis,
      top_substances: top_substances,
      route_stats: route_analysis,
      peak_month_data: peak_month,
      day_stats: day_of_week_analysis,
      one_time_uses: one_time_substances,
      streaks: streak_analysis,
      hardest_day_data: hardest_day,
      longest_sober: longest_sober_streak
    }
  end

  def build_sections(stats)
    sections = []
    sections << build_header_section
    sections << build_substances_section(stats[:top_substances])
    sections << build_monthly_section(stats[:monthly], stats[:peak_month_data])
    sections << build_time_analysis_section(stats[:time_analysis])
    sections << build_day_analysis_section(stats[:day_stats])
    sections << build_route_analysis_section(stats[:route_stats], stats[:top_substances])
    sections << build_onetime_section(stats[:one_time_uses]) if stats[:one_time_uses].any?
    sections << build_streaks_section(stats[:streaks], stats[:longest_sober])
    sections << build_hardest_day_section(stats[:hardest_day_data])
    sections << build_summary_section
    sections.compact
  end

  def build_header_section
    user_possessive = @user ? "#{@user}'s" : "Your"
    [
      "**#{user_possessive}**",
      "**Substance Wrapped #{@year}**"
    ]
  end

  def build_substances_section(top_substances)
    section = []
    OutputFormatter.add_section(section, random_top_substances_title, get_top_substances_comment(top_substances)) do |out|
      top_substances.each do |substance, count|
        out << OutputFormatter.format_substance_entry(substance, count)
      end
    end
    section
  end

  def build_monthly_section(monthly, peak_month_data)
    peak_month_name, peak_count = peak_month_data || [nil, 0]
    section = []
    OutputFormatter.add_section(section, random_monthly_title, get_monthly_breakdown_comment(peak_month_name, peak_count, monthly)) do |out|
      monthly.keys.sort.each do |month|
        substances = monthly[month]
        out << ""
        out << OutputFormatter.format_month_header(month, month.split('-')[0])
        substances.sort_by { |_, data| -data[:count] }.take(SharedConstants::Analysis::TOP_ROUTES_LIMIT).each do |substance, data|
          out << OutputFormatter.format_substance_with_average(substance, data[:count], data[:avg_dose])
        end
      end
    end
    section
  end

  def build_time_analysis_section(time_analysis)
    section = []
    OutputFormatter.add_section(section, random_peak_times_title(time_analysis), get_peak_time_comment(time_analysis)) do |out|
      time_analysis.each do |period, substances|
        next if substances.empty?
        total = substances.values.sum
        out << OutputFormatter.format_time_period_entry(period, total)
      end
    end
    section
  end

  def build_day_analysis_section(day_stats)
    section = []
    OutputFormatter.add_section(section, random_peak_day_title) do |out|
      top_days = day_stats.sort_by { |_, count| -count }.take(SharedConstants::Analysis::TOP_DAYS_LIMIT)
      top_days.each do |day, count|
        out << OutputFormatter.format_day_entry(day, count)
      end
    end
    section
  end

  def build_route_analysis_section(route_stats, top_substances)
    section = []
    OutputFormatter.add_section(section, random_route_title) do |out|
      route_stats.sort_by { |sub, _| -top_substances.to_h[sub].to_i }.take(SharedConstants::Analysis::TOP_ROUTES_LIMIT).each do |substance, routes|
        top_route = routes.max_by { |_, count| count }
        total_uses = routes.values.sum
        out << OutputFormatter.format_route_entry(substance, top_route[0], top_route[1], total_uses)
      end
    end
    section
  end

  def build_onetime_section(one_time_uses)
    section = []
    OutputFormatter.add_section(section, random_onetime_title) do |out|
      one_time_uses.take(SharedConstants::Analysis::MAX_ONETIME_DISPLAY).each do |substance|
        out << OutputFormatter.format_tabbed_entry(substance, "", 1)
      end
    end
    section
  end

  def build_streaks_section(streaks, longest_sober)
    return nil unless streaks.any? || longest_sober > 0
    
    section = []
    section << ""
    section << OutputFormatter.format_section_header("Longest Streaks (consecutive days):")
    
    if streaks.any?
      top_substance, top_days = streaks.max_by { |_, days| days }
      section << OutputFormatter.format_comment(get_streak_girlie_comment(top_substance))
      section << ""
      
      streaks.sort_by { |_, days| -days }.take(SharedConstants::Analysis::MAX_STREAKS_DISPLAY).each do |substance, days|
        section << OutputFormatter.format_streak_entry(substance, days)
      end
    end
    
    if longest_sober > 0
      section << "" if streaks.any?
      section << OutputFormatter.format_streak_entry("Sober streak", longest_sober)
    end
    
    section
  end

  def build_hardest_day_section(hardest_day_data)
    return nil unless hardest_day_data
    
    hardest_date, hardest_count = hardest_day_data
    doses = doses_on_day(hardest_date)
    
    section = []
    section << ""
    section << OutputFormatter.format_section_header(random_hardest_day_title(hardest_count))
    section << OutputFormatter.format_tabbed_entry(hardest_date.strftime('%B %d, %Y'), "(#{hardest_count} doses)")
    
    doses.each do |dose|
      section << OutputFormatter.format_dose_entry(dose[:time], dose[:substance], dose[:dose], dose[:route])
    end
    
    section
  end

  def build_summary_section
    section = []
    section << ""
    section << OutputFormatter.format_section_header(random_overall_title)
    section << OutputFormatter.format_summary_stat("Total entries", @data.length)
    section << OutputFormatter.format_summary_stat("Unique substances", @data.map { |e| e[:substance] }.uniq.count)
    section
  end

  def format_output(sections)
    sections.flatten.join("\n")
  end

  public

  private
  
  def substance_counts
    @substance_counts ||= DataAnalysisHelper.get_substance_counts(@data)
  end
  
  # Helper method to add section with optional comment
  def add_section(output, title, comment = nil, &block)
    output << ""
    output << "**#{title}**"
    
    if comment
      output << "\t#{comment}"
      output << ""
    end
    
    block.call(output) if block_given?
  end
  
  # Helper method to get time period usage counts from existing analysis
  def get_time_period_usage_from_analysis(time_analysis)
    DataAnalysisHelper.get_time_period_usage_from_analysis(time_analysis)
  end
  
  # Helper method to count routes by type
  def count_routes_by_type
    @route_counts ||= DataAnalysisHelper.count_routes_by_type(@data)
  end
  
  # Helper method to select random title
  def select_random_title(base_titles)
    TextGenerationHelper.select_random_title(base_titles)
  end
  
  def format_month_name(month_string)
    Date.strptime(month_string, '%Y-%m').strftime('%B')
  end
  
  def extract_user_from_data
    return 'User' if @data.empty?
    # Use the first line's username
    @data.first[:user] || 'User'
  end

  def random_peak_day_title
    day_stats = day_of_week_analysis
    top_day, top_day_count = day_stats.max_by { |_, count| count }
    
    # Dynamic titles based on the day of the week
    day_specific_titles = case top_day
    when 'Monday'
      ["Monday motivation hits different:", "Case of the Mondays? More like case of the FUN-days:"]
    when 'Tuesday'
      ["Tuesday was your main character day:", "Taco Tuesday? More like Turnt Tuesday:"]
    when 'Wednesday'
      ["Wednesday was your sweet spot:", "Hump day? More like PUMP day:"]
    when 'Thursday'
      ["Thursday was your power day:", "Thirsty Thursday lived up to its name:"]
    when 'Friday'
      ["Friday was your victory lap:", "Thank god it's Friday energy:"]
    when 'Saturday'
      ["Saturday was your main event:", "Weekend warrior status achieved:"]
    when 'Sunday'
      ["Sunday was your preparation day:", "Sunday funday taken literally:"]
    else
      []
    end
    
    base_titles = [
      "You really went hard on this day:",
      "You were fuckin' BALLING on this day:",
      "Looks like someone was mfin On one:"
    ]
    
    # Add dynamic title based on usage count
    if top_day_count >= 10
      base_titles << "#{top_day_count}-Use Bender, No Surrender. That's dedication"
    elsif top_day_count >= 5
      base_titles << "#{top_day_count} uses in one day? Someone was feeling themselves"
    end
    
    # Combine day-specific and general titles
    all_titles = day_specific_titles + base_titles
    all_titles.sample
  end

  def random_top_substances_title
    top_subs = top_substances
    return "Top 10 Substances:" if top_subs.empty?
    
    top_substance, top_count = top_subs.first
    total_uses = @data.length
    
    # Dynamic titles based on usage patterns
    if top_count > total_uses * 0.5
      return "#{top_substance} was clearly the main character:"
    elsif top_count > total_uses * 0.3
      return "#{top_substance} dominated the charts:"
    elsif top_subs.length >= 10
      return "Your diverse chemical portfolio:"
    else
      return "#{top_substance} was your go-to:"
    end
  end

  def random_monthly_title
    select_random_title([
      "Monthly Breakdown:",
      "The Monthly Sesh Schedule:",
      "How You Rolled Through the Year:",
      "Monthly Vibes Check:",
      "Your Monthly Mood Rings:"
    ])
  end

  def random_peak_times_title(time_analysis)
    usage = get_time_period_usage_from_analysis(time_analysis)
    total_uses = @data.length
    
    # Dynamic titles based on usage patterns
    if usage[:night] > total_uses * 0.4
      return "The night owl chronicles:"
    elsif usage[:morning] > total_uses * 0.4
      return "Rise and grind energy:"
    elsif usage[:evening] > total_uses * 0.5
      return "Happy hour hits different:"
    elsif usage[:afternoon] > total_uses * 0.4
      return "Afternoon adventure time:"
    else
      # Find the actual peak period
      peak_period = usage.max_by { |_, count| count }.first
      return "#{peak_period.capitalize} was your time:"
    end
  end

  def random_route_title
    route_counts = count_routes_by_type
    total_uses = @data.length
    
    # Dynamic titles based on route preferences
    if route_counts[:rectal] > total_uses * 0.3
      return "The backdoor bandit's guide to absorption:"
    elsif route_counts[:rectal] > total_uses * 0.1
      return "Sometimes you gotta go where the sun don't shine:"
    elsif route_counts[:insufflated] > total_uses * 0.6
      return "You're a little hoover:"
    elsif route_counts[:insufflated] > total_uses * 0.4
      return "Nose knows best:"
    elsif route_counts[:oral] > total_uses * 0.8
      return "Keep it simple, keep it oral:"
    else
      # Find the most common route and create dynamic title
      max_route = route_counts.max_by { |_, count| count }.first
      return "#{max_route.capitalize} was your preferred method:"
    end
  end

  def random_onetime_title
    select_random_title([
      "Obscure One-Time Uses:",
      "The Experimental Phase:",
      "One-Hit Wonders:",
      "Trial Run Hall of Fame:",
      "The 'Eh, Never Again' Collection:"
    ])
  end

  def random_overall_title
    select_random_title([
      "Overall Stats:",
      "The Numbers Don't Lie:",
      "Year in Review:",
      "Your Statistical Breakdown:",
      "The Final Tally:"
    ])
  end

  def get_peak_time_comment(time_analysis)
    usage = get_time_period_usage_from_analysis(time_analysis)
    TextGenerationHelper.get_peak_time_comment(usage, @data.length)
  end

  def random_hardest_day_title(dose_count)
    TextGenerationHelper.get_hardest_day_title(dose_count)
  end

  def get_top_substances_comment(top_subs)
    TextGenerationHelper.get_top_substances_comment(top_subs)
  end

  def get_monthly_breakdown_comment(peak_month_name, peak_count, monthly)
    TextGenerationHelper.get_monthly_breakdown_comment(peak_month_name, peak_count, monthly)
  end

  def get_streak_girlie_comment(substance)
    TextGenerationHelper.get_streak_girlie_comment(substance)
  end
end

# Image generation class
class SubstanceWrappedImageGenerator
  PADDING = 50
  LINE_HEIGHT = 25
  FONT_SIZE = 18
  CHAR_WIDTH = 10
  
  def initialize(substance_wrapped)
    @wrapped = substance_wrapped
  end
  
  def generate_image
    require 'mini_magick'
    
    data = { wrapped_text: @wrapped.generate_wrapped }
    text_content = data[:wrapped_text].split("\n")
    
    # Calculate dimensions first
    max_line_length = text_content.map(&:length).max
    max_left_width, max_right_width = calculate_table_widths(text_content)
    table_left_width = (max_left_width + 10) * CHAR_WIDTH
    table_total_width = table_left_width + (max_right_width * CHAR_WIDTH)
    content_width = [max_line_length * CHAR_WIDTH, table_total_width].max
    
    # Generate graphs with matching content width
    graph_generator = GraphGenerator.new(content_width, 400)
    monthly_data = @wrapped.monthly_summary
    line_graph = graph_generator.generate_monthly_doses_line_graph(monthly_data)
    
    monthly_category_data = graph_generator.calculate_monthly_category_data(@wrapped.instance_variable_get(:@data))
    category_chart = graph_generator.generate_category_stacked_chart(monthly_category_data)
    
    canvas_width = content_width + (PADDING * 2)
    # Add extra height for graphs (two 400px graphs + spacing + legend space)
    canvas_height = (text_content.length * LINE_HEIGHT) + (PADDING * 2) + 1000
    
    # Create background
    MiniMagick::Tool.new('magick') do |convert|
      convert.size "#{canvas_width}x#{canvas_height}"
      convert.depth "8"
      convert.colorspace "sRGB"
      convert << 'xc:black'
      convert << 'temp_canvas.png'
    end
    
    MiniMagick::Tool.new('magick') do |convert|
      convert << SharedConstants::ImageGeneration::BACKGROUND_IMAGE
      convert.resize "x#{canvas_height}"
      convert << 'temp_bg.png'
    end
    
    MiniMagick::Tool.new('magick') do |convert|
      convert << 'temp_canvas.png'
      convert << 'temp_bg.png'
      convert.gravity 'Center'
      convert.composite
      convert << 'temp_image.png'
    end
    
    # Clean up intermediate files
    File.delete('temp_canvas.png') if File.exist?('temp_canvas.png')
    File.delete('temp_bg.png') if File.exist?('temp_bg.png')
    
    image = MiniMagick::Image.open('temp_image.png')
    
    # Draw all text in a single batch operation for better performance
    left_col_x = PADDING
    right_col_x = left_col_x + table_left_width
    y_pos = PADDING
    
    # Batch all text operations together
    image.combine_options do |c|
      text_content.each do |line|
        process_line_batch(c, line, y_pos, left_col_x, right_col_x)
        y_pos += LINE_HEIGHT
      end
    end
    
    # Add graphs to the image
    graph_y_start = y_pos + 30
    
    # Save graphs as temporary files
    line_graph.write('temp_line_graph.png')
    category_chart.write('temp_category_chart.png')
    
    # Save current image
    image.write('temp_with_text.png')
    
    # Composite the line graph
    MiniMagick::Tool.new('magick') do |convert|
      convert << 'temp_with_text.png'
      convert << 'temp_line_graph.png'
      convert.colorspace 'sRGB'
      convert.gravity 'NorthWest'
      convert.geometry "+#{PADDING}+#{graph_y_start}"
      convert.composite
      convert.colorspace 'sRGB'
      convert << 'temp_with_line.png'
    end
    
    # Composite the category chart
    MiniMagick::Tool.new('magick') do |convert|
      convert << 'temp_with_line.png'
      convert << 'temp_category_chart.png'
      convert.colorspace 'sRGB'
      convert.gravity 'NorthWest'
      convert.geometry "+#{PADDING}+#{graph_y_start + 450}"
      convert.composite
      convert.colorspace 'sRGB'
      convert << 'temp_final.png'
    end
    
    # Load the final image
    image = MiniMagick::Image.open('temp_final.png')
    
    # Clean up temporary graph files
    File.delete('temp_line_graph.png') if File.exist?('temp_line_graph.png')
    File.delete('temp_category_chart.png') if File.exist?('temp_category_chart.png')
    File.delete('temp_image.png') if File.exist?('temp_image.png')
    File.delete('temp_with_text.png') if File.exist?('temp_with_text.png')
    File.delete('temp_with_line.png') if File.exist?('temp_with_line.png')
    File.delete('temp_final.png') if File.exist?('temp_final.png')
    image
  end
  
  def save_image(filename)
    image = generate_image
    image.write(filename)
    filename
  end
  
  private
  
  def calculate_table_widths(text_content)
    max_left_width = 0
    max_right_width = 0
    
    text_content.each do |line|
      clean_line = line.gsub('**', '')
      if clean_line.include?("\t")
        parts = clean_line.split("\t").reject(&:empty?)
        if parts.length >= 2
          max_left_width = [max_left_width, parts[-2].length].max
          max_right_width = [max_right_width, parts[-1].length].max
        end
      end
    end
    
    [max_left_width, max_right_width]
  end
  
  def process_line_batch(c, line, y_pos, left_col_x, right_col_x)
    is_bold = line.include?('**')
    clean_line = line.gsub('**', '')
    
    if clean_line.include?("\t")
      parts = clean_line.split("\t").reject(&:empty?)
      if parts.length >= 2
        draw_text_batch(c, parts[-2], left_col_x, y_pos, is_bold: is_bold)
        draw_text_batch(c, parts[-1], right_col_x, y_pos, is_bold: is_bold)
      else
        indented_line = clean_line.gsub("\t", "    ")
        draw_text_batch(c, indented_line, PADDING, y_pos, is_bold: is_bold)
      end
    else
      size = clean_line.include?("Substance Wrapped") ? FONT_SIZE * 1.5 : FONT_SIZE
      draw_text_batch(c, clean_line, PADDING, y_pos, is_bold: is_bold, size: size)
    end
  end

  def draw_text_batch(c, text, x, y, is_bold: false, size: FONT_SIZE, color: 'white', gravity: 'NorthWest')
    font = is_bold ? 'DejaVu-Sans-Bold' : 'DejaVu-Sans'
    c.font font
    c.pointsize size
    c.fill color
    c.gravity gravity
    c.annotate "+#{x}+#{y}", text
  end

end

# Data processing utilities
def invert_hash(input_hash)
  LutHelper.invert_hash(input_hash)
end

def safe_filename_for_user(user)
  FileHelper.safe_filename_for_user(user)
end

def process_csv_with_luts(user, year)
  LutHelper.process_csv_with_luts(user, year)
end

def upload_to_sploit(file_path)
  FileHelper.upload_to_sploit(file_path)
end

# Bot integration method
def generate_substance_wrapped(csv_data, user, year = Date.today.year)
  wrapped = SubstanceWrapped.new(csv_data, user, year)
  wrapped.generate_wrapped
end

# CLI interface
if __FILE__ == $0
  if ARGV.length < 1
    puts "Usage: ruby substance_wrapped.rb <user> [year]"
    puts ""
    puts "Examples:"
    puts "  ruby substance_wrapped.rb josie 2024"
    puts "  ruby substance_wrapped.rb josie"
    exit 1
  end

  # Store original directory for assets
  original_dir = Dir.pwd

  # Change to OUTPUT_DIR if specified for temporary file permissions
  if ENV['OUTPUT_DIR'] && Dir.exist?(ENV['OUTPUT_DIR'])
    Dir.chdir(ENV['OUTPUT_DIR'])
    # Set asset directory environment variable for use in helper files
    ENV['ASSET_DIR'] = File.join(original_dir, 'assets')
  end

  user = ARGV[0]
  year = ARGV[1] ? ARGV[1].to_i : Date.today.year

  # Generate safe filename based on user and year
  safe_user = safe_filename_for_user(user)
  output_file = "#{safe_user}_wrapped_#{year}.png"

  begin
    csv_data = process_csv_with_luts(user, year)
    wrapped = SubstanceWrapped.new(csv_data, user, year)
    
    # Generate image and upload to sploit.zip
    image_generator = SubstanceWrappedImageGenerator.new(wrapped)
    image_generator.save_image(output_file)
    
    url = upload_to_sploit(output_file)
    if url
      puts url
    else
      puts "Upload failed"
      exit 1
    end
    
  rescue LoadError => e
    puts "Error: mini_magick gem required for image generation"
    exit 1
  rescue Errno::ENOENT
    puts "Error: File 'data.csv' not found"
    exit 1
  rescue => e
    puts "Error: #{e.message}"
    exit 1
  end
end
