// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium;

import static org.assertj.core.api.Assertions.assertThat;
import static org.assertj.core.api.Assertions.assertThatExceptionOfType;
import static org.openqa.selenium.Proxy.ProxyType.AUTODETECT;
import static org.openqa.selenium.Proxy.ProxyType.DIRECT;
import static org.openqa.selenium.Proxy.ProxyType.MANUAL;
import static org.openqa.selenium.Proxy.ProxyType.PAC;
import static org.openqa.selenium.Proxy.ProxyType.SYSTEM;
import static org.openqa.selenium.Proxy.ProxyType.UNSPECIFIED;
import static org.openqa.selenium.remote.CapabilityType.PROXY;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import org.openqa.selenium.Proxy.ProxyType;
import org.openqa.selenium.json.Json;

@Tag("UnitTests")
class ProxyTest {

  @Test
  void testNotInitializedProxy() {
    Proxy proxy = new Proxy();

    assertThat(proxy.getProxyType()).isEqualTo(UNSPECIFIED);

    assertThat(proxy.getFtpProxy()).isNull();
    assertThat(proxy.getHttpProxy()).isNull();
    assertThat(proxy.getSslProxy()).isNull();
    assertThat(proxy.getSocksProxy()).isNull();
    assertThat(proxy.getSocksVersion()).isNull();
    assertThat(proxy.getSocksUsername()).isNull();
    assertThat(proxy.getSocksPassword()).isNull();
    assertThat(proxy.getNoProxy()).isNull();
    assertThat(proxy.getProxyAutoconfigUrl()).isNull();
    assertThat(proxy.isAutodetect()).isFalse();
  }

  @Test
  void testCanNotChangeAlreadyInitializedProxyType() {
    final Proxy proxy = new Proxy();
    proxy.setProxyType(DIRECT);

    assertThatExceptionOfType(IllegalStateException.class)
        .isThrownBy(() -> proxy.setAutodetect(true));

    assertThatExceptionOfType(IllegalStateException.class)
        .isThrownBy(() -> proxy.setSocksPassword(""));

    assertThatExceptionOfType(IllegalStateException.class)
        .isThrownBy(() -> proxy.setSocksUsername(""));

    assertThatExceptionOfType(IllegalStateException.class)
        .isThrownBy(() -> proxy.setSocksProxy(""));

    assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> proxy.setFtpProxy(""));

    assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> proxy.setHttpProxy(""));

    assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> proxy.setNoProxy(""));

    assertThatExceptionOfType(IllegalStateException.class)
        .isThrownBy(() -> proxy.setProxyAutoconfigUrl(""));

    assertThatExceptionOfType(IllegalStateException.class)
        .isThrownBy(() -> proxy.setProxyType(SYSTEM));

    assertThatExceptionOfType(IllegalStateException.class).isThrownBy(() -> proxy.setSslProxy(""));

    final Proxy proxy2 = new Proxy();
    proxy2.setProxyType(AUTODETECT);

    assertThatExceptionOfType(IllegalStateException.class)
        .isThrownBy(() -> proxy2.setProxyType(SYSTEM));

    assertThatExceptionOfType(IllegalStateException.class)
        .isThrownBy(() -> proxy.setSocksVersion(5));
  }

  @Test
  void testManualProxy() {
    Proxy proxy = new Proxy();

    proxy
        .setHttpProxy("http.proxy:1234")
        .setFtpProxy("ftp.proxy")
        .setSslProxy("ssl.proxy")
        .setNoProxy("localhost,127.0.0.*")
        .setSocksProxy("socks.proxy:65555")
        .setSocksVersion(5)
        .setSocksUsername("test1")
        .setSocksPassword("test2");

    assertThat(proxy.getProxyType()).isEqualTo(MANUAL);
    assertThat(proxy.getFtpProxy()).isEqualTo("ftp.proxy");
    assertThat(proxy.getHttpProxy()).isEqualTo("http.proxy:1234");
    assertThat(proxy.getSslProxy()).isEqualTo("ssl.proxy");
    assertThat(proxy.getSocksProxy()).isEqualTo("socks.proxy:65555");
    assertThat(proxy.getSocksVersion()).isEqualTo(5);
    assertThat(proxy.getSocksUsername()).isEqualTo("test1");
    assertThat(proxy.getSocksPassword()).isEqualTo("test2");
    assertThat(proxy.getNoProxy()).isEqualTo("localhost,127.0.0.*");

    assertThat(proxy.getProxyAutoconfigUrl()).isNull();
    assertThat(proxy.isAutodetect()).isFalse();
  }

  @Test
  void testPACProxy() {
    Proxy proxy = new Proxy();
    proxy.setProxyAutoconfigUrl("http://aaa/bbb.pac");

    assertThat(proxy.getProxyType()).isEqualTo(PAC);
    assertThat(proxy.getProxyAutoconfigUrl()).isEqualTo("http://aaa/bbb.pac");

    assertThat(proxy.getFtpProxy()).isNull();
    assertThat(proxy.getHttpProxy()).isNull();
    assertThat(proxy.getSslProxy()).isNull();
    assertThat(proxy.getSocksProxy()).isNull();
    assertThat(proxy.getSocksVersion()).isNull();
    assertThat(proxy.getSocksUsername()).isNull();
    assertThat(proxy.getSocksPassword()).isNull();
    assertThat(proxy.getNoProxy()).isNull();
    assertThat(proxy.isAutodetect()).isFalse();
  }

  @Test
  void testAutodetectProxy() {
    Proxy proxy = new Proxy();
    proxy.setAutodetect(true);

    assertThat(proxy.getProxyType().name()).isEqualTo(AUTODETECT.name());
    assertThat(proxy.isAutodetect()).isTrue();

    assertThat(proxy.getFtpProxy()).isNull();
    assertThat(proxy.getHttpProxy()).isNull();
    assertThat(proxy.getSslProxy()).isNull();
    assertThat(proxy.getSocksProxy()).isNull();
    assertThat(proxy.getSocksVersion()).isNull();
    assertThat(proxy.getSocksUsername()).isNull();
    assertThat(proxy.getSocksPassword()).isNull();
    assertThat(proxy.getNoProxy()).isNull();
    assertThat(proxy.getProxyAutoconfigUrl()).isNull();
  }

  @Test
  void manualProxyFromMap() {
    Map<String, Object> proxyData = new HashMap<>();
    proxyData.put("proxyType", "manual");
    proxyData.put("httpProxy", "http.proxy:1234");
    proxyData.put("ftpProxy", "ftp.proxy");
    proxyData.put("sslProxy", "ssl.proxy");
    proxyData.put("noProxy", "localhost,127.0.0.*");
    proxyData.put("socksProxy", "socks.proxy:65555");
    proxyData.put("socksVersion", 5);
    proxyData.put("socksUsername", "test1");
    proxyData.put("socksPassword", "test2");

    Proxy proxy = new Proxy(proxyData);

    assertThat(proxy.getProxyType()).isEqualTo(MANUAL);
    assertThat(proxy.getFtpProxy()).isEqualTo("ftp.proxy");
    assertThat(proxy.getHttpProxy()).isEqualTo("http.proxy:1234");
    assertThat(proxy.getSslProxy()).isEqualTo("ssl.proxy");
    assertThat(proxy.getSocksProxy()).isEqualTo("socks.proxy:65555");
    assertThat(proxy.getSocksVersion()).isEqualTo(5);
    assertThat(proxy.getSocksUsername()).isEqualTo("test1");
    assertThat(proxy.getSocksPassword()).isEqualTo("test2");
    assertThat(proxy.getNoProxy()).isEqualTo("localhost,127.0.0.*");

    assertThat(proxy.getProxyAutoconfigUrl()).isNull();
    assertThat(proxy.isAutodetect()).isFalse();
  }

  @Test
  void longSocksVersionFromMap() {
    Map<String, Object> proxyData = new HashMap<>();
    long l = 5;
    proxyData.put("proxyType", "manual");
    proxyData.put("httpProxy", "http.proxy:1234");
    proxyData.put("ftpProxy", "ftp.proxy");
    proxyData.put("sslProxy", "ssl.proxy");
    proxyData.put("noProxy", "localhost,127.0.0.*");
    proxyData.put("socksProxy", "socks.proxy:65555");
    proxyData.put("socksVersion", l);
    proxyData.put("socksUsername", "test1");
    proxyData.put("socksPassword", "test2");

    Proxy proxy = new Proxy(proxyData);

    assertThat(proxy.getSocksVersion()).isEqualTo(5);
  }

  @Test
  void manualProxyToJson() {
    Proxy proxy = new Proxy();
    proxy.setProxyType(ProxyType.MANUAL);
    proxy.setHttpProxy("http.proxy:1234");
    proxy.setFtpProxy("ftp.proxy");
    proxy.setSslProxy("ssl.proxy");
    proxy.setNoProxy("localhost,127.0.0.*");
    proxy.setSocksProxy("socks.proxy:65555");
    proxy.setSocksVersion(5);
    proxy.setSocksUsername("test1");
    proxy.setSocksPassword("test2");

    Map<String, Object> json = proxy.toJson();

    assertThat(json.get("proxyType")).isEqualTo("manual");
    assertThat(json.get("ftpProxy")).isEqualTo("ftp.proxy");
    assertThat(json.get("httpProxy")).isEqualTo("http.proxy:1234");
    assertThat(json.get("sslProxy")).isEqualTo("ssl.proxy");
    assertThat(json.get("socksProxy")).isEqualTo("socks.proxy:65555");
    assertThat(json.get("socksVersion")).isEqualTo(5);
    assertThat(json.get("socksUsername")).isEqualTo("test1");
    assertThat(json.get("socksPassword")).isEqualTo("test2");
    assertThat(json.get("noProxy")).isEqualTo(Arrays.asList("localhost", "127.0.0.*"));
    assertThat(json.entrySet()).hasSize(9);
  }

  @Test
  void pacProxyFromMap() {
    Map<String, String> proxyData = new HashMap<>();
    proxyData.put("proxyType", "PAC");
    proxyData.put("proxyAutoconfigUrl", "http://aaa/bbb.pac");

    Proxy proxy = new Proxy(proxyData);

    assertThat(proxy.getProxyType()).isEqualTo(PAC);
    assertThat(proxy.getProxyAutoconfigUrl()).isEqualTo("http://aaa/bbb.pac");

    assertThat(proxy.getFtpProxy()).isNull();
    assertThat(proxy.getHttpProxy()).isNull();
    assertThat(proxy.getSslProxy()).isNull();
    assertThat(proxy.getSocksProxy()).isNull();
    assertThat(proxy.getSocksVersion()).isNull();
    assertThat(proxy.getSocksUsername()).isNull();
    assertThat(proxy.getSocksPassword()).isNull();
    assertThat(proxy.getNoProxy()).isNull();
    assertThat(proxy.isAutodetect()).isFalse();
  }

  @Test
  void pacProxyToJson() {
    Proxy proxy = new Proxy();
    proxy.setProxyType(ProxyType.PAC);
    proxy.setProxyAutoconfigUrl("http://aaa/bbb.pac");

    Map<String, Object> json = proxy.toJson();

    assertThat(json.get("proxyType")).isEqualTo("pac");
    assertThat(json.get("proxyAutoconfigUrl")).isEqualTo("http://aaa/bbb.pac");
    assertThat(json.entrySet()).hasSize(2);
  }

  @Test
  void autodetectProxyFromMap() {
    Map<String, Object> proxyData = new HashMap<>();
    proxyData.put("proxyType", "AUTODETECT");
    proxyData.put("autodetect", true);

    Proxy proxy = new Proxy(proxyData);

    assertThat(proxy.getProxyType()).isEqualTo(AUTODETECT);
    assertThat(proxy.isAutodetect()).isTrue();

    assertThat(proxy.getFtpProxy()).isNull();
    assertThat(proxy.getHttpProxy()).isNull();
    assertThat(proxy.getSslProxy()).isNull();
    assertThat(proxy.getSocksProxy()).isNull();
    assertThat(proxy.getSocksVersion()).isNull();
    assertThat(proxy.getSocksUsername()).isNull();
    assertThat(proxy.getSocksPassword()).isNull();
    assertThat(proxy.getNoProxy()).isNull();
    assertThat(proxy.getProxyAutoconfigUrl()).isNull();
  }

  @Test
  void autodetectProxyToJson() {
    Proxy proxy = new Proxy();
    proxy.setProxyType(ProxyType.AUTODETECT);
    proxy.setAutodetect(true);

    Map<String, ?> json = proxy.toJson();

    assertThat(json.get("proxyType")).isEqualTo("autodetect");
    assertThat((Boolean) json.get("autodetect")).isTrue();
    assertThat(json.entrySet()).hasSize(2);
  }

  @Test
  void systemProxyFromMap() {
    Map<String, String> proxyData = new HashMap<>();
    proxyData.put("proxyType", "system");

    Proxy proxy = new Proxy(proxyData);

    assertThat(proxy.getProxyType()).isEqualTo(SYSTEM);

    assertThat(proxy.getFtpProxy()).isNull();
    assertThat(proxy.getHttpProxy()).isNull();
    assertThat(proxy.getSslProxy()).isNull();
    assertThat(proxy.getSocksProxy()).isNull();
    assertThat(proxy.getSocksVersion()).isNull();
    assertThat(proxy.getSocksUsername()).isNull();
    assertThat(proxy.getSocksPassword()).isNull();
    assertThat(proxy.getNoProxy()).isNull();
    assertThat(proxy.isAutodetect()).isFalse();
    assertThat(proxy.getProxyAutoconfigUrl()).isNull();
  }

  @Test
  void systemProxyToJson() {
    Proxy proxy = new Proxy();
    proxy.setProxyType(ProxyType.SYSTEM);

    Map<String, Object> json = proxy.toJson();

    assertThat(json.get("proxyType")).isEqualTo("system");
    assertThat(json.entrySet()).hasSize(1);
  }

  @Test
  void directProxyFromMap() {
    Map<String, String> proxyData = new HashMap<>();
    proxyData.put("proxyType", "DIRECT");

    Proxy proxy = new Proxy(proxyData);

    assertThat(proxy.getProxyType()).isEqualTo(DIRECT);

    assertThat(proxy.getFtpProxy()).isNull();
    assertThat(proxy.getHttpProxy()).isNull();
    assertThat(proxy.getSslProxy()).isNull();
    assertThat(proxy.getSocksProxy()).isNull();
    assertThat(proxy.getSocksVersion()).isNull();
    assertThat(proxy.getSocksUsername()).isNull();
    assertThat(proxy.getSocksPassword()).isNull();
    assertThat(proxy.getNoProxy()).isNull();
    assertThat(proxy.isAutodetect()).isFalse();
    assertThat(proxy.getProxyAutoconfigUrl()).isNull();
  }

  @Test
  void directProxyToJson() {
    Proxy proxy = new Proxy();
    proxy.setProxyType(ProxyType.DIRECT);

    Map<String, Object> json = proxy.toJson();

    assertThat(json.get("proxyType")).isEqualTo("direct");
    assertThat(json.entrySet()).hasSize(1);
  }

  @Test
  void constructingWithNullKeysWorksAsExpected() {
    Map<String, String> rawProxy = new HashMap<>();
    rawProxy.put("ftpProxy", null);
    rawProxy.put("httpProxy", "http://www.example.com");
    rawProxy.put("autodetect", null);
    Capabilities caps = new ImmutableCapabilities(PROXY, rawProxy);

    Proxy proxy = Proxy.extractFrom(caps);

    assertThat(proxy.getFtpProxy()).isNull();
    assertThat(proxy.isAutodetect()).isFalse();
    assertThat(proxy.getHttpProxy()).isEqualTo("http://www.example.com");
  }

  @Test
  @Disabled
  public void serializesAndDeserializesWithoutError() {
    Proxy proxy = new Proxy();
    proxy.setProxyAutoconfigUrl("http://www.example.com/config.pac");

    Capabilities caps = new ImmutableCapabilities(PROXY, proxy);

    String rawJson = new Json().toJson(caps);
    Capabilities converted = new Json().toType(rawJson, Capabilities.class);

    Object returnedProxy = converted.getCapability(PROXY);
    assertThat(returnedProxy).isInstanceOf(Proxy.class);
  }
}
