package server_test

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"testing"

	"github.com/keys-pub/keys"
	"github.com/stretchr/testify/require"
)

func TestUserSearch(t *testing.T) {
	env := newEnv(t)
	// env.logLevel = server.DebugLevel
	srv := newTestServer(t, env)

	// GET /user/search
	req, err := http.NewRequest("GET", "/user/search", nil)
	require.NoError(t, err)
	code, _, body := srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	require.Equal(t, `{"users":[]}`, body)

	// Alice
	alice := keys.NewEdX25519KeyFromSeed(keys.Bytes32(bytes.Repeat([]byte{0x01}, 32)))

	// Alice sign user statement
	st := userMock(t, alice, "alice", "github", env.client, env.clock)
	// PUT alice
	b, err := st.Bytes()
	require.NoError(t, err)
	req, err = http.NewRequest("PUT", fmt.Sprintf("/sigchain/%s/1", alice.ID()), bytes.NewReader(b))
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	require.Equal(t, "{}", body)

	// GET /user/search
	req, err = http.NewRequest("GET", "/user/search", nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	expected := `{
  "users": [
    {
      "id": "alice@github",
      "name": "alice",
      "kid": "kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077",
      "seq": 1,
      "service": "github",
      "url": "https://gist.github.com/alice/6769746875622f61",
      "status": "ok",
      "statement": "BEGIN MESSAGE.\nY24cziOYlfrf5Jo StDTUuPnS6LKuhx awYPRXyhwwlRHUA c0pXakmOZjgjMzs8DCAifCEdAkZfEU VDk6hs3Z2OGTCKq 6Xr2MZHgg4UNRDb Zy2loGoGN3Mvxd4r7FIwpZOJPE1JEq D2gGjkgLByR9CFG 2aCgRgZZwl5UAa4 6bmBzjEOhmsiW0KTDXulMpC51JXgyc 1MliDDv03o9DXy5 mbXLLP0PDrc9ziK lQqXFL3j737xB4VyAwvIctTRYqHeOH X5y51pUcCb2s5VI uU0x96I37FiTbk8 BW9SG90C6ST.\nEND MESSAGE.",
      "verifiedAt": 1234567890004,
      "ts": 1234567890004
    }
  ]
}`
	require.Equal(t, expected, pretty(t, body))

	// GET /user/search?q=alice
	req, err = http.NewRequest("GET", "/user/search?q=alice", nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	expected = `{
  "users": [
    {
      "id": "alice@github",
      "name": "alice",
      "kid": "kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077",
      "seq": 1,
      "service": "github",
      "url": "https://gist.github.com/alice/6769746875622f61",
      "status": "ok",
      "statement": "BEGIN MESSAGE.\nY24cziOYlfrf5Jo StDTUuPnS6LKuhx awYPRXyhwwlRHUA c0pXakmOZjgjMzs8DCAifCEdAkZfEU VDk6hs3Z2OGTCKq 6Xr2MZHgg4UNRDb Zy2loGoGN3Mvxd4r7FIwpZOJPE1JEq D2gGjkgLByR9CFG 2aCgRgZZwl5UAa4 6bmBzjEOhmsiW0KTDXulMpC51JXgyc 1MliDDv03o9DXy5 mbXLLP0PDrc9ziK lQqXFL3j737xB4VyAwvIctTRYqHeOH X5y51pUcCb2s5VI uU0x96I37FiTbk8 BW9SG90C6ST.\nEND MESSAGE.",
      "verifiedAt": 1234567890004,
      "ts": 1234567890004
    }
  ]
}`
	require.Equal(t, expected, pretty(t, body))

	// GET /user/search?q=kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077
	req, err = http.NewRequest("GET", "/user/search?q=kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077", nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	expected = `{
  "users": [
    {
      "id": "alice@github",
      "name": "alice",
      "kid": "kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077",
      "seq": 1,
      "service": "github",
      "url": "https://gist.github.com/alice/6769746875622f61",
      "status": "ok",
      "statement": "BEGIN MESSAGE.\nY24cziOYlfrf5Jo StDTUuPnS6LKuhx awYPRXyhwwlRHUA c0pXakmOZjgjMzs8DCAifCEdAkZfEU VDk6hs3Z2OGTCKq 6Xr2MZHgg4UNRDb Zy2loGoGN3Mvxd4r7FIwpZOJPE1JEq D2gGjkgLByR9CFG 2aCgRgZZwl5UAa4 6bmBzjEOhmsiW0KTDXulMpC51JXgyc 1MliDDv03o9DXy5 mbXLLP0PDrc9ziK lQqXFL3j737xB4VyAwvIctTRYqHeOH X5y51pUcCb2s5VI uU0x96I37FiTbk8 BW9SG90C6ST.\nEND MESSAGE.",
      "verifiedAt": 1234567890004,
      "ts": 1234567890004,
      "mf": "kid"
    }
  ]
}`
	require.Equal(t, expected, pretty(t, body))

	// GET /user/search?q=kbx1rvd43h2sag2tvrdp0duse5p82nvhpjd6hpjwhv7q7vqklega8atshec5ws
	req, err = http.NewRequest("GET", "/user/search?q=kbx1rvd43h2sag2tvrdp0duse5p82nvhpjd6hpjwhv7q7vqklega8atshec5ws", nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	expected = `{
  "users": [
    {
      "id": "alice@github",
      "name": "alice",
      "kid": "kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077",
      "seq": 1,
      "service": "github",
      "url": "https://gist.github.com/alice/6769746875622f61",
      "status": "ok",
      "statement": "BEGIN MESSAGE.\nY24cziOYlfrf5Jo StDTUuPnS6LKuhx awYPRXyhwwlRHUA c0pXakmOZjgjMzs8DCAifCEdAkZfEU VDk6hs3Z2OGTCKq 6Xr2MZHgg4UNRDb Zy2loGoGN3Mvxd4r7FIwpZOJPE1JEq D2gGjkgLByR9CFG 2aCgRgZZwl5UAa4 6bmBzjEOhmsiW0KTDXulMpC51JXgyc 1MliDDv03o9DXy5 mbXLLP0PDrc9ziK lQqXFL3j737xB4VyAwvIctTRYqHeOH X5y51pUcCb2s5VI uU0x96I37FiTbk8 BW9SG90C6ST.\nEND MESSAGE.",
      "verifiedAt": 1234567890004,
      "ts": 1234567890004,
      "mf": "kid"
    }
  ]
}`
	require.Equal(t, expected, pretty(t, body))

	// GET /user/search?q=unknown
	req, err = http.NewRequest("GET", "/user/search?q=unknown", nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	require.Equal(t, `{"users":[]}`, body)
}

func pretty(t *testing.T, s string) string {
	var pretty bytes.Buffer
	err := json.Indent(&pretty, []byte(s), "", "  ")
	require.NoError(t, err)
	return pretty.String()
}

func TestUserGet(t *testing.T) {
	env := newEnv(t)
	// env.logLevel = server.DebugLevel
	srv := newTestServer(t, env)

	// Alice
	alice := keys.NewEdX25519KeyFromSeed(keys.Bytes32(bytes.Repeat([]byte{0x01}, 32)))

	// Alice sign user statement
	st := userMock(t, alice, "alice", "github", env.client, env.clock)
	// PUT alice
	b, err := st.Bytes()
	require.NoError(t, err)
	req, err := http.NewRequest("PUT", fmt.Sprintf("/sigchain/%s/1", alice.ID()), bytes.NewReader(b))
	require.NoError(t, err)
	code, _, body := srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	require.Equal(t, "{}", body)

	// GET /user/kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077 (alice)
	req, err = http.NewRequest("GET", "/user/kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077", nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	expected := `{
  "user": {
    "id": "alice@github",
    "name": "alice",
    "kid": "kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077",
    "seq": 1,
    "service": "github",
    "url": "https://gist.github.com/alice/6769746875622f61",
    "status": "ok",
    "statement": "BEGIN MESSAGE.\nY24cziOYlfrf5Jo StDTUuPnS6LKuhx awYPRXyhwwlRHUA c0pXakmOZjgjMzs8DCAifCEdAkZfEU VDk6hs3Z2OGTCKq 6Xr2MZHgg4UNRDb Zy2loGoGN3Mvxd4r7FIwpZOJPE1JEq D2gGjkgLByR9CFG 2aCgRgZZwl5UAa4 6bmBzjEOhmsiW0KTDXulMpC51JXgyc 1MliDDv03o9DXy5 mbXLLP0PDrc9ziK lQqXFL3j737xB4VyAwvIctTRYqHeOH X5y51pUcCb2s5VI uU0x96I37FiTbk8 BW9SG90C6ST.\nEND MESSAGE.",
    "verifiedAt": 1234567890004,
    "ts": 1234567890004
  }
}`
	require.Equal(t, expected, pretty(t, body))

	// GET /user/:kid (not found)
	key := keys.GenerateEdX25519Key()
	req, err = http.NewRequest("GET", "/user/"+key.ID().String(), nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusNotFound, code)
	require.Equal(t, `{"error":{"code":404,"message":"user not found"}}`, body)

	// GET /user/search?q=alice@github
	req, err = http.NewRequest("GET", "/user/search?q=alice@github", nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	expected = `{
  "users": [
    {
      "id": "alice@github",
      "name": "alice",
      "kid": "kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077",
      "seq": 1,
      "service": "github",
      "url": "https://gist.github.com/alice/6769746875622f61",
      "status": "ok",
      "statement": "BEGIN MESSAGE.\nY24cziOYlfrf5Jo StDTUuPnS6LKuhx awYPRXyhwwlRHUA c0pXakmOZjgjMzs8DCAifCEdAkZfEU VDk6hs3Z2OGTCKq 6Xr2MZHgg4UNRDb Zy2loGoGN3Mvxd4r7FIwpZOJPE1JEq D2gGjkgLByR9CFG 2aCgRgZZwl5UAa4 6bmBzjEOhmsiW0KTDXulMpC51JXgyc 1MliDDv03o9DXy5 mbXLLP0PDrc9ziK lQqXFL3j737xB4VyAwvIctTRYqHeOH X5y51pUcCb2s5VI uU0x96I37FiTbk8 BW9SG90C6ST.\nEND MESSAGE.",
      "verifiedAt": 1234567890004,
      "ts": 1234567890004
    }
  ]
}`
	require.Equal(t, expected, pretty(t, body))

	// GET /user/:user (bob@github)
	req, err = http.NewRequest("GET", "/user/bob@github", nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusNotFound, code)
	require.Equal(t, `{"error":{"code":404,"message":"user not found"}}`, body)

	// GET /user/kbx1rvd43h2sag2tvrdp0duse5p82nvhpjd6hpjwhv7q7vqklega8atshec5ws
	req, err = http.NewRequest("GET", "/user/kbx1rvd43h2sag2tvrdp0duse5p82nvhpjd6hpjwhv7q7vqklega8atshec5ws", nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	expected = `{
  "user": {
    "id": "alice@github",
    "name": "alice",
    "kid": "kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077",
    "seq": 1,
    "service": "github",
    "url": "https://gist.github.com/alice/6769746875622f61",
    "status": "ok",
    "statement": "BEGIN MESSAGE.\nY24cziOYlfrf5Jo StDTUuPnS6LKuhx awYPRXyhwwlRHUA c0pXakmOZjgjMzs8DCAifCEdAkZfEU VDk6hs3Z2OGTCKq 6Xr2MZHgg4UNRDb Zy2loGoGN3Mvxd4r7FIwpZOJPE1JEq D2gGjkgLByR9CFG 2aCgRgZZwl5UAa4 6bmBzjEOhmsiW0KTDXulMpC51JXgyc 1MliDDv03o9DXy5 mbXLLP0PDrc9ziK lQqXFL3j737xB4VyAwvIctTRYqHeOH X5y51pUcCb2s5VI uU0x96I37FiTbk8 BW9SG90C6ST.\nEND MESSAGE.",
    "verifiedAt": 1234567890004,
    "ts": 1234567890004
  }
}`
	require.Equal(t, expected, pretty(t, body))

	// GET /user/kbx1rvd43h2sag2tvrdp0duse5p82nvhpjd6hpjwhv7q7vqklega8atshec5ws
	req, err = http.NewRequest("GET", "/user/kbx1rvd43h2sag2tvrdp0duse5p82nvhpjd6hpjwhv7q7vqklega8atshec5ws", nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	expected = `{
  "user": {
    "id": "alice@github",
    "name": "alice",
    "kid": "kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077",
    "seq": 1,
    "service": "github",
    "url": "https://gist.github.com/alice/6769746875622f61",
    "status": "ok",
    "statement": "BEGIN MESSAGE.\nY24cziOYlfrf5Jo StDTUuPnS6LKuhx awYPRXyhwwlRHUA c0pXakmOZjgjMzs8DCAifCEdAkZfEU VDk6hs3Z2OGTCKq 6Xr2MZHgg4UNRDb Zy2loGoGN3Mvxd4r7FIwpZOJPE1JEq D2gGjkgLByR9CFG 2aCgRgZZwl5UAa4 6bmBzjEOhmsiW0KTDXulMpC51JXgyc 1MliDDv03o9DXy5 mbXLLP0PDrc9ziK lQqXFL3j737xB4VyAwvIctTRYqHeOH X5y51pUcCb2s5VI uU0x96I37FiTbk8 BW9SG90C6ST.\nEND MESSAGE.",
    "verifiedAt": 1234567890004,
    "ts": 1234567890004
  }
}`
	require.Equal(t, expected, pretty(t, body))
}

func TestUserDuplicate(t *testing.T) {
	// user.SetLogger(user.NewLogger(user.DebugLevel))

	env := newEnv(t)
	// env.logLevel = server.DebugLevel
	srv := newTestServer(t, env)

	// Alice
	alice := keys.NewEdX25519KeyFromSeed(keys.Bytes32(bytes.Repeat([]byte{0x01}, 32)))
	alice2 := keys.NewEdX25519KeyFromSeed(keys.Bytes32(bytes.Repeat([]byte{0x08}, 32)))

	// PUT /sigchain/alice/1
	st := userMock(t, alice, "alice", "github", env.client, env.clock)
	b, err := st.Bytes()
	require.NoError(t, err)
	req, err := http.NewRequest("PUT", fmt.Sprintf("/sigchain/%s/1", alice.ID()), bytes.NewReader(b))
	require.NoError(t, err)
	code, _, body := srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	require.Equal(t, "{}", body)

	// GET /user/search
	req, err = http.NewRequest("GET", "/user/search", nil)
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusOK, code)
	expected := `{
  "users": [
    {
      "id": "alice@github",
      "name": "alice",
      "kid": "kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077",
      "seq": 1,
      "service": "github",
      "url": "https://gist.github.com/alice/6769746875622f61",
      "status": "ok",
      "statement": "BEGIN MESSAGE.\nY24cziOYlfrf5Jo StDTUuPnS6LKuhx awYPRXyhwwlRHUA c0pXakmOZjgjMzs8DCAifCEdAkZfEU VDk6hs3Z2OGTCKq 6Xr2MZHgg4UNRDb Zy2loGoGN3Mvxd4r7FIwpZOJPE1JEq D2gGjkgLByR9CFG 2aCgRgZZwl5UAa4 6bmBzjEOhmsiW0KTDXulMpC51JXgyc 1MliDDv03o9DXy5 mbXLLP0PDrc9ziK lQqXFL3j737xB4VyAwvIctTRYqHeOH X5y51pUcCb2s5VI uU0x96I37FiTbk8 BW9SG90C6ST.\nEND MESSAGE.",
      "verifiedAt": 1234567890004,
      "ts": 1234567890004
    }
  ]
}`
	require.Equal(t, expected, pretty(t, body))

	// PUT /sigchain/alice2/1
	st2 := userMock(t, alice2, "alice", "github", env.client, env.clock)
	b2, err := st2.Bytes()
	require.NoError(t, err)
	req, err = http.NewRequest("PUT", fmt.Sprintf("/sigchain/%s/1", alice2.ID()), bytes.NewReader(b2))
	require.NoError(t, err)
	code, _, body = srv.Serve(req)
	require.Equal(t, http.StatusConflict, code)
	require.Equal(t, `{"error":{"code":409,"message":"user already exists with key kex132yw8ht5p8cetl2jmvknewjawt9xwzdlrk2pyxlnwjyqrdq0dawqqph077, if you removed or revoked the previous statement you may need to wait briefly for search to update"}}`, body)
}
