# frozen_string_literal: true

module Webdrone
  class Browser
    def form
      @form ||= Form.new self
    end
  end

  class Form
    attr_accessor :data
    attr_reader :a0

    def initialize(a0)
      @a0 = a0
      @data = nil
    end

    def with_xpath(xpath = nil, &block)
      old_xpath, @xpath = @xpath, xpath
      instance_eval(&block)
    rescue StandardError => error
      Webdrone.report_error(@a0, error)
    ensure
      @xpath = old_xpath
    end

    def save(filename:, sheet:, item:, name: 'ITEM')
      prev = @data
      data = {}
      @data = data

      yield
    ensure
      File.open(".#{filename}.lock", File::RDWR | File::CREAT, 0o644) do
        items = {}
        items[item] = data

        begin
          workbook = RubyXL::Parser.parse(filename)
          worksheet = workbook[sheet]
          worksheet ||= workbook.add_worksheet sheet
        rescue StandardError
          workbook = RubyXL::Workbook.new
          worksheet = workbook[0]
          worksheet.sheet_name = sheet
        end

        rows = worksheet.sheet_data.rows.collect do |row|
          row.cells.collect do |cell|
            cell&.value
          end
        end
        heads = rows.shift || []
        rows.each do |row|
          item = {}
          key = nil
          row.each_with_index do |val, i|
            val = val.to_s if val
            if i.zero?
              key = val
            elsif key
              items[key] = {} unless items[key]
              items[key][heads[i]] = val if !heads[i].nil? && items[key][heads[i]].nil?
            end
          end
        end
        x = heads.shift
        x ||= name
        worksheet.add_cell 0, 0, x

        heads += data.keys.sort
        heads = heads.uniq

        heads.each_with_index do |field, coli|
          worksheet.add_cell 0, coli + 1, field
        end
        worksheet.change_row_bold 0, true
        items.each_with_index do |elem, rowi|
          key, item = elem
          worksheet.add_cell rowi + 1, 0, key
          heads.each_with_index do |field, coli|
            worksheet.add_cell rowi + 1, coli + 1, item[field]
          end
        end

        workbook.write filename
      end

      @data = prev
    end

    def set(key, val, n: 1, visible: true, scroll: false, parent: a0.conf.parent, mark: false)
      item = find_item(key, n: n, visible: visible, scroll: scroll, parent: parent)
      @a0.mark.mark_item item if mark
      if item.tag_name == 'select'
        options = item.find_elements :xpath, Webdrone::XPath.option(val).to_s
        raise "option not found for value: #{val} " if options.empty?

        # Take the exact match, or the first one
        option = options.find do |elem|
          elem.text == val
        end
        option ||= options.first

        option.click
      else
        item.clear
        item.send_keys(val)
      end
      @data[key] = val if @data
      nil
    rescue StandardError => error
      Webdrone.report_error(@a0, error)
    end

    def get(key, n: 1, visible: true, scroll: false, parent: a0.conf.parent, mark: false)
      item = find_item(key, n: n, visible: visible, scroll: scroll, parent: parent)
      @a0.mark.mark_item item if mark
      item[:value]
    rescue StandardError => error
      Webdrone.report_error(@a0, error)
    end

    def clic(key, n: 1, visible: true, scroll: false, parent: a0.conf.parent, mark: false)
      item = find_item(key, n: n, visible: visible, scroll: scroll, parent: parent)
      @a0.mark.mark_item item if mark
      item.click
    rescue StandardError => error
      Webdrone.report_error(@a0, error)
    end

    def selected?(key, n: 1, visible: true, scroll: false, parent: a0.conf.parent, mark: false)
      item = find_item(key, n: n, visible: visible, scroll: scroll, parent: parent)
      @a0.mark.mark_item item if mark
      item.selected?
    rescue StandardError => error
      Webdrone.report_error(@a0, error)
    end

    def mark(key, n: 1, visible: true, scroll: false, parent: a0.conf.parent, color: '#af1616', times: nil, delay: nil, shot: nil)
      @a0.mark.mark_item find_item(key, n: n, visible: visible, scroll: scroll, parent: parent), color: color, times: times, delay: delay, shot: shot
    rescue StandardError => error
      Webdrone.report_error(@a0, error)
    end

    def submit(key = nil, n: 1, visible: true, scroll: false, parent: a0.conf.parent, mark: false)
      item = find_item(key, n: n, visible: visible, scroll: scroll, parent: parent) if key
      @a0.mark.mark_item item if mark
      @lastitem.submit
    rescue StandardError => error
      Webdrone.report_error(@a0, error)
    end

    def xlsx(sheet: nil, filename: nil)
      @a0.xlsx.dict(sheet: sheet, filename: filename).each do |k, v|
        set k, v
      end
    rescue StandardError => error
      Webdrone.report_error(@a0, error)
    end

    def find_item(key, n: 1, visible: true, scroll: false, parent: a0.conf.parent)
      @lastitem = \
        if @xpath.respond_to? :call
          @a0.find.xpath @xpath.call(key).to_s, n: n, visible: visible, scroll: scroll, parent: parent
        elsif @xpath.is_a?(String) && @xpath.include?('%s')
          @a0.find.xpath sprintf(@xpath, key), n: n, visible: visible, scroll: scroll, parent: parent
        else
          @a0.find.xpath Webdrone::XPath.field(key).to_s, n: n, visible: visible, scroll: scroll, parent: parent
        end
    end
  end
end