require 'net/http'
require 'json'

module Wuzapi
  class Client
    class Error < StandardError; end
    class AuthenticationError < Error; end
    class ConnectionError < Error; end

    attr_reader :base_url

    def initialize(base_url)
      @base_url = normalize_url(base_url)
    end

    # Admin Endpoints (Use Authorization header)
    def create_user(admin_token, name, user_token)
      payload = { name: name, token: user_token }
      request(:post, '/admin/users', payload, admin_auth_headers(admin_token))
    end

    def delete_user(admin_token, user_id)
      request(:delete, "/admin/users/#{user_id}", nil, admin_auth_headers(admin_token))
    end

    # User Endpoints (Use token header)
    def send_text(user_token, phone_number, body)
      # Payload MUST be Case-Sensitive: Key 'Phone' and 'Body'
      payload = { 'Phone' => phone_number, 'Body' => body }
      request(:post, '/chat/send/text', payload, user_auth_headers(user_token))
    end

    def send_image(user_token, phone_number, base64_data, caption = nil)
      payload = { 'Phone' => phone_number, 'Body' => base64_data, 'Caption' => caption }
      request(:post, '/chat/send/image', payload, user_auth_headers(user_token))
    end

    def send_file(user_token, phone_number, base64_data, filename)
      payload = { 'Phone' => phone_number, 'Body' => base64_data, 'Filename' => filename }
      request(:post, '/chat/send/file', payload, user_auth_headers(user_token))
    end

    def send_reaction(user_token, phone_number, message_id, emoji)
      payload = { 'Phone' => phone_number, 'Body' => emoji, 'Id' => message_id }
      request(:post, '/chat/react', payload, user_auth_headers(user_token))
    end

    def session_status(user_token)
      request(:get, '/session/status', nil, user_auth_headers(user_token))
    end

    def get_qr_code(user_token)
      request(:get, '/session/qr', nil, user_auth_headers(user_token))
    end

    def session_connect(user_token)
      request(:post, '/session/connect', {}, user_auth_headers(user_token))
    end

    def session_disconnect(user_token)
      request(:post, '/session/disconnect', nil, user_auth_headers(user_token))
    end

    def session_logout(user_token)
      request(:get, '/session/logout', nil, user_auth_headers(user_token))
    end

    def set_webhook(user_token, webhook_url)
      # Wuzapi expects PascalCase keys 'WebhookURL' and 'Events' with 'All' per user verification.
      payload = { 'WebhookURL' => webhook_url, 'Events' => ['All'] }
      request(:post, '/webhook', payload, user_auth_headers(user_token))
    end

    def update_webhook(user_token, webhook_url)
      payload = { 'WebhookURL' => webhook_url, 'Events' => ['All'] }
      request(:put, '/webhook', payload, user_auth_headers(user_token))
    end

    def get_webhook(user_token)
      request(:get, '/webhook', nil, user_auth_headers(user_token))
    end

    private

    def normalize_url(url)
      url.to_s.gsub(%r{/$}, '')
    end

    def admin_auth_headers(token)
      { 'Authorization' => token }
    end

    def user_auth_headers(token)
      { 'token' => token }
    end

    def request(method, path, payload, headers)
      uri = URI.parse("#{base_url}#{path}")
      http = Net::HTTP.new(uri.host, uri.port)

      if uri.scheme == 'https'
        http.use_ssl = true
        http.verify_mode = OpenSSL::SSL::VERIFY_NONE
      end

      request_obj = case method
                    when :get
                      Net::HTTP::Get.new(uri.request_uri)
                    when :post
                      Net::HTTP::Post.new(uri.request_uri)
                    when :put
                      Net::HTTP::Put.new(uri.request_uri)
                    when :delete
                      Net::HTTP::Delete.new(uri.request_uri)
                    end

      # Common headers
      request_obj['Content-Type'] = 'application/json'
      request_obj['Accept'] = 'application/json'

      # Auth headers
      headers.each { |k, v| request_obj[k] = v }

      request_obj.body = payload.to_json if payload

      begin
        response = http.request(request_obj)
        handle_response(response)
      rescue Errno::ECONNREFUSED, Net::OpenTimeout, Net::ReadTimeout => e
        raise ConnectionError, "Could not connect to Wuzapi: #{e.message}"
      end
    end

    def handle_response(response)
      Rails.logger.info "WUZAPI RAW RESPONSE: status=#{response.code} ct=#{response['content-type']} body=#{response.body.to_s.truncate(1000)}"

      if response.code.to_i >= 200 && response.code.to_i < 300
        content_type = response['content-type'] || ''

        if content_type.include?('image/')
          require 'base64'
          base64_image = Base64.strict_encode64(response.body)
          return { 'qrcode' => "data:#{content_type};base64,#{base64_image}" }
        end

        begin
          body = JSON.parse(response.body)
          # Normalize keys to 'qrcode'
          # Check nested data object
          if body['data'].is_a?(Hash)
            found = body['data']['qrcode'] || body['data']['qr'] || body['data']['QRCode'] || body['data']['QR'] || body['data']['base64'] || body['data']['image']
            body['qrcode'] = found if found
          # Check if data is the string itself
          elsif body['data'].is_a?(String) && (body['data'].start_with?('data:') || body['data'].length > 50)
            body['qrcode'] = body['data']
          end

          # Check root keys if still not found
          body['qrcode'] = body['qr'] || body['QRCode'] || body['QR'] || body['base64'] || body['image'] unless body['qrcode']

          return body
        rescue JSON::ParserError
          Rails.logger.warn "Wuzapi response parse error or non-JSON: #{response.body}"
          return { 'raw_body' => response.body }
        end
      elsif response.code.to_i == 401 || response.code.to_i == 403
        raise AuthenticationError, "Authentication failed: #{response.code} #{response.body}"
      else
        raise Error, "API Error: #{response.code} #{response.body}"
      end
    end
  end
end
