#!/usr/bin/env ruby

# This script verifies the "people" app generated by tool/rails_runner.rb.
# All CRUD operations are confirmed.
# Generated by ChatGPT.

require "net/http"
require "uri"
require "cgi"
require "securerandom"

BASE_URL = "http://localhost:3000"

class Session
  def initialize(base_url)
    @base_uri = URI(base_url)
    @cookies = {}
  end

  def get(path, headers = {})
    request(Net::HTTP::Get, path, headers: headers)
  end

  def post(path, form: {}, headers: {})
    request(Net::HTTP::Post, path, form: form, headers: headers)
  end

  private

  def request(klass, path, form: nil, headers: {})
    uri = URI.join(@base_uri.to_s, path)

    http = Net::HTTP.new(uri.host, uri.port)

    req = klass.new(uri)

    if @cookies.any?
      req["Cookie"] = @cookies.map { |k, v| "#{k}=#{v}" }.join("; ")
    end

    headers.each do |k, v|
      req[k] = v
    end

    req.set_form_data(form) if form

    response = http.request(req)

    store_cookies(response)

    unless response.is_a?(Net::HTTPSuccess) ||
           response.is_a?(Net::HTTPRedirection)
      raise "#{req.method} #{path} failed: #{response.code}\n#{response.body}"
    end

    response
  end

  def store_cookies(response)
    set_cookie_headers = response.get_fields("Set-Cookie")
    return unless set_cookie_headers

    set_cookie_headers.each do |cookie|
      pair = cookie.split(";").first
      key, value = pair.split("=", 2)
      @cookies[key] = value
    end
  end
end

def extract_csrf_token(html)
  match = html.match(
    /<meta\s+name=["']csrf-token["']\s+content=["']([^"']+)["']/
  )

  raise "Could not find CSRF token" unless match

  CGI.unescapeHTML(match[1])
end

def extract_person_id(location)
  match = location.match(%r{/people/(\d+)})
  raise "Could not extract person id from #{location}" unless match
  match[1]
end

puts "Waiting for server to start..."
tries = 0
begin
  sock = TCPSocket.new("localhost", 3000)
rescue
  puts $!
  sleep 5
  retry unless tries > 10
  raise
else
  sock.close
end

session = Session.new(BASE_URL)

original_name = "Person-#{SecureRandom.hex(6)}"
updated_name  = "Updated-#{SecureRandom.hex(6)}"

puts "Original name: #{original_name}"
puts "Updated name:  #{updated_name}"

#
# Step 1: Load index page to establish session + csrf token
#
index_response = session.get("/people")

csrf_token = extract_csrf_token(index_response.body)

puts "Fetched CSRF token"

#
# Step 2: Create person
#
create_response = session.post(
  "/people",
  form: {
    "person[name]" => original_name
  },
  headers: {
    "X-CSRF-Token" => csrf_token,
    "Referer" => "#{BASE_URL}/people"
  }
)

location = create_response["Location"] || create_response["location"]

raise "Missing redirect location" unless location

person_id = extract_person_id(location)

puts "Created person id=#{person_id}"

#
# Step 3: Verify person appears in index
#
index_response = session.get("/people")

unless index_response.body.include?(original_name)
  raise "Original name not found in people index"
end

puts "Verified person appears in index"

#
# Step 4: View person page
#
show_response = session.get("/people/#{person_id}")

unless show_response.body.include?(original_name)
  raise "Original name missing from show page"
end

puts "Verified person show page"

#
# Step 5: Update person name
#
update_csrf = extract_csrf_token(show_response.body)

update_response = session.post(
  "/people/#{person_id}",
  form: {
    "_method" => "patch",
    "person[name]" => updated_name
  },
  headers: {
    "X-CSRF-Token" => update_csrf,
    "Referer" => "#{BASE_URL}/people/#{person_id}/edit"
  }
)

unless update_response.is_a?(Net::HTTPRedirection)
  raise "Update did not redirect"
end

puts "Updated person"

#
# Step 6: Verify updated name in index
#
updated_index = session.get("/people")

unless updated_index.body.include?(updated_name)
  raise "Updated name not found in people index"
end

if updated_index.body.include?(original_name)
  raise "Original name still present after update"
end

puts "Verified updated name in index"

#
# Step 7: Verify updated show page
#
updated_show = session.get("/people/#{person_id}")

unless updated_show.body.include?(updated_name)
  raise "Updated name missing from show page"
end

puts "Verified updated show page"

#
# Step 8: Delete person
#
delete_csrf = extract_csrf_token(updated_show.body)

delete_response = session.post(
  "/people/#{person_id}",
  form: {
    "_method" => "delete"
  },
  headers: {
    "X-CSRF-Token" => delete_csrf,
    "Referer" => "#{BASE_URL}/people/#{person_id}"
  }
)

unless delete_response.is_a?(Net::HTTPRedirection)
  raise "Delete did not redirect"
end

puts "Deleted person"

#
# Step 9: Verify person is gone
#
final_index = session.get("/people")

if final_index.body.include?(updated_name)
  raise "Updated person still exists after deletion"
end

puts "Verified person removed"

puts
puts "SUCCESS"