// Copyright (c) Tailscale Inc & contributors
// SPDX-License-Identifier: BSD-3-Clause

//go:build go1.19

package local

import (
	"context"
	"net"
	"net/http"
	"testing"

	"tailscale.com/tstest/deptest"
	"tailscale.com/tstest/nettest"
	"tailscale.com/types/key"
)

func TestGetServeConfigFromJSON(t *testing.T) {
	sc, err := getServeConfigFromJSON([]byte("null"))
	if sc != nil {
		t.Errorf("want nil for null")
	}
	if err != nil {
		t.Errorf("reading null: %v", err)
	}

	sc, err = getServeConfigFromJSON([]byte(`{"TCP":{}}`))
	if err != nil {
		t.Errorf("reading object: %v", err)
	} else if sc == nil {
		t.Errorf("want non-nil for object")
	} else if sc.TCP == nil {
		t.Errorf("want non-nil TCP for object")
	}
}

func TestWhoIsPeerNotFound(t *testing.T) {
	nw := nettest.GetNetwork(t)
	ts := nettest.NewHTTPServer(nw, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(404)
	}))
	defer ts.Close()

	lc := &Client{
		Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
			return nw.Dial(ctx, network, ts.Listener.Addr().String())
		},
	}
	var k key.NodePublic
	if err := k.UnmarshalText([]byte("nodekey:5c8f86d5fc70d924e55f02446165a5dae8f822994ad26bcf4b08fd841f9bf261")); err != nil {
		t.Fatal(err)
	}
	res, err := lc.WhoIsNodeKey(context.Background(), k)
	if err != ErrPeerNotFound {
		t.Errorf("got (%v, %v), want ErrPeerNotFound", res, err)
	}
	res, err = lc.WhoIs(context.Background(), "1.2.3.4:5678")
	if err != ErrPeerNotFound {
		t.Errorf("got (%v, %v), want ErrPeerNotFound", res, err)
	}
}

func TestUserDialSelf(t *testing.T) {
	// Start a real TCP listener that the client should dial directly
	// when the server tells it to dial-self.
	ln, err := net.Listen("tcp", "127.0.0.1:0")
	if err != nil {
		t.Fatal(err)
	}
	defer ln.Close()
	go func() {
		for {
			c, err := ln.Accept()
			if err != nil {
				return
			}
			c.Write([]byte("hello"))
			c.Close()
		}
	}()
	targetAddr := ln.Addr().(*net.TCPAddr)

	// Mock LocalAPI server that returns Dial-Self response.
	nw := nettest.GetNetwork(t)
	ts := nettest.NewHTTPServer(nw, http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Dial-Self", "true")
		w.Header().Set("Dial-Addr", targetAddr.String())
		w.WriteHeader(http.StatusOK)
	}))
	defer ts.Close()

	lc := &Client{
		Dial: func(ctx context.Context, network, addr string) (net.Conn, error) {
			return nw.Dial(ctx, network, ts.Listener.Addr().String())
		},
	}

	conn, err := lc.UserDial(context.Background(), "tcp", targetAddr.IP.String(), uint16(targetAddr.Port))
	if err != nil {
		t.Fatalf("UserDial: %v", err)
	}
	defer conn.Close()

	buf := make([]byte, 5)
	n, err := conn.Read(buf)
	if err != nil {
		t.Fatalf("Read: %v", err)
	}
	if got := string(buf[:n]); got != "hello" {
		t.Errorf("got %q, want %q", got, "hello")
	}
}

func TestDeps(t *testing.T) {
	deptest.DepChecker{
		BadDeps: map[string]string{
			// Make sure we don't again accidentally bring in a dependency on
			// drive or its transitive dependencies
			"testing":                        "do not use testing package in production code",
			"tailscale.com/drive/driveimpl":  "https://github.com/tailscale/tailscale/pull/10631",
			"github.com/studio-b12/gowebdav": "https://github.com/tailscale/tailscale/pull/10631",
		},
	}.Check(t)
}
