package xlstranslator

import (
	"encoding/xml"
	"strings"
	"testing"

	"github.com/xuri/excelize/v2"
)

func TestTemplateSpecs_UniqueNamesAndFilenames(t *testing.T) {
	names := make(map[string]struct{})
	files := make(map[string]struct{})
	for _, spec := range TemplateSpecs {
		if _, ok := names[spec.Name]; ok {
			t.Fatalf("duplicate template name: %s", spec.Name)
		}
		names[spec.Name] = struct{}{}

		if _, ok := files[spec.Filename]; ok {
			t.Fatalf("duplicate template filename: %s", spec.Filename)
		}
		files[spec.Filename] = struct{}{}

		if spec.Kind == "xlsx" && len(spec.Sheets) == 0 {
			t.Fatalf("xlsx template %s has no sheets", spec.Name)
		}
		if spec.Kind == "xml" && strings.TrimSpace(spec.XMLSkeleton) == "" {
			t.Fatalf("xml template %s has no skeleton", spec.Name)
		}
	}
}

func TestGenerateTemplate_XLSX(t *testing.T) {
	for _, spec := range TemplateSpecs {
		if spec.Kind != "xlsx" {
			continue
		}
		t.Run(spec.Name, func(t *testing.T) {
			b, mime, err := GenerateTemplate(spec.Name)
			if err != nil {
				t.Fatalf("generate template: %v", err)
			}
			if mime != "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" {
				t.Fatalf("unexpected mime: %s", mime)
			}

			f, err := excelize.OpenReader(strings.NewReader(string(b)))
			if err != nil {
				t.Fatalf("open generated xlsx: %v", err)
			}
			defer f.Close()

			sheets := f.GetSheetList()
			if len(sheets) != len(spec.Sheets) {
				t.Fatalf("expected %d sheets, got %d: %v", len(spec.Sheets), len(sheets), sheets)
			}

			for i, st := range spec.Sheets {
				if sheets[i] != st.Name {
					t.Fatalf("sheet %d: expected %q, got %q", i, st.Name, sheets[i])
				}

				rowNum := st.HeaderRowIndex + 1
				rows, err := f.GetRows(st.Name)
				if err != nil {
					t.Fatalf("get rows %q: %v", st.Name, err)
				}
				if len(rows) < rowNum {
					t.Fatalf("sheet %q: expected at least %d rows, got %d", st.Name, rowNum, len(rows))
				}

				row := rows[st.HeaderRowIndex]
				if len(row) < len(st.Headers) {
					t.Fatalf("sheet %q header row has %d cols, expected at least %d", st.Name, len(row), len(st.Headers))
				}
				for j, want := range st.Headers {
					if row[j] != want {
						t.Fatalf("sheet %q col %d: expected %q, got %q", st.Name, j, want, row[j])
					}
				}
			}
		})
	}
}

func TestGenerateTemplate_Brembo(t *testing.T) {
	b, mime, err := GenerateTemplate("brembo")
	if err != nil {
		t.Fatalf("generate template: %v", err)
	}
	if mime != "application/xml" {
		t.Fatalf("unexpected mime: %s", mime)
	}

	s := string(b)
	if !strings.Contains(s, "<ACES") {
		t.Error("missing <ACES> root")
	}
	if !strings.Contains(s, "<App>") {
		t.Error("missing <App> element")
	}
	if !strings.Contains(s, "<PartNumber>P06033</PartNumber>") {
		t.Error("missing allowlisted example part number")
	}

	var parsed struct {
		Apps []struct {
			PartNumber string `xml:"PartNumber"`
		} `xml:"App"`
	}
	if err := xml.Unmarshal(b, &parsed); err != nil {
		t.Fatalf("unmarshal xml: %v", err)
	}
	if len(parsed.Apps) == 0 {
		t.Fatal("no App elements parsed")
	}
}

func TestGetTemplateSpec_Unknown(t *testing.T) {
	_, ok := GetTemplateSpec("does-not-exist")
	if ok {
		t.Fatal("expected unknown template to return false")
	}
}

func TestConverterTypes_MatchConverterFileNames(t *testing.T) {
	types := ConverterTypes()
	if len(types) != len(converterFileNames) {
		t.Fatalf("expected %d converter types, got %d", len(converterFileNames), len(types))
	}

	typeNames := make(map[string]struct{}, len(types))
	for _, ct := range types {
		typeNames[ct.Name] = struct{}{}
	}

	for name := range converterFileNames {
		if _, ok := typeNames[name]; !ok {
			t.Errorf("converter type %q is missing from ConverterTypes", name)
		}
	}
}

func TestTemplateSpecs_MatchConverterFileNames(t *testing.T) {
	specNames := make(map[string]struct{}, len(TemplateSpecs))
	for _, spec := range TemplateSpecs {
		specNames[spec.Name] = struct{}{}
	}

	for name := range converterFileNames {
		if _, ok := specNames[name]; !ok {
			t.Errorf("converter %q exists in converterFileNames but has no TemplateSpec (Plantillas would be missing it)", name)
		}
	}

	for _, spec := range TemplateSpecs {
		if _, ok := converterFileNames[spec.Name]; !ok {
			t.Errorf("TemplateSpec %q exists but there is no converter in converterFileNames (Convertir would accept it but nothing runs it)", spec.Name)
		}
	}
}
