package handlers

import (
	"bytes"
	"encoding/json"
	"testing"
	"time"

	airrescuechecklist "wucher/internal/domain/air_rescue_checklist"
	"wucher/internal/shared/pkg/uuidv7"
	responsedto "wucher/internal/transport/http/dto/response"
)

func TestBuildAirRescuerChecklistGroups_DefaultTabsIncludeCATAndDetail(t *testing.T) {
	baseID := uuidv7.MustBytes()
	checklistTA := uuidv7.MustBytes()
	checklistMO := uuidv7.MustBytes()
	itemTA := uuidv7.MustBytes()
	now := time.Now().UTC()

	bases := []airrescuechecklist.HEMSBaseView{
		{ID: baseID, BaseName: "Gallus 1 - Zurs Lech", BaseAbbreviation: "G1"},
	}
	headers := []airrescuechecklist.ChecklistHeaderView{
		{ID: checklistTA, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeTA, Title: "Cockpit Flugretter", Position: 1, CreatedAt: now, UpdatedAt: now},
		{ID: checklistMO, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeMO, Title: "Monday Checklist", Position: 2, CreatedAt: now, UpdatedAt: now},
	}
	items := []airrescuechecklist.ChecklistItemView{
		{ID: itemTA, ChecklistID: checklistTA, Name: "Kartenmaterial", Position: 1, CreatedAt: now, UpdatedAt: now},
	}

	groups := buildAirRescuerChecklistGroups(bases, headers, items, "")
	if len(groups) != 1 {
		t.Fatalf("expected 1 base group, got %d", len(groups))
	}

	tabs := groups[0].Attributes.Tabs
	if len(tabs) != 1+len(airrescuechecklist.AirRescuerChecklistPersistedScopeOrder()) {
		t.Fatalf("unexpected tabs count: %d", len(tabs))
	}

	cat := mustFindChecklistTab(t, tabs, airrescuechecklist.AirRescuerChecklistScopeCAT)
	if cat.ScopeCodeName != "Category" || cat.ScopeCodeNameDe != "Category" {
		t.Fatalf("unexpected CAT scope names: en=%q de=%q", cat.ScopeCodeName, cat.ScopeCodeNameDe)
	}
	if len(cat.Groups) != 2 {
		t.Fatalf("expected CAT to include only non-empty scopes, got %d groups", len(cat.Groups))
	}
	for i := range cat.Groups {
		if len(cat.Groups[i].Checklists) == 0 {
			t.Fatalf("CAT group must have checklists")
		}
	}
	if cat.Groups[0].ScopeCodeDe == airrescuechecklist.AirRescuerChecklistScopeTA &&
		(cat.Groups[0].ScopeCodeName != "Daily" || cat.Groups[0].ScopeCodeNameDe != "Taeglich") {
		t.Fatalf("unexpected TA group scope names: en=%q de=%q", cat.Groups[0].ScopeCodeName, cat.Groups[0].ScopeCodeNameDe)
	}

	ta := mustFindChecklistTab(t, tabs, airrescuechecklist.AirRescuerChecklistScopeTA)
	if ta.ScopeCodeName != "Daily" || ta.ScopeCodeNameDe != "Taeglich" {
		t.Fatalf("unexpected TA scope names: en=%q de=%q", ta.ScopeCodeName, ta.ScopeCodeNameDe)
	}
	if ta.Checklists == nil || len(*ta.Checklists) != 1 {
		t.Fatalf("expected TA detail to have 1 checklist")
	}
	if len((*ta.Checklists)[0].ChecklistItems) != 1 {
		t.Fatalf("expected TA detail checklist to include items")
	}

	di := mustFindChecklistTab(t, tabs, airrescuechecklist.AirRescuerChecklistScopeDI)
	if di.Checklists == nil || len(*di.Checklists) != 0 {
		t.Fatalf("expected empty scope detail tab to return checklists: []")
	}

	raw, err := json.Marshal(groups)
	if err != nil {
		t.Fatalf("marshal groups: %v", err)
	}
	if !jsonContainsTabWithEmptyChecklists(raw, airrescuechecklist.AirRescuerChecklistScopeDI) {
		t.Fatalf("expected DI tab serialized with checklists: []")
	}
}

func TestBuildAirRescuerChecklistGroups_CATOnlyRequest(t *testing.T) {
	baseID := uuidv7.MustBytes()
	checklistTA := uuidv7.MustBytes()
	now := time.Now().UTC()

	bases := []airrescuechecklist.HEMSBaseView{
		{ID: baseID, BaseName: "Gallus 1 - Zurs Lech", BaseAbbreviation: "G1"},
	}
	headers := []airrescuechecklist.ChecklistHeaderView{
		{ID: checklistTA, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeTA, Title: "Cockpit Flugretter", Position: 1, CreatedAt: now, UpdatedAt: now},
	}

	groups := buildAirRescuerChecklistGroups(bases, headers, nil, airrescuechecklist.AirRescuerChecklistScopeCAT)
	if len(groups) != 1 {
		t.Fatalf("expected 1 base group, got %d", len(groups))
	}
	if len(groups[0].Attributes.Tabs) != 1 {
		t.Fatalf("expected CAT query to return only CAT tab")
	}
	cat := groups[0].Attributes.Tabs[0]
	if cat.ScopeCodeDe != airrescuechecklist.AirRescuerChecklistScopeCAT {
		t.Fatalf("expected CAT tab")
	}
	if cat.ScopeCodeName != "Category" || cat.ScopeCodeNameDe != "Category" {
		t.Fatalf("unexpected CAT scope names: en=%q de=%q", cat.ScopeCodeName, cat.ScopeCodeNameDe)
	}
	if len(cat.Groups) != 1 || cat.Groups[0].ScopeCodeDe != airrescuechecklist.AirRescuerChecklistScopeTA {
		t.Fatalf("expected CAT to include only non-empty TA group")
	}
	if cat.Groups[0].ScopeCodeName != "Daily" || cat.Groups[0].ScopeCodeNameDe != "Taeglich" {
		t.Fatalf("unexpected TA group scope names: en=%q de=%q", cat.Groups[0].ScopeCodeName, cat.Groups[0].ScopeCodeNameDe)
	}
	if cat.Checklists != nil {
		t.Fatalf("expected CAT tab to omit detail checklists")
	}
}

func TestBuildAirRescuerChecklistGroups_RequestedPersistedScopeCanBeEmpty(t *testing.T) {
	baseID := uuidv7.MustBytes()
	bases := []airrescuechecklist.HEMSBaseView{
		{ID: baseID, BaseName: "Gallus 1 - Zurs Lech", BaseAbbreviation: "G1"},
	}

	groups := buildAirRescuerChecklistGroups(bases, nil, nil, airrescuechecklist.AirRescuerChecklistScopeM10)
	if len(groups) != 1 {
		t.Fatalf("expected 1 base group, got %d", len(groups))
	}
	if len(groups[0].Attributes.Tabs) != 1 {
		t.Fatalf("expected one requested tab")
	}
	tab := groups[0].Attributes.Tabs[0]
	if tab.ScopeCodeDe != airrescuechecklist.AirRescuerChecklistScopeM10 {
		t.Fatalf("expected M10 tab, got %s", tab.ScopeCodeDe)
	}
	if tab.ScopeCodeName != "M10" || tab.ScopeCodeNameDe != "M10" {
		t.Fatalf("unexpected M10 scope names: en=%q de=%q", tab.ScopeCodeName, tab.ScopeCodeNameDe)
	}
	if tab.Checklists == nil || len(*tab.Checklists) != 0 {
		t.Fatalf("expected requested empty persisted tab to return checklists: []")
	}
}

func TestBuildAirRescuerChecklistGroups_CATAndDetailOrderingWithDuplicatePosition(t *testing.T) {
	baseID := uuidv7.MustBytes()
	taChecklistAlpha := uuidv7.MustBytes()
	taChecklistBravo := uuidv7.MustBytes()
	taChecklistCharlie := uuidv7.MustBytes()
	moChecklist := uuidv7.MustBytes()
	now := time.Now().UTC()

	bases := []airrescuechecklist.HEMSBaseView{
		{ID: baseID, BaseName: "Gallus 1 - Zurs Lech", BaseAbbreviation: "G1"},
	}

	// Header order here follows repository contract: position ASC, title ASC.
	headers := []airrescuechecklist.ChecklistHeaderView{
		{ID: taChecklistAlpha, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeTA, Title: "Alpha", Position: 1, CreatedAt: now, UpdatedAt: now},
		{ID: taChecklistBravo, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeTA, Title: "Bravo", Position: 1, CreatedAt: now, UpdatedAt: now},
		{ID: moChecklist, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeMO, Title: "Lagerbestand", Position: 1, CreatedAt: now, UpdatedAt: now},
		{ID: taChecklistCharlie, HEMSBaseID: baseID, ScopeCode: airrescuechecklist.AirRescuerChecklistScopeTA, Title: "Charlie", Position: 2, CreatedAt: now, UpdatedAt: now},
	}

	// Item order here follows repository contract: position ASC, name ASC.
	items := []airrescuechecklist.ChecklistItemView{
		{ID: uuidv7.MustBytes(), ChecklistID: taChecklistAlpha, Name: "Oxygen", Position: 1, CreatedAt: now, UpdatedAt: now},
		{ID: uuidv7.MustBytes(), ChecklistID: taChecklistAlpha, Name: "Battery", Position: 2, CreatedAt: now, UpdatedAt: now},
		{ID: uuidv7.MustBytes(), ChecklistID: taChecklistAlpha, Name: "Radio", Position: 2, CreatedAt: now, UpdatedAt: now},
	}

	groups := buildAirRescuerChecklistGroups(bases, headers, items, "")
	if len(groups) != 1 {
		t.Fatalf("expected 1 base group, got %d", len(groups))
	}
	tabs := groups[0].Attributes.Tabs

	cat := mustFindChecklistTab(t, tabs, airrescuechecklist.AirRescuerChecklistScopeCAT)
	if cat.Checklists != nil {
		t.Fatalf("expected CAT tab to omit detail checklists")
	}
	if len(cat.Groups) != 2 {
		t.Fatalf("expected CAT to include only non-empty TA and MO groups, got %d groups", len(cat.Groups))
	}
	if cat.Groups[0].ScopeCodeDe != airrescuechecklist.AirRescuerChecklistScopeTA || cat.Groups[1].ScopeCodeDe != airrescuechecklist.AirRescuerChecklistScopeMO {
		t.Fatalf("unexpected CAT group order: %+v", cat.Groups)
	}
	if len(cat.Groups[0].Checklists) != 3 {
		t.Fatalf("expected CAT TA group to include 3 checklist summaries")
	}
	if cat.Groups[0].Checklists[0].Title != "Alpha" || cat.Groups[0].Checklists[1].Title != "Bravo" || cat.Groups[0].Checklists[2].Title != "Charlie" {
		t.Fatalf("unexpected CAT TA checklist order: %+v", cat.Groups[0].Checklists)
	}

	catJSON, err := json.Marshal(cat)
	if err != nil {
		t.Fatalf("marshal CAT tab: %v", err)
	}
	if jsonContainsChecklistItemsField(catJSON) {
		t.Fatalf("expected CAT summary payload without checklist_items")
	}

	ta := mustFindChecklistTab(t, tabs, airrescuechecklist.AirRescuerChecklistScopeTA)
	if ta.Checklists == nil || len(*ta.Checklists) != 3 {
		t.Fatalf("expected TA detail to include 3 checklists")
	}
	if (*ta.Checklists)[0].Title != "Alpha" || (*ta.Checklists)[1].Title != "Bravo" || (*ta.Checklists)[2].Title != "Charlie" {
		t.Fatalf("unexpected TA detail checklist order: %+v", *ta.Checklists)
	}
	if len((*ta.Checklists)[0].ChecklistItems) != 3 {
		t.Fatalf("expected Alpha checklist to include 3 items")
	}
	if (*ta.Checklists)[0].ChecklistItems[0].Name != "Oxygen" || (*ta.Checklists)[0].ChecklistItems[1].Name != "Battery" || (*ta.Checklists)[0].ChecklistItems[2].Name != "Radio" {
		t.Fatalf("unexpected TA checklist item order: %+v", (*ta.Checklists)[0].ChecklistItems)
	}
}

func mustFindChecklistTab(t *testing.T, tabs []responsedto.AirRescuerChecklistTabResource, scopeCode string) responsedto.AirRescuerChecklistTabResource {
	t.Helper()
	for i := range tabs {
		if tabs[i].ScopeCodeDe == scopeCode {
			return tabs[i]
		}
	}
	t.Fatalf("scope tab %s not found", scopeCode)
	return responsedto.AirRescuerChecklistTabResource{}
}

func jsonContainsTabWithEmptyChecklists(raw []byte, scopeCode string) bool {
	var payload []map[string]any
	if err := json.Unmarshal(raw, &payload); err != nil {
		return false
	}
	for _, group := range payload {
		attrsRaw, ok := group["attributes"].(map[string]any)
		if !ok {
			continue
		}
		tabsRaw, ok := attrsRaw["tabs"].([]any)
		if !ok {
			continue
		}
		for _, tab := range tabsRaw {
			tabMap, ok := tab.(map[string]any)
			if !ok {
				continue
			}
			if tabMap["scope_code_de"] != scopeCode {
				continue
			}
			checklists, ok := tabMap["checklists"].([]any)
			return ok && len(checklists) == 0
		}
	}
	return false
}

func jsonContainsChecklistItemsField(raw []byte) bool {
	return bytes.Contains(raw, []byte(`"checklist_items"`))
}

func TestAirRescuerChecklistMapperResourceAndItemData(t *testing.T) {
	now := time.Date(2026, 3, 25, 8, 0, 0, 0, time.UTC)
	baseID := []byte("1234567890123456")
	checklistID := []byte("abcdefghijklmnop")
	itemID := []byte("itemitemitemitem")
	creatorID := uuidv7.MustBytes()

	resource := airRescuerChecklistResource(airrescuechecklist.ChecklistHeaderView{
		ID:               checklistID,
		HEMSBaseID:       baseID,
		BaseName:         "  Gallus 1 - Zurs Lech  ",
		BaseAbbreviation: " G1 ",
		BaseCategory:     " regular ",
		ScopeCode:        " TA ",
		Title:            " Daily Tasks ",
		Position:         2,
		CreatedBy:        creatorID,
		CreatedAt:        now,
		UpdatedAt:        now,
	}, []airrescuechecklist.ChecklistItemView{{
		ID:          itemID,
		ChecklistID: checklistID,
		Name:        "  Battery  ",
		Position:    1,
		CreatedBy:   creatorID,
		CreatedAt:   now,
		UpdatedAt:   now,
	}})

	if resource.Type != "air_rescuer_checklist" {
		t.Fatalf("unexpected type: %s", resource.Type)
	}
	if resource.Attributes.Bases.BaseName != "Gallus 1 - Zurs Lech" {
		t.Fatalf("expected trimmed base name, got %q", resource.Attributes.Bases.BaseName)
	}
	if resource.Attributes.Bases.BaseCategory != "regular" {
		t.Fatalf("expected base_category regular, got %q", resource.Attributes.Bases.BaseCategory)
	}
	if resource.Attributes.ScopeCode != "DA" || resource.Attributes.ScopeCodeDe != "TA" || resource.Attributes.Title != "Daily Tasks" {
		t.Fatalf("expected mapped scope/title, got scope=%q scope_de=%q title=%q", resource.Attributes.ScopeCode, resource.Attributes.ScopeCodeDe, resource.Attributes.Title)
	}
	if len(resource.Attributes.Items) != 1 || resource.Attributes.Items[0].Name != "Battery" {
		t.Fatalf("expected mapped and trimmed items")
	}
	if resource.Attributes.CreatedAt != "2026-03-25T08:00:00Z" {
		t.Fatalf("unexpected created_at: %s", resource.Attributes.CreatedAt)
	}
	if resource.Attributes.CreatedBy == "" || resource.Attributes.Items[0].CreatedBy == "" {
		t.Fatalf("expected created_by on checklist resource and item")
	}

	itemData := airRescuerChecklistItemResponseData(airrescuechecklist.ChecklistItemView{
		ID:          itemID,
		ChecklistID: checklistID,
		Name:        "  Radio  ",
		Position:    3,
		CreatedBy:   creatorID,
		CreatedAt:   now,
		UpdatedAt:   now,
	})
	if itemData.Type != "air_rescuer_checklist_item" {
		t.Fatalf("unexpected item type: %s", itemData.Type)
	}
	if itemData.Attributes.Name != "Radio" {
		t.Fatalf("expected trimmed item name, got %q", itemData.Attributes.Name)
	}
	if itemData.Attributes.Position != 3 {
		t.Fatalf("expected position=3, got %d", itemData.Attributes.Position)
	}
	if itemData.Attributes.CreatedBy == "" {
		t.Fatalf("expected created_by on item response")
	}
}

func TestPaginateAirRescuerChecklistGroups(t *testing.T) {
	groups := []responsedto.AirRescuerChecklistBaseGroupResource{
		{ID: "base-1"},
		{ID: "base-2"},
		{ID: "base-3"},
	}

	got, total := paginateAirRescuerChecklistGroups(groups, 10, 2)
	if total != 3 {
		t.Fatalf("expected total=3, got %d", total)
	}
	if len(got) != 0 {
		t.Fatalf("expected empty page when start exceeds total")
	}

	got, total = paginateAirRescuerChecklistGroups(groups, 1, 5)
	if total != 3 {
		t.Fatalf("expected total=3, got %d", total)
	}
	if len(got) != 2 || got[0].ID != "base-2" || got[1].ID != "base-3" {
		t.Fatalf("unexpected paged groups: %+v", got)
	}
}
