require "ssh2"

class SFTPClient
  @host : String
  @port : Int32
  @username : String
  @password : String?
  @private_key_path : String?
  @csv_file_path : String
  @base_directory : String

  def initialize(@host : String, @port : Int32, @username : String, @csv_file_path : String, password : String? = nil, @private_key_path : String? = nil)
    if @password.nil? && @private_key_path.nil?
      raise "Either password or private key path must be provided"
    end
    # Extract base directory from the main CSV path
    @base_directory = File.dirname(@csv_file_path)
  end
  
  def vitals_csv_path : String
    "#{@base_directory}/vitals.csv"
  end
  
  def labs_csv_path : String
    "#{@base_directory}/labs.csv"
  end

  def fetch_csv_content : String
    puts "DEBUG: Connecting to #{@username}@#{@host}:#{@port} with password: '#{@password}'"
    puts "DEBUG: CSV path: #{@csv_file_path}"
    SSH2::Session.open(@host, @port) do |session|
      if password = @password
        puts "DEBUG: Attempting password login..."
        session.login(@username, password)
        puts "DEBUG: Login successful!"
      elsif private_key = @private_key_path
        # For key-based auth, we need both private and public key paths
        public_key = private_key + ".pub"
        session.login_with_pubkey(@username, private_key, public_key)
      else
        raise "No authentication method available"
      end

      session.sftp_session do |sftp|
        puts "DEBUG: Trying to open file at: #{@csv_file_path}"
        file = sftp.open(@csv_file_path, "r")
        puts "DEBUG: File opened successfully"
        
        file_content = String.build do |str|
          buffer = Bytes.new(4096)
          total_bytes = 0
          while (bytes_read = file.read(buffer)) > 0
            total_bytes += bytes_read
            str.write(buffer[0, bytes_read])
          end
          puts "DEBUG: Read #{total_bytes} bytes from file"
        end
        file.close
        puts "DEBUG: File closed, content length: #{file_content.size} characters"
        puts "DEBUG: First 200 chars of content: #{file_content[0..199]}" if file_content.size > 0
        file_content
      end
    end
  rescue ex : Exception
    raise "Failed to fetch CSV from SFTP: #{ex.message}"
  end

  def append_to_csv(line : String) : Nil
    SSH2::Session.open(@host, @port) do |session|
      if password = @password
        session.login(@username, password)
      elsif private_key = @private_key_path
        # For key-based auth, we need both private and public key paths
        public_key = private_key + ".pub"
        session.login_with_pubkey(@username, private_key, public_key)
      else
        raise "No authentication method available"
      end

      session.sftp_session do |sftp|
        puts "DEBUG: Trying to append to file at: #{@csv_file_path}"
        
        # Read existing content
        puts "DEBUG: Reading existing file content..."
        existing_content = ""
        begin
          file = sftp.open(@csv_file_path, "r")
          existing_content = String.build do |str|
            buffer = Bytes.new(4096)
            while (bytes_read = file.read(buffer)) > 0
              str.write(buffer[0, bytes_read])
            end
          end
          file.close
          puts "DEBUG: Read #{existing_content.size} bytes from existing file"
        rescue ex
          puts "WARNING: Could not read existing file: #{ex.message}"
          # File might not exist, that's ok
        end
        
        # Append the new line
        line_with_newline = line.ends_with?('\n') ? line : "#{line}\n"
        new_content = existing_content + line_with_newline
        puts "DEBUG: New content size: #{new_content.size} bytes"
        
        # Write the entire content back
        puts "DEBUG: Writing updated content to file..."
        file = sftp.open(@csv_file_path, "w")
        file.write(new_content.to_slice)
        file.close
        puts "DEBUG: Successfully wrote updated content"
      end
    end
  rescue ex : Exception
    raise "Failed to append to CSV via SFTP: #{ex.message}"
  end

  def write_csv_content(content : String) : Nil
    puts "DEBUG: Writing full CSV content, length: #{content.size}"
    SSH2::Session.open(@host, @port) do |session|
      if password = @password
        session.login(@username, password)
      elsif private_key = @private_key_path
        # For key-based auth, we need both private and public key paths
        public_key = private_key + ".pub"
        session.login_with_pubkey(@username, private_key, public_key)
      else
        raise "No authentication method available"
      end

      session.sftp_session do |sftp|
        puts "DEBUG: Opening file for writing: #{@csv_file_path}"
        file = sftp.open(@csv_file_path, "w")
        bytes_written = file.write(content.to_slice)
        puts "DEBUG: Wrote #{bytes_written} bytes to file"
        
        # Explicitly flush and sync the file
        file.flush if file.responds_to?(:flush)
        file.fsync if file.responds_to?(:fsync)
        
        file.close
        puts "DEBUG: File closed successfully"
        
        # Verify the write by reading back the first few bytes
        begin
          verify_file = sftp.open(@csv_file_path, "r")
          buffer = Bytes.new(100)
          bytes_read = verify_file.read(buffer)
          verify_content = String.new(buffer[0, bytes_read])
          puts "DEBUG: Verification read first #{bytes_read} bytes: #{verify_content[0..50]}..."
          verify_file.close
        rescue ex
          puts "DEBUG: Could not verify write: #{ex.message}"
        end
      end
    end
    puts "DEBUG: Successfully wrote full CSV content"
  rescue ex : Exception
    puts "ERROR: Failed to write CSV content via SFTP: #{ex.message}"
    raise "Failed to write CSV content via SFTP: #{ex.message}"
  end

  def test_connection : Bool
    SSH2::Session.open(@host, @port) do |session|
      if password = @password
        session.login(@username, password)
      elsif private_key = @private_key_path
        # For key-based auth, we need both private and public key paths
        public_key = private_key + ".pub"
        session.login_with_pubkey(@username, private_key, public_key)
      else
        return false
      end

      session.sftp_session do |sftp|
        # Try to stat the file to check if it exists
        stat = sftp.stat(@csv_file_path)
        return stat.size > 0
      end
    end
    true
  rescue
    false
  end

  # Methods for vitals CSV
  def fetch_vitals_csv : String
    fetch_generic_csv(vitals_csv_path)
  end

  def append_to_vitals_csv(line : String) : Nil
    append_to_generic_csv(vitals_csv_path, line)
  end

  def write_vitals_csv(content : String) : Nil
    write_generic_csv(vitals_csv_path, content)
  end

  # Methods for labs CSV
  def fetch_labs_csv : String
    fetch_generic_csv(labs_csv_path)
  end

  def append_to_labs_csv(line : String) : Nil
    append_to_generic_csv(labs_csv_path, line)
  end

  def write_labs_csv(content : String) : Nil
    write_generic_csv(labs_csv_path, content)
  end

  # Generic CSV operations
  private def fetch_generic_csv(path : String) : String
    SSH2::Session.open(@host, @port) do |session|
      if password = @password
        session.login(@username, password)
      elsif private_key = @private_key_path
        public_key = private_key + ".pub"
        session.login_with_pubkey(@username, private_key, public_key)
      else
        raise "No authentication method available"
      end

      session.sftp_session do |sftp|
        begin
          file = sftp.open(path, "r")
          file_content = String.build do |str|
            buffer = Bytes.new(4096)
            while (bytes_read = file.read(buffer)) > 0
              str.write(buffer[0, bytes_read])
            end
          end
          file.close
          file_content
        rescue ex
          # Return empty CSV with header if file doesn't exist
          case path
          when vitals_csv_path
            "timestamp,user_id,pulse,systolic_bp,diastolic_bp,blood_oxygen,temperature,notes\n"
          when labs_csv_path
            "timestamp,user_id,test_type,value,unit,reference_range,notes\n"
          else
            ""
          end
        end
      end
    end
  rescue ex : Exception
    raise "Failed to fetch CSV from SFTP: #{ex.message}"
  end

  private def append_to_generic_csv(path : String, line : String) : Nil
    SSH2::Session.open(@host, @port) do |session|
      if password = @password
        session.login(@username, password)
      elsif private_key = @private_key_path
        public_key = private_key + ".pub"
        session.login_with_pubkey(@username, private_key, public_key)
      else
        raise "No authentication method available"
      end

      session.sftp_session do |sftp|
        # Read existing content or use default header
        existing_content = ""
        begin
          file = sftp.open(path, "r")
          existing_content = String.build do |str|
            buffer = Bytes.new(4096)
            while (bytes_read = file.read(buffer)) > 0
              str.write(buffer[0, bytes_read])
            end
          end
          file.close
        rescue ex
          # Create with header if doesn't exist
          case path
          when vitals_csv_path
            existing_content = "timestamp,user_id,pulse,systolic_bp,diastolic_bp,blood_oxygen,temperature,notes\n"
          when labs_csv_path
            existing_content = "timestamp,user_id,test_type,value,unit,reference_range,notes\n"
          end
        end
        
        # Append the new line
        line_with_newline = line.ends_with?('\n') ? line : "#{line}\n"
        new_content = existing_content + line_with_newline
        
        # Write back
        file = sftp.open(path, "w")
        file.write(new_content.to_slice)
        file.flush if file.responds_to?(:flush)
        file.fsync if file.responds_to?(:fsync)
        file.close
      end
    end
  rescue ex : Exception
    raise "Failed to append to CSV via SFTP: #{ex.message}"
  end

  private def write_generic_csv(path : String, content : String) : Nil
    SSH2::Session.open(@host, @port) do |session|
      if password = @password
        session.login(@username, password)
      elsif private_key = @private_key_path
        public_key = private_key + ".pub"
        session.login_with_pubkey(@username, private_key, public_key)
      else
        raise "No authentication method available"
      end

      session.sftp_session do |sftp|
        file = sftp.open(path, "w")
        bytes_written = file.write(content.to_slice)
        file.flush if file.responds_to?(:flush)
        file.fsync if file.responds_to?(:fsync)
        file.close
      end
    end
  rescue ex : Exception
    raise "Failed to write CSV content via SFTP: #{ex.message}"
  end
end