# prompt_manager/lib/prompt_manager/storage/file_system_adapter.rb


require 'json'

class PromptManager::Storage::FileSystemAdapter
  PARAMS_EXTENSION = '.json'.freeze
  PROMPT_EXTENSION = '.txt'.freeze

  attr_reader :prompts_dir

  def initialize(
      prompts_dir:  '.prompts',
      search_proc:  nil   # Example: ->(q) {`ag -l #{q}`}
    )

    # validate that prompts_dir exist and is in fact a directory.
    unless Dir.exist?(prompts_dir)
      raise "Directory #{prompts_dir} does not exist or is not a directory"
    end

    @prompts_dir  = prompts_dir
    @search_proc  = search_proc
  end


  def get(id:)
    validate_id(id)

    {
      id:         id,
      text:       prompt_text(id),
      parameters: parameter_values(id)
    }
  end


  # Retrieve prompt text by its id
  def prompt_text(prompt_id)
    read_file(file_path(prompt_id, PROMPT_EXTENSION))
  end


  # Retrieve parameter values by its id
  def parameter_values(prompt_id)
    json_content = read_file(file_path(prompt_id, PARAMS_EXTENSION))
    deserialize(json_content)
  end


  # Save prompt text and parameter values to corresponding files
  def save(
      id:, 
      text: "", 
      parameters: {}
    )
    validate_id(id)

    prompt_filepath = file_path(id, PROMPT_EXTENSION)
    params_filepath = file_path(id, PARAMS_EXTENSION)
    
    write_with_error_handling(prompt_filepath, text)
    write_with_error_handling(params_filepath, serialize(parameters))
  end


  # Delete prompted text and parameter values files
  def delete(id:)
    validate_id(id)

    prompt_filepath = file_path(id, PROMPT_EXTENSION)
    params_filepath = file_path(id, PARAMS_EXTENSION)
    
    delete_with_error_handling(prompt_filepath)
    delete_with_error_handling(params_filepath)
  end


  def search(for_what)
    if @search_proc
      @search_proc.call(for_what)
    else
      search_prompts(for_what)
    end
  end


  ##########################################
  private

  def validate_id(id)
    raise ArgumentError, 'Invalid ID format' unless id =~ /^[a-zA-Z0-9\-_]+$/
  end


  def write_with_error_handling(file_path, content)
    begin
      File.write(file_path, content)
    rescue IOError => e
      raise "Failed to write to file: #{e.message}"
    end
  end


  def delete_with_error_handling(file_path)
    begin
      FileUtils.rm_f(file_path)
    rescue IOError => e
      raise "Failed to delete file: #{e.message}"
    end
  end


  def file_path(id, extension)
    File.join(@prompts_dir, "#{id}#{extension}")
  end


  def read_file(full_path)
    raise IOError, 'File does not exist' unless File.exist?(full_path)
    File.read(full_path)
  end


  def search_prompts(search_term)
    query_term = search_term.downcase

    Dir.glob(File.join(@prompts_dir, "*#{PROMPT_EXTENSION}")).each_with_object([]) do |file_path, ids|
      File.open(file_path) do |file|
        file.each_line do |line|
          if line.downcase.include?(query_term)
            ids << File.basename(file_path, PROMPT_EXTENSION)
            next
          end
        end
      end
    end.uniq
  end


  def serialize(data)
    data.to_json
  end


  def deserialize(data)
    JSON.parse(data, symbolize_names: true)
  end
end