// +build !race

// TODO(e.burkov): remove this weird buildtag.

package home

import (
	"context"
	"encoding/base64"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/AdguardTeam/dnsproxy/proxyutil"
	"github.com/AdguardTeam/dnsproxy/upstream"
	"github.com/miekg/dns"
	"github.com/stretchr/testify/assert"
)

const yamlConf = `bind_host: 127.0.0.1
bind_port: 3000
users: []
language: en
rlimit_nofile: 0
web_session_ttl: 720
dns:
  bind_host: 127.0.0.1
  port: 5354
  statistics_interval: 90
  querylog_enabled: true
  querylog_interval: 90
  querylog_size_memory: 0
  protection_enabled: true
  blocking_mode: null_ip
  blocked_response_ttl: 0
  ratelimit: 100
  ratelimit_whitelist: []
  refuse_any: false
  bootstrap_dns:
  - 1.1.1.1:53
  all_servers: false
  allowed_clients: []
  disallowed_clients: []
  blocked_hosts: []
  parental_block_host: family-block.dns.adguard.com
  safebrowsing_block_host: standard-block.dns.adguard.com
  cache_size: 0
  upstream_dns:
  - https://1.1.1.1/dns-query
  filtering_enabled: true
  filters_update_interval: 168
  parental_sensitivity: 13
  parental_enabled: true
  safesearch_enabled: false
  safebrowsing_enabled: false
  safebrowsing_cache_size: 1048576
  safesearch_cache_size: 1048576
  parental_cache_size: 1048576
  cache_time: 30
  rewrites: []
  blocked_services: []
tls:
  enabled: false
  server_name: www.example.com
  force_https: false
  port_https: 443
  port_dns_over_tls: 853
  allow_unencrypted_doh: true
  certificate_chain: ""
  private_key: ""
  certificate_path: ""
  private_key_path: ""
filters:
- enabled: true
  url: https://adguardteam.github.io/AdGuardSDNSFilter/Filters/filter.txt
  name: AdGuard Simplified Domain Names filter
  id: 1
- enabled: false
  url: https://hosts-file.net/ad_servers.txt
  name: hpHosts - Ad and Tracking servers only
  id: 2
- enabled: false
  url: https://adaway.org/hosts.txt
  name: adaway
  id: 3
user_rules:
- ""
dhcp:
  enabled: false
  interface_name: ""
  gateway_ip: ""
  subnet_mask: ""
  range_start: ""
  range_end: ""
  lease_duration: 86400
  icmp_timeout_msec: 1000
clients: []
log_file: ""
verbose: false
schema_version: 5
`

// . Create a configuration file
// . Start AGH instance
// . Check Web server
// . Check DNS server
// . Check DNS server with DOH
// . Wait until the filters are downloaded
// . Stop and cleanup
func TestHome(t *testing.T) {
	// Init new context
	Context = homeContext{}

	dir := prepareTestDir()
	defer func() { _ = os.RemoveAll(dir) }()
	fn := filepath.Join(dir, "AdGuardHome.yaml")

	// Prepare the test config
	assert.True(t, ioutil.WriteFile(fn, []byte(yamlConf), 0o644) == nil)
	fn, _ = filepath.Abs(fn)

	config = configuration{} // the global variable is dirty because of the previous tests run
	args := options{}
	args.configFilename = fn
	args.workDir = dir
	go run(args)

	var err error
	var resp *http.Response
	h := http.Client{}
	for i := 0; i != 50; i++ {
		resp, err = h.Get("http://127.0.0.1:3000/")
		if err == nil && resp.StatusCode != 404 {
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
	assert.Truef(t, err == nil, "%s", err)
	assert.Equal(t, http.StatusOK, resp.StatusCode)

	resp, err = h.Get("http://127.0.0.1:3000/control/status")
	assert.Truef(t, err == nil, "%s", err)
	assert.Equal(t, http.StatusOK, resp.StatusCode)

	// test DNS over UDP
	r, err := upstream.NewResolver("127.0.0.1:5354", 3*time.Second)
	assert.Nil(t, err)
	addrs, err := r.LookupIPAddr(context.TODO(), "static.adguard.com")
	assert.Nil(t, err)
	haveIP := len(addrs) != 0
	assert.True(t, haveIP)

	// test DNS over HTTP without encryption
	req := dns.Msg{}
	req.Id = dns.Id()
	req.RecursionDesired = true
	req.Question = []dns.Question{{Name: "static.adguard.com.", Qtype: dns.TypeA, Qclass: dns.ClassINET}}
	buf, err := req.Pack()
	assert.True(t, err == nil, "%s", err)
	requestURL := "http://127.0.0.1:3000/dns-query?dns=" + base64.RawURLEncoding.EncodeToString(buf)
	resp, err = http.DefaultClient.Get(requestURL)
	assert.True(t, err == nil, "%s", err)
	body, err := ioutil.ReadAll(resp.Body)
	assert.True(t, err == nil, "%s", err)
	assert.True(t, resp.StatusCode == http.StatusOK)
	response := dns.Msg{}
	err = response.Unpack(body)
	assert.True(t, err == nil, "%s", err)
	addrs = nil
	proxyutil.AppendIPAddrs(&addrs, response.Answer)
	haveIP = len(addrs) != 0
	assert.True(t, haveIP)

	for i := 1; ; i++ {
		st, err := os.Stat(filepath.Join(dir, "data", "filters", "1.txt"))
		if err == nil && st.Size() != 0 {
			break
		}
		if i == 5 {
			assert.True(t, false)
			break
		}
		time.Sleep(1 * time.Second)
	}

	cleanup()
	cleanupAlways()
}