// SPDX-FileCopyrightText: Amolith <amolith@secluded.site>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

package lunatask_test

import (
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"

	lunatask "git.secluded.site/go-lunatask"
)

const personID = "5999b945-b2b1-48c6-aa72-b251b75b3c2e"

// --- ListPeople ---

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

	server := newJSONServer(t, "/people", peopleResponseBody)
	defer server.Close()

	client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))

	people, err := client.ListPeople(ctx(), nil)
	if err != nil {
		t.Fatalf("error = %v", err)
	}

	if len(people) != 2 {
		t.Fatalf("len = %d, want 2", len(people))
	}

	person := people[0]
	if person.ID != personID {
		t.Errorf("ID = %q, want %q", person.ID, personID)
	}

	if person.RelationshipStrength == nil || *person.RelationshipStrength != lunatask.RelationshipBusiness {
		t.Errorf("RelationshipStrength = %v, want %v", person.RelationshipStrength, lunatask.RelationshipBusiness)
	}

	wantCreated := time.Date(2021, 1, 10, 10, 39, 25, 0, time.UTC)
	if !person.CreatedAt.Equal(wantCreated) {
		t.Errorf("CreatedAt = %v, want %v", person.CreatedAt, wantCreated)
	}
}

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

	tests := []filterTest{
		{
			Name:      "source_only",
			Source:    ptr("salesforce"),
			SourceID:  nil,
			WantQuery: url.Values{"source": {"salesforce"}},
		},
		{
			Name:      "source_and_id",
			Source:    ptr("salesforce"),
			SourceID:  ptr("sf-123"),
			WantQuery: url.Values{"source": {"salesforce"}, "source_id": {"sf-123"}},
		},
	}

	runFilterTests(t, "/people", `{"people": []}`, tests, func(c *lunatask.Client, source, sourceID *string) error {
		opts := &lunatask.ListPeopleOptions{Source: source, SourceID: sourceID}
		_, err := c.ListPeople(ctx(), opts)

		return err //nolint:wrapcheck // test helper
	})
}

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

	testErrorCases(t, func(c *lunatask.Client) error {
		_, err := c.ListPeople(ctx(), nil)

		return err //nolint:wrapcheck // test helper
	})
}

// --- GetPerson ---

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

	server := newJSONServer(t, "/people/"+personID, singlePersonResponseBody)
	defer server.Close()

	client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))

	person, err := client.GetPerson(ctx(), personID)
	if err != nil {
		t.Fatalf("error = %v", err)
	}

	if person == nil {
		t.Fatal("returned nil")
	}

	if person.ID != personID {
		t.Errorf("ID = %q, want %q", person.ID, personID)
	}

	if len(person.Sources) != 1 || person.Sources[0].Source != "salesforce" {
		t.Errorf("Sources = %v, want salesforce source", person.Sources)
	}
}

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

	testErrorCases(t, func(c *lunatask.Client) error {
		_, err := c.GetPerson(ctx(), personID)

		return err //nolint:wrapcheck // test helper
	})
}

// --- CreatePerson ---

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

	server, capture := newPOSTServer(t, "/people", singlePersonResponseBody)
	defer server.Close()

	client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))

	person, err := client.NewPerson("John", "Doe").
		WithRelationshipStrength(lunatask.RelationshipBusiness).
		FromSource("salesforce", "sf-123").
		Create(ctx())
	if err != nil {
		t.Fatalf("error = %v", err)
	}

	if person == nil {
		t.Fatal("returned nil")
	}

	if person.ID != personID {
		t.Errorf("ID = %q, want %q", person.ID, personID)
	}

	assertBodyField(t, capture.Body, "first_name", "John")
	assertBodyField(t, capture.Body, "last_name", "Doe")
	assertBodyField(t, capture.Body, "relationship_strength", "business-contacts")
	assertBodyField(t, capture.Body, "source", "salesforce")
	assertBodyField(t, capture.Body, "source_id", "sf-123")
}

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

	server, capture := newPOSTServer(t, "/people", singlePersonResponseBody)
	defer server.Close()

	client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))

	_, err := client.NewPerson("Ada", "Lovelace").
		WithRelationshipStrength(lunatask.RelationshipCloseFriend).
		WithCustomField("email", "ada@example.com").
		WithCustomField("phone", "+1234567890").
		Create(ctx())
	if err != nil {
		t.Fatalf("error = %v", err)
	}

	assertBodyField(t, capture.Body, "first_name", "Ada")
	assertBodyField(t, capture.Body, "last_name", "Lovelace")
	assertBodyField(t, capture.Body, "relationship_strength", "close-friends")
	assertBodyField(t, capture.Body, "email", "ada@example.com")
	assertBodyField(t, capture.Body, "phone", "+1234567890")
}

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

	server := httptest.NewServer(http.HandlerFunc(func(writer http.ResponseWriter, _ *http.Request) {
		writer.WriteHeader(http.StatusNoContent)
	}))
	defer server.Close()

	client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))

	person, err := client.NewPerson("Dup", "Person").
		FromSource("salesforce", "dup-123").
		Create(ctx())
	if err != nil {
		t.Fatalf("error = %v, want nil", err)
	}

	if person != nil {
		t.Errorf("person = %v, want nil for duplicate", person)
	}
}

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

	testErrorCases(t, func(c *lunatask.Client) error {
		_, err := c.NewPerson("Test", "User").Create(ctx())

		return err //nolint:wrapcheck // test helper
	})
}

// --- UpdatePerson ---

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

	server, capture := newPUTServer(t, "/people/"+personID, singlePersonResponseBody)
	defer server.Close()

	client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))

	person, err := client.NewPersonUpdate(personID).
		FirstName("Jane").
		LastName("Smith").
		WithRelationshipStrength(lunatask.RelationshipCloseFriend).
		Update(ctx())
	if err != nil {
		t.Fatalf("error = %v", err)
	}

	if person == nil {
		t.Fatal("returned nil")
	}

	if person.ID != personID {
		t.Errorf("ID = %q, want %q", person.ID, personID)
	}

	assertBodyField(t, capture.Body, "first_name", "Jane")
	assertBodyField(t, capture.Body, "last_name", "Smith")
	assertBodyField(t, capture.Body, "relationship_strength", "close-friends")
}

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

	server, capture := newPUTServer(t, "/people/"+personID, singlePersonResponseBody)
	defer server.Close()

	client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))

	_, err := client.NewPersonUpdate(personID).
		FirstName("Ada").
		LastName("Lovelace").
		WithRelationshipStrength(lunatask.RelationshipFamily).
		WithCustomField("email", "ada@example.com").
		WithCustomField("phone", "+1234567890").
		WithCustomField("birthday", "1815-12-10").
		Update(ctx())
	if err != nil {
		t.Fatalf("error = %v", err)
	}

	assertBodyField(t, capture.Body, "first_name", "Ada")
	assertBodyField(t, capture.Body, "last_name", "Lovelace")
	assertBodyField(t, capture.Body, "relationship_strength", "family")
	assertBodyField(t, capture.Body, "email", "ada@example.com")
	assertBodyField(t, capture.Body, "phone", "+1234567890")
	assertBodyField(t, capture.Body, "birthday", "1815-12-10")
}

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

	testErrorCases(t, func(c *lunatask.Client) error {
		_, err := c.NewPersonUpdate(personID).FirstName("x").Update(ctx())

		return err //nolint:wrapcheck // test helper
	})
}

// --- DeletePerson ---

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

	server := newDELETEServer(t, "/people/"+personID, singlePersonResponseBody)
	defer server.Close()

	client := lunatask.NewClient(testToken, lunatask.BaseURL(server.URL))

	person, err := client.DeletePerson(ctx(), personID)
	if err != nil {
		t.Fatalf("error = %v", err)
	}

	if person == nil {
		t.Fatal("returned nil")
	}

	if person.ID != personID {
		t.Errorf("ID = %q, want %q", person.ID, personID)
	}
}

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

	testErrorCases(t, func(c *lunatask.Client) error {
		_, err := c.DeletePerson(ctx(), personID)

		return err //nolint:wrapcheck // test helper
	})
}

// --- Test Data ---

const singlePersonResponseBody = `{
	"person": {
		"id": "5999b945-b2b1-48c6-aa72-b251b75b3c2e",
		"relationship_strength": "business-contacts",
		"sources": [{"source": "salesforce", "source_id": "352fd2d7-cdc0-4e91-a0a3-9d6cc9d440e7"}],
		"created_at": "2021-01-10T10:39:25Z",
		"updated_at": "2021-01-10T10:39:25Z"
	}
}`

const peopleResponseBody = `{
	"people": [
		{
			"id": "5999b945-b2b1-48c6-aa72-b251b75b3c2e",
			"relationship_strength": "business-contacts",
			"sources": [{"source": "salesforce", "source_id": "352fd2d7-cdc0-4e91-a0a3-9d6cc9d440e7"}],
			"created_at": "2021-01-10T10:39:25Z",
			"updated_at": "2021-01-10T10:39:25Z"
		},
		{
			"id": "109cbf01-dba9-4136-8cf1-a02084ba3977",
			"relationship_strength": "family",
			"sources": [],
			"created_at": "2021-01-10T10:39:25Z",
			"updated_at": "2021-01-10T10:39:25Z"
		}
	]
}`
