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

package lunatask_test

import (
	"errors"
	"testing"

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

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

	validUUID := "12345678-1234-1234-1234-123456789012"
	validUUID2 := "abcdef12-3456-7890-abcd-ef1234567890"

	tests := []struct {
		name         string
		input        string
		wantResource lunatask.Resource
		wantUUID     string
		wantErr      error
	}{
		// Valid deep links
		{"task_link", "lunatask://tasks/" + validUUID, lunatask.ResourceTask, validUUID, nil},
		{"area_link", "lunatask://areas/" + validUUID, lunatask.ResourceArea, validUUID, nil},
		{"goal_link", "lunatask://goals/" + validUUID, lunatask.ResourceGoal, validUUID, nil},
		{"note_link", "lunatask://notes/" + validUUID, lunatask.ResourceNote, validUUID, nil},
		{"person_link", "lunatask://people/" + validUUID, lunatask.ResourcePerson, validUUID, nil},
		{"notebook_link", "lunatask://notebooks/" + validUUID, lunatask.ResourceNotebook, validUUID, nil},

		// Plain UUIDs return ResourceUnknown
		{"plain_uuid", validUUID2, lunatask.ResourceUnknown, validUUID2, nil},
		{"uuid_only", validUUID, lunatask.ResourceUnknown, validUUID, nil},

		// Invalid inputs
		{"empty", "", "", "", lunatask.ErrInvalidReference},
		{"invalid_resource", "lunatask://invalid/" + validUUID, "", "", lunatask.ErrInvalidResource},
		{"missing_uuid", "lunatask://tasks/", "", "", lunatask.ErrInvalidDeepLink},
		{"missing_resource", "lunatask:///" + validUUID, "", "", lunatask.ErrInvalidDeepLink},
		{"malformed", "lunatask://tasks", "", "", lunatask.ErrInvalidDeepLink},
		{"invalid_uuid_in_link", "lunatask://tasks/not-a-uuid", "", "", lunatask.ErrInvalidUUID},
		{"invalid_plain_uuid", "not-a-valid-uuid", "", "", lunatask.ErrInvalidReference},
	}

	for _, testCase := range tests {
		t.Run(testCase.name, func(t *testing.T) {
			t.Parallel()

			resource, uuid, err := lunatask.ParseReference(testCase.input)

			if testCase.wantErr != nil {
				if !errors.Is(err, testCase.wantErr) {
					t.Errorf("ParseReference(%q) error = %v, want %v", testCase.input, err, testCase.wantErr)
				}

				return
			}

			if err != nil {
				t.Errorf("ParseReference(%q) unexpected error = %v", testCase.input, err)

				return
			}

			if resource != testCase.wantResource {
				t.Errorf("ParseReference(%q) resource = %q, want %q", testCase.input, resource, testCase.wantResource)
			}

			if uuid != testCase.wantUUID {
				t.Errorf("ParseReference(%q) uuid = %q, want %q", testCase.input, uuid, testCase.wantUUID)
			}
		})
	}
}

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

	validUUID := "12345678-1234-1234-1234-123456789012"

	resource, uuid, err := lunatask.ParseDeepLink("lunatask://tasks/" + validUUID)
	if err != nil {
		t.Errorf("ParseDeepLink() unexpected error = %v", err)
	}

	if resource != lunatask.ResourceTask {
		t.Errorf("ParseDeepLink() resource = %q, want %q", resource, lunatask.ResourceTask)
	}

	if uuid != validUUID {
		t.Errorf("ParseDeepLink() uuid = %q, want %q", uuid, validUUID)
	}
}

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

	validUUID := "12345678-1234-1234-1234-123456789012"

	tests := []struct {
		name     string
		resource lunatask.Resource
		id       string
		want     string
		wantErr  error
	}{
		// Valid builds
		{"task", lunatask.ResourceTask, validUUID, "lunatask://tasks/" + validUUID, nil},
		{"area", lunatask.ResourceArea, validUUID, "lunatask://areas/" + validUUID, nil},
		{"goal", lunatask.ResourceGoal, validUUID, "lunatask://goals/" + validUUID, nil},
		{"note", lunatask.ResourceNote, validUUID, "lunatask://notes/" + validUUID, nil},
		{"person", lunatask.ResourcePerson, validUUID, "lunatask://people/" + validUUID, nil},
		{"notebook", lunatask.ResourceNotebook, validUUID, "lunatask://notebooks/" + validUUID, nil},

		// Invalid inputs
		{"empty_id", lunatask.ResourceTask, "", "", lunatask.ErrInvalidUUID},
		{"invalid_resource", lunatask.Resource("invalid"), validUUID, "", lunatask.ErrInvalidResource},
		{"invalid_uuid", lunatask.ResourceTask, "not-a-uuid", "", lunatask.ErrInvalidUUID},
	}

	for _, testCase := range tests {
		t.Run(testCase.name, func(t *testing.T) {
			t.Parallel()

			got, err := lunatask.BuildDeepLink(testCase.resource, testCase.id)

			if testCase.wantErr != nil {
				if !errors.Is(err, testCase.wantErr) {
					t.Errorf("BuildDeepLink(%q, %q) error = %v, want %v", testCase.resource, testCase.id, err, testCase.wantErr)
				}

				return
			}

			if err != nil {
				t.Errorf("BuildDeepLink(%q, %q) unexpected error = %v", testCase.resource, testCase.id, err)

				return
			}

			if got != testCase.want {
				t.Errorf("BuildDeepLink(%q, %q) = %q, want %q", testCase.resource, testCase.id, got, testCase.want)
			}
		})
	}
}

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

	tests := []struct {
		name    string
		input   string
		wantErr bool
	}{
		{"valid_uuid", "12345678-1234-1234-1234-123456789012", false},
		{"valid_uuid_lowercase", "abcdef12-3456-7890-abcd-ef1234567890", false},
		{"valid_uuid_uppercase", "ABCDEF12-3456-7890-ABCD-EF1234567890", false},
		{"valid_uuid_mixed", "AbCdEf12-3456-7890-AbCd-Ef1234567890", false},
		{"empty", "", true},
		{"invalid_format", "not-a-uuid", true},
		{"too_short", "12345678-1234-1234-1234", true},
		{"too_long", "12345678-1234-1234-1234-1234567890123", true},
		{"missing_hyphens", "12345678123412341234123456789012", false}, // google/uuid accepts this
	}

	for _, testCase := range tests {
		t.Run(testCase.name, func(t *testing.T) {
			t.Parallel()

			err := lunatask.ValidateUUID(testCase.input)
			if (err != nil) != testCase.wantErr {
				t.Errorf("ValidateUUID(%q) error = %v, wantErr %v", testCase.input, err, testCase.wantErr)
			}

			if testCase.wantErr && err != nil && !errors.Is(err, lunatask.ErrInvalidUUID) {
				t.Errorf("ValidateUUID(%q) error = %v, want wrapped ErrInvalidUUID", testCase.input, err)
			}
		})
	}
}

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

	tests := []struct {
		name     string
		resource lunatask.Resource
		want     bool
	}{
		{"area", lunatask.ResourceArea, true},
		{"goal", lunatask.ResourceGoal, true},
		{"task", lunatask.ResourceTask, true},
		{"note", lunatask.ResourceNote, true},
		{"person", lunatask.ResourcePerson, true},
		{"notebook", lunatask.ResourceNotebook, true},
		{"invalid", lunatask.Resource("invalid"), false},
		{"empty", lunatask.Resource(""), false},
	}

	for _, testCase := range tests {
		t.Run(testCase.name, func(t *testing.T) {
			t.Parallel()

			if got := testCase.resource.Valid(); got != testCase.want {
				t.Errorf("Resource(%q).Valid() = %v, want %v", testCase.resource, got, testCase.want)
			}
		})
	}
}

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

	tests := []struct {
		name     string
		resource lunatask.Resource
		want     string
	}{
		{"area", lunatask.ResourceArea, "areas"},
		{"goal", lunatask.ResourceGoal, "goals"},
		{"task", lunatask.ResourceTask, "tasks"},
		{"note", lunatask.ResourceNote, "notes"},
		{"person", lunatask.ResourcePerson, "people"},
		{"notebook", lunatask.ResourceNotebook, "notebooks"},
	}

	for _, testCase := range tests {
		t.Run(testCase.name, func(t *testing.T) {
			t.Parallel()

			if got := testCase.resource.String(); got != testCase.want {
				t.Errorf("Resource.String() = %q, want %q", got, testCase.want)
			}
		})
	}
}
