package contact

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

	lark "github.com/larksuite/oapi-sdk-go/v3"
	"github.com/stretchr/testify/require"
)

type httpClientFunc func(req *http.Request) (*http.Response, error)

func (f httpClientFunc) Do(req *http.Request) (*http.Response, error) {
	return f(req)
}

func TestGetUsersOfDepartmentReturnsAllPages(t *testing.T) {
	t.Parallel()

	var userRequestCount int
	rawClient := lark.NewClient("app-id", "app-secret", lark.WithHttpClient(httpClientFunc(
		func(req *http.Request) (*http.Response, error) {
			switch req.URL.Path {
			case "/open-apis/auth/v3/tenant_access_token/internal":
				return jsonResponse(`{"code":0,"expire":7200,"tenant_access_token":"token"}`), nil
			case "/open-apis/contact/v3/users/find_by_department":
				userRequestCount++
				require.Equal(t, "department-rd", req.URL.Query().Get("department_id"))
				require.Equal(t, "department_id", req.URL.Query().Get("department_id_type"))
				require.Equal(t, "user_id", req.URL.Query().Get("user_id_type"))
				require.Equal(t, "50", req.URL.Query().Get("page_size"))
				if userRequestCount == 1 {
					return jsonResponse(`{"code":0,"data":{"has_more":true,"page_token":"next","items":[{"user_id":"user-1"}]}}`), nil
				}
				require.Equal(t, "next", req.URL.Query().Get("page_token"))
				return jsonResponse(`{"code":0,"data":{"has_more":false,"items":[{"user_id":"user-2"}]}}`), nil
			default:
				t.Fatalf("unexpected request path: %s", req.URL.Path)
				return nil, nil
			}
		},
	)))

	users, err := New(rawClient).GetUsersOfDepartment(context.Background(), "department-rd")
	require.NoError(t, err)
	require.Len(t, users, 2)
	require.Equal(t, "user-1", *users[0].UserId)
	require.Equal(t, "user-2", *users[1].UserId)
	require.Equal(t, 2, userRequestCount)
}

func TestGetUsersOfDepartmentReturnsLarkError(t *testing.T) {
	t.Parallel()

	rawClient := lark.NewClient("app-id", "app-secret", lark.WithHttpClient(httpClientFunc(
		func(req *http.Request) (*http.Response, error) {
			switch req.URL.Path {
			case "/open-apis/auth/v3/tenant_access_token/internal":
				return jsonResponse(`{"code":0,"expire":7200,"tenant_access_token":"token"}`), nil
			case "/open-apis/contact/v3/users/find_by_department":
				return jsonResponse(`{"code":123,"msg":"forbidden"}`), nil
			default:
				t.Fatalf("unexpected request path: %s", req.URL.Path)
				return nil, nil
			}
		},
	)))

	_, err := New(rawClient).GetUsersOfDepartment(context.Background(), "department-rd")
	require.ErrorContains(t, err, "code: 123")
	require.ErrorContains(t, err, "message: forbidden")
}

func TestBatchGetUserOpenIDsUsesOpenIDType(t *testing.T) {
	t.Parallel()

	rawClient := lark.NewClient("app-id", "app-secret", lark.WithHttpClient(httpClientFunc(
		func(req *http.Request) (*http.Response, error) {
			switch req.URL.Path {
			case "/open-apis/auth/v3/tenant_access_token/internal":
				return jsonResponse(`{"code":0,"expire":7200,"tenant_access_token":"token"}`), nil
			case "/open-apis/contact/v3/users/batch_get_id":
				require.Equal(t, "open_id", req.URL.Query().Get("user_id_type"))
				body, err := io.ReadAll(req.Body)
				require.NoError(t, err)
				require.JSONEq(t, `{"emails":["assignee@example.com"]}`, string(body))
				return jsonResponse(`{"code":0,"data":{"user_list":[{"user_id":"ou_assignee","email":"assignee@example.com"}]}}`), nil
			default:
				t.Fatalf("unexpected request path: %s", req.URL.Path)
				return nil, nil
			}
		},
	)))

	users, err := New(rawClient).BatchGetUserOpenIDs(context.Background(), []string{"assignee@example.com"})
	require.NoError(t, err)
	require.Len(t, users, 1)
	require.Equal(t, "ou_assignee", *users[0].UserId)
}

func jsonResponse(body string) *http.Response {
	header := make(http.Header)
	header.Set("Content-Type", "application/json")
	return &http.Response{
		StatusCode: http.StatusOK,
		Header:     header,
		Body:       io.NopCloser(strings.NewReader(body)),
	}
}
