package services

import (
	"context"
	"strings"
	"testing"
)

// TestGetActiveEmbeddedFallback covers the no-DB path. Without a sqlx.DB,
// every key/lang pair returns the embedded default with IsDefault=true.
func TestGetActiveEmbeddedFallback(t *testing.T) {
	svc := NewEmailTemplateService(nil)
	for _, key := range CanonicalEmailTemplateKeys {
		for _, lang := range EmailTemplateLanguages {
			row, err := svc.GetActive(context.Background(), key, lang)
			if err != nil {
				t.Errorf("GetActive(%s, %s): %v", key, lang, err)
				continue
			}
			if !row.IsDefault {
				t.Errorf("GetActive(%s, %s): IsDefault=false on no-DB service", key, lang)
			}
			if row.Body == "" {
				t.Errorf("GetActive(%s, %s): empty body", key, lang)
			}
			if key == EmailTemplateKeyBase {
				if row.Subject != "" {
					t.Errorf("base subject expected empty, got %q", row.Subject)
				}
			} else if row.Subject == "" {
				t.Errorf("GetActive(%s, %s): empty subject for non-base key", key, lang)
			}
		}
	}
}

// TestGetActiveUnknownKey ensures unknown keys are 404-shaped.
func TestGetActiveUnknownKey(t *testing.T) {
	svc := NewEmailTemplateService(nil)
	if _, err := svc.GetActive(context.Background(), "nope", "de"); err != ErrTemplateUnknownKey {
		t.Errorf("expected ErrTemplateUnknownKey, got %v", err)
	}
}

// TestGetActiveUnknownLang ensures unknown languages are 400-shaped.
func TestGetActiveUnknownLang(t *testing.T) {
	svc := NewEmailTemplateService(nil)
	if _, err := svc.GetActive(context.Background(), EmailTemplateKeyInvitation, "fr"); err != ErrTemplateUnknownLang {
		t.Errorf("expected ErrTemplateUnknownLang, got %v", err)
	}
}

// TestSaveRequiresStore checks that mutations against a no-DB service fail
// closed — no silent acceptance, no in-memory drift.
func TestSaveRequiresStore(t *testing.T) {
	svc := NewEmailTemplateService(nil)
	_, err := svc.Save(context.Background(), SaveInput{
		Key:     EmailTemplateKeyInvitation,
		Lang:    "de",
		Subject: "x",
		Body:    `{{define "content"}}<p>x</p>{{end}}`,
	})
	if err != ErrTemplateStoreUnavailable {
		t.Errorf("expected ErrTemplateStoreUnavailable, got %v", err)
	}
}

// TestValidateTemplate checks every code path of the validation function.
// Save and the preview endpoint both lean on this — a drift here breaks both.
func TestValidateTemplate(t *testing.T) {
	cases := []struct {
		name        string
		key, subj   string
		body        string
		wantErr     error
		wantSubsErr string // substring required in the wrapped error
	}{
		{
			"invitation valid",
			EmailTemplateKeyInvitation,
			"[Paliad] {{.InviterName}} invites you",
			`{{define "content"}}<h1>{{.InviterName}}</h1>{{end}}`,
			nil, "",
		},
		{
			"invitation missing content block",
			EmailTemplateKeyInvitation,
			"x",
			`<p>oops, no define</p>`,
			ErrTemplateMissingContent, "",
		},
		{
			"invitation bad body syntax",
			EmailTemplateKeyInvitation,
			"x",
			`{{define "content"}}<p>{{.InviterName}{{end}}`,
			nil, "syntax",
		},
		{
			"invitation bad subject syntax",
			EmailTemplateKeyInvitation,
			`[Paliad] {{.InviterName`,
			`{{define "content"}}<p>x</p>{{end}}`,
			nil, "syntax",
		},
		{
			"base valid",
			EmailTemplateKeyBase,
			"",
			`<html><body>{{block "content" .}}{{end}}</body></html>`,
			nil, "",
		},
		{
			"base missing block call",
			EmailTemplateKeyBase,
			"",
			`<html><body>no inner</body></html>`,
			ErrTemplateMissingBaseBlock, "",
		},
	}
	for _, tc := range cases {
		t.Run(tc.name, func(t *testing.T) {
			err := ValidateTemplate(tc.key, tc.subj, tc.body)
			if tc.wantErr != nil {
				if err != tc.wantErr {
					t.Errorf("got %v, want %v", err, tc.wantErr)
				}
				return
			}
			if tc.wantSubsErr != "" {
				if err == nil || !strings.Contains(err.Error(), tc.wantSubsErr) {
					t.Errorf("got %v, want substring %q", err, tc.wantSubsErr)
				}
				return
			}
			if err != nil {
				t.Errorf("expected no error, got %v", err)
			}
		})
	}
}

// TestEmailTemplateVariablesShape ensures every canonical key has a non-empty
// variable contract — the editor sidebar would render an empty box otherwise.
func TestEmailTemplateVariablesShape(t *testing.T) {
	for _, key := range CanonicalEmailTemplateKeys {
		vars := EmailTemplateVariables(key)
		if len(vars) == 0 {
			t.Errorf("key %s: no variables registered", key)
		}
		for _, v := range vars {
			if v.Name == "" || v.Type == "" || v.Description == "" {
				t.Errorf("key %s: variable %+v has empty field", key, v)
			}
		}
	}
}
