package dto_test

import (
	"testing"
	"time"

	sprintdom "github.com/Paca-AI/api/internal/domain/sprint"
	"github.com/Paca-AI/api/internal/transport/http/dto"
	"github.com/google/uuid"
)

// ---------------------------------------------------------------------------
// ViewFromEntity roundtrip
// ---------------------------------------------------------------------------

// TestViewFromEntity_FiltersRoundtrip verifies that all ViewFilters fields
// using FilterConfig are correctly mapped to ViewResponse.
func TestViewFromEntity_FiltersRoundtrip(t *testing.T) {
	sprintID := uuid.New()
	projectID := uuid.New()
	viewID := uuid.New()
	statusID := uuid.NewString()
	assigneeID := uuid.NewString()

	entity := &sprintdom.SprintView{
		ID:        viewID,
		SprintID:  &sprintID,
		ProjectID: projectID,
		Name:      "Board",
		ViewType:  sprintdom.ViewTypeBoard,
		Position:  1,
		Config: sprintdom.ViewConfig{
			ColumnBy: "status",
			Filters: &sprintdom.ViewFilters{
				TaskTypes: &sprintdom.FilterConfig{
					All: false,
					Items: map[string]sprintdom.FilterEntry{
						"normal": sprintdom.FilterEntryNested(sprintdom.FilterConfig{All: true}),
					},
				},
				Sprints: &sprintdom.FilterConfig{
					All: false,
					Items: map[string]sprintdom.FilterEntry{
						sprintID.String(): sprintdom.FilterEntryInclude(),
						uuid.NewString():  sprintdom.FilterEntryInclude(),
					},
				},
				Statuses: &sprintdom.FilterConfig{
					All: false,
					Items: map[string]sprintdom.FilterEntry{
						statusID: sprintdom.FilterEntryInclude(),
					},
				},
				Assignees: &sprintdom.FilterConfig{
					All: false,
					Items: map[string]sprintdom.FilterEntry{
						assigneeID: sprintdom.FilterEntryInclude(),
					},
				},
			},
		},
		CreatedAt: time.Now(),
		UpdatedAt: time.Now(),
	}

	resp := dto.ViewFromEntity(entity)

	if resp.Config.Filters == nil {
		t.Fatal("expected non-nil Filters in response")
	}
	f := resp.Config.Filters

	if f.TaskTypes == nil {
		t.Fatal("expected non-nil TaskTypes in response filters")
	}
	normalEntry, ok := f.TaskTypes.Items["normal"]
	if !ok {
		t.Fatal("expected 'normal' key in TaskTypes.Items")
	}
	if !normalEntry.IsNested() {
		t.Error("expected 'normal' entry to be a nested FilterConfig")
	}
	if normalEntry.Config() == nil || !normalEntry.Config().All {
		t.Error("expected 'normal' nested config to have All=true")
	}

	if f.Sprints == nil {
		t.Fatal("expected non-nil Sprints in response filters")
	}
	if len(f.Sprints.Items) != 2 {
		t.Errorf("Sprints.Items: want 2 entries, got %d", len(f.Sprints.Items))
	}
	if _, ok := f.Sprints.Items[sprintID.String()]; !ok {
		t.Errorf("Sprints.Items missing original sprintID %s", sprintID)
	}

	if f.Statuses == nil || len(f.Statuses.Items) != 1 {
		t.Error("Statuses.Items: want 1 entry")
	}
	if f.Assignees == nil || len(f.Assignees.Items) != 1 {
		t.Error("Assignees.Items: want 1 entry")
	}
}

// TestViewFromEntity_NilFilters verifies that a nil Filters field maps to nil
// in the DTO (no panic).
func TestViewFromEntity_NilFilters(t *testing.T) {
	entity := &sprintdom.SprintView{
		ID:        uuid.New(),
		ProjectID: uuid.New(),
		Name:      "Table",
		ViewType:  sprintdom.ViewTypeTable,
		Config:    sprintdom.ViewConfig{},
	}
	resp := dto.ViewFromEntity(entity)
	if resp.Config.Filters != nil {
		t.Errorf("expected nil Filters for entity with nil filters, got %+v", resp.Config.Filters)
	}
}

// TestViewFromEntity_PluginConfigRoundtrip verifies plugin fields are copied
// from domain config to DTO config.
func TestViewFromEntity_PluginConfigRoundtrip(t *testing.T) {
	entity := &sprintdom.SprintView{
		ID:        uuid.New(),
		ProjectID: uuid.New(),
		Name:      "Plugin View",
		ViewType:  sprintdom.ViewTypePlugin,
		Config: sprintdom.ViewConfig{
			PluginID:        uuid.NewString(),
			PluginComponent: "KanbanByPriority",
		},
	}

	resp := dto.ViewFromEntity(entity)

	if resp.Config.PluginManifestID != entity.Config.PluginID {
		t.Errorf("PluginManifestID: want %q, got %q", entity.Config.PluginID, resp.Config.PluginManifestID)
	}
	if resp.Config.PluginComponent != entity.Config.PluginComponent {
		t.Errorf("PluginComponent: want %q, got %q", entity.Config.PluginComponent, resp.Config.PluginComponent)
	}
}

// ---------------------------------------------------------------------------
// toViewConfig (via CreateViewRequest / UpdateViewRequest) roundtrip
// ---------------------------------------------------------------------------

// TestCreateViewRequest_ToCreateInput_FiltersRoundtrip exercises toViewConfig
// indirectly through the CreateViewRequest helper.
func TestCreateViewRequest_ToCreateInput_FiltersRoundtrip(t *testing.T) {
	sprintID := uuid.New()
	projectID := uuid.New()
	systemTypeID := uuid.NewString()

	req := dto.CreateViewRequest{
		Name:     "Board",
		ViewType: sprintdom.ViewTypeBoard,
		Config: &dto.ViewConfigDTO{
			ColumnBy: "status",
			Filters: &dto.ViewFiltersDTO{
				TaskTypes: &sprintdom.FilterConfig{
					All: false,
					Items: map[string]sprintdom.FilterEntry{
						"normal":     sprintdom.FilterEntryNested(sprintdom.FilterConfig{All: true}),
						systemTypeID: sprintdom.FilterEntryInclude(),
					},
				},
				Sprints: &sprintdom.FilterConfig{
					All: false,
					Items: map[string]sprintdom.FilterEntry{
						sprintID.String(): sprintdom.FilterEntryInclude(),
					},
				},
			},
		},
	}

	input := req.ToCreateInput(sprintID, projectID)

	f := input.Config.Filters
	if f == nil {
		t.Fatal("expected non-nil Filters in domain input")
	}
	if f.TaskTypes == nil {
		t.Fatal("expected non-nil TaskTypes in domain input filters")
	}
	if len(f.TaskTypes.Items) != 2 {
		t.Errorf("TaskTypes.Items: want 2, got %d", len(f.TaskTypes.Items))
	}
	normalEntry, ok := f.TaskTypes.Items["normal"]
	if !ok || !normalEntry.IsNested() {
		t.Error("expected 'normal' entry to be a nested FilterConfig")
	}
	sysEntry, ok := f.TaskTypes.Items[systemTypeID]
	if !ok || sysEntry.IsNested() || !sysEntry.Flag() {
		t.Errorf("expected system type entry to be a plain include flag, got nested=%v flag=%v", sysEntry.IsNested(), sysEntry.Flag())
	}
	if f.Sprints == nil || len(f.Sprints.Items) != 1 {
		t.Errorf("Sprints.Items: want 1, got %v", f.Sprints)
	}
	sprintEntry, ok := f.Sprints.Items[sprintID.String()]
	if !ok || !sprintEntry.Flag() {
		t.Errorf("expected sprint entry to be included, got %+v", sprintEntry)
	}
}

// TestCreateViewRequest_ToCreateInput_PluginConfigRoundtrip verifies plugin
// fields are copied from DTO config to domain config through toViewConfig.
func TestCreateViewRequest_ToCreateInput_PluginConfigRoundtrip(t *testing.T) {
	sprintID := uuid.New()
	projectID := uuid.New()
	pluginID := uuid.NewString()

	req := dto.CreateViewRequest{
		Name:     "Plugin View",
		ViewType: sprintdom.ViewTypePlugin,
		Config: &dto.ViewConfigDTO{
			PluginManifestID: pluginID,
			PluginComponent:  "KanbanByPriority",
		},
	}

	input := req.ToCreateInput(sprintID, projectID)

	if input.Config.PluginID != pluginID {
		t.Errorf("PluginID: want %q, got %q", pluginID, input.Config.PluginID)
	}
	if input.Config.PluginComponent != "KanbanByPriority" {
		t.Errorf("PluginComponent: want %q, got %q", "KanbanByPriority", input.Config.PluginComponent)
	}
}

// TestViewFiltersDTO_NilTaskTypesPreserved verifies that a nil TaskTypes does
// not get set to a non-nil value after a roundtrip through entity → DTO.
func TestViewFiltersDTO_NilTaskTypesPreserved(t *testing.T) {
	entity := &sprintdom.SprintView{
		ID:        uuid.New(),
		ProjectID: uuid.New(),
		Name:      "Table",
		ViewType:  sprintdom.ViewTypeTable,
		Config: sprintdom.ViewConfig{
			Filters: &sprintdom.ViewFilters{
				Statuses: &sprintdom.FilterConfig{
					All: false,
					Items: map[string]sprintdom.FilterEntry{
						uuid.NewString(): sprintdom.FilterEntryInclude(),
					},
				},
				// TaskTypes intentionally absent.
			},
		},
	}

	resp := dto.ViewFromEntity(entity)
	if resp.Config.Filters == nil {
		t.Fatal("expected non-nil Filters")
	}
	if resp.Config.Filters.TaskTypes != nil {
		t.Errorf("expected nil TaskTypes, got %+v", resp.Config.Filters.TaskTypes)
	}
}
