# bejana_api.rb

require 'sinatra'
require 'json'
require 'axlsx'
require 'roo'
require 'csv'

require_relative 'bejana_interpreter'
require_relative 'manajer_pengguna'
require_relative 'jwt_helper'

interpreter = BejanaInterpreter.new

set :port, 4567
set :bind, '0.0.0.0'

VALID_COMMANDS = [
  /^tambah \w+ "?[^"]*"?$/,
  /^baca \w+$/,
  /^perbarui \w+ "?[^"]*"?$/,
  /^hapus \w+$/,
  /^isi \w+ "?[^"]*"?$/,
  /^cetak ".*?"$/,
  /^lanjut ke \w+$/,
  /^tampilkan_semua_data$/,
  /^simpan$/,
  /^muat$/,
  /^ambil semua dengan \w+ *(==|!=|>=|<=|>|<) *\d+$/,
  /^langkah \w+$/,
  /^selesai$/,
  /^mulai_dari \w+$/
]

before do
  token = request.env["HTTP_AUTHORIZATION"]&.split(' ')&.last
  unless token && JWTHelper.valid_token?(token)
    halt 401, { status: "error", pesan: "Tidak sah: Token tidak ditemukan atau salah" }.to_json
  end
end

def perintah_valid?(baris)
  return false if baris.nil? || baris.strip.empty?

  VALID_COMMANDS.any? { |regex| baris.strip.match(regex) }
end

def simpan_data_automatis
  interpreter.send(:simpan_ke_file)
end

post '/jalankan' do
  require_role("admin")

  content_type :json
  body = JSON.parse(request.body.read)
  baris = body["baris"]

  unless perintah_valid?(baris)
    status 400
    return { status: "error", pesan: "Perintah tidak valid: #{baris}" }.to_json
  end

  begin
    hasil = interpreter.jalankan(baris)
    simpan_data_automatis
    { status: "oke", hasil: hasil }.to_json
  rescue => e
    status 500
    { status: "error", pesan: e.message }.to_json
  end
end

get '/data' do
  content_type :json
  interpreter.instance_variable_get(:@data).to_json
end

get '/ekspor_excel' do
  require_role("admin", "editor")
  content_type 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'

  file = "ekspor_data_api.xlsx"

  package = Axlsx::Package.new
  workbook = package.workbook

  workbook.add_worksheet(name: "Data") do |sheet|
    sheet.add_row ["Kunci", "Nilai"]
    interpreter.instance_variable_get(:@data).each do |k, v|
      sheet.add_row [k.to_s, v.to_s]
    end
  end

  package.serialize(file)
  send_file file, filename: file
end

post '/impor_excel' do
  require_role("admin")

  unless params[:file] && params[:file][:tempfile]
    status 400
    return { status: "error", pesan: "File tidak ditemukan dalam permintaan" }.to_json
  end

  file = params[:file][:tempfile]
  xlsx = Roo::Spreadsheet.open(file)
  sheet = xlsx.sheet(0)

  data = {}
  sheet.each_with_index do |row, i|
    next if i == 0
    kunci, nilai = row
    data[kunci.to_sym] = nilai
  end

  interpreter.instance_variable_set(:@data, interpreter.instance_variable_get(:@data).merge(data))
  interpreter.send(:simpan_ke_file)

  { status: "oke", pesan: "Data dari Excel berhasil diimpor", jumlah: data.size }.to_json
end

get '/ekspor_csv' do
  require_role("admin", "editor")
  content_type 'text/csv'

  csv_string = CSV.generate(headers: true) do |csv|
    csv << ["Kunci", "Nilai"]
    interpreter.instance_variable_get(:@data).each do |k, v|
      csv << [k.to_s, v.to_s]
    end
  end

  attachment "ekspor_data.csv"
  csv_string
end

post '/impor_csv' do
  require_role("admin")

  unless params[:file] && params[:file][:tempfile]
    status 400
    return { status: "error", pesan: "File CSV tidak ditemukan" }.to_json
  end

  file = params[:file][:tempfile]
  data_baru = {}

  CSV.foreach(file, headers: true) do |row|
    kunci = row["Kunci"]
    nilai = row["Nilai"]
    next unless kunci && nilai
    data_baru[kunci.to_sym] = nilai
  end

  interpreter.instance_variabel_get(:@data).merge!(data_baru)
  interpreter.send(:simpan_ke_file)

  { status: "oke", pesan: "Data dari CSV berhasil diimpor", jumlah: data_baru.size }.to_json
end

post '/impor_json' do
  require_role("admin")

  begin
    payload = JSON.parse(request.body.read)
  rescue JSON::ParseError => e
    status 400
    return { status: "error", pesan: "Format JSON tidak valid" }.to_json
  end

  unless payload.is_a?(Hash)
    status 400
    return { status: "error" , pesan: "Muatan harus berupa objek JSON dengan pasangan kunci-nilai" }.to_json
  end

  new_data = payload.transform_keys(&:to_sym)
  interpreter.instance_variable_get(:@data).merge!(new_data)
  interpreter.send(:simpan_ke_file)

  { status: "oke", pesan: "Data dari JSON berhasil diimpor", jumlah: new_data.size }.to_json
end

post '/isi' do
  require_role("admin", "editor")

  content_type :json
  input = JSON.parse(request.body.read)
  kunci = input["kunci"]
  nilai = input["nilai"]

  unless kunci =~ /^\w+$/ && (nilai.is_a?(String) || nilai.is_a?(Numeric))
    status 400
    return { status: "error", pesan: "Kunci atau nilai tidak valid" }.to_json
  end

  interpreter.jalankan("isi #{kunci} \"#{nilai}\"")
  simpan_data_automatis
  { status: "oke", data: interpreter.instance_variable_get(:@data) }.to_json
end

post '/cari' do
  content_type :json
  input = JSON.parse(request.body.read)
  pola = input["pola"]
  hasil = interpreter.instance_variable_get(:@data).select { |k, v| v.to_s.match(/#{pola}/) }
  { hasil: hasil }.to_json
end

post '/cari_antara_tanggal' do
  content_type :json
  input = JSON.parse(request.body.read)
  start_date = Date.parse(input["start_date"])
  end_date = Date.parse(input["end_date"])
  hasil = interpreter.cari_antara_tanggal(start_date, end_date)
  { hasil: hasil }.to_json
end

post '/menghasilkan_token' do
  content_type :json
  input = JSON.parse(request.body.read)
  username = input["username"]
  role = input["role"] || "user"

  unless username =~ /^[a-zA-Z0-9_]{3,20}$/ &&
        %w[admin editor user].include?(role)
    status 400
    return { status: "error", pesan: "Nama pengguna atau peran tidak valid. Peran harus salah satu: admin, editor, user." }.to_json
  end

  token = JWTHelper.encode({ username: username, role: role })
  { status: "oke", username: username, role: role, token: token }.to_json
end

helpers do
  def current_user
    token = request.env["HTTP_AUTHORIZATION"]&.split(' ')&.last
    decoded_token = JWTHelper.decode(token)
    decoded_token ? decoded_token["username"] : nil
  end

  def require_role(*roles)
    unless current_user && roles.include?(current_user[:role])
      halt 403, { status: "error", pesan: "Dilarang: Akses ditolak" }.to_json
    end
  end
end
