package service

import (
	"context"
	"errors"
	"testing"

	"wucher/internal/domain/health_insurance_companies"
)

type healthInsuranceCompaniesRepoMock struct {
	createCalled  bool
	updateCalled  bool
	deleteCalled  bool
	getByIDCalled bool
	listCalled    bool

	lastCreate     *health_insurance_companies.HealthInsuranceCompany
	lastUpdate     *health_insurance_companies.HealthInsuranceCompany
	lastDeleteID   []byte
	lastDeleteBy   []byte
	lastGetByID    []byte
	lastListFilter string
	lastListSort   string
	lastListLimit  int
	lastListOffset int

	getByIDResult *health_insurance_companies.HealthInsuranceCompany
	listResult    []health_insurance_companies.HealthInsuranceCompany
	listTotal     int64

	createErr  error
	updateErr  error
	deleteErr  error
	getByIDErr error
	listErr    error
	resolveErr error

	resolveResult     map[string]health_insurance_companies.StateDetail
	resolveLandResult map[string]health_insurance_companies.LandDetail
}

func (m *healthInsuranceCompaniesRepoMock) Create(_ context.Context, o *health_insurance_companies.HealthInsuranceCompany) error {
	m.createCalled = true
	m.lastCreate = o
	return m.createErr
}

func (m *healthInsuranceCompaniesRepoMock) Update(_ context.Context, o *health_insurance_companies.HealthInsuranceCompany) error {
	m.updateCalled = true
	m.lastUpdate = o
	return m.updateErr
}

func (m *healthInsuranceCompaniesRepoMock) Delete(_ context.Context, id []byte, deletedBy []byte) error {
	m.deleteCalled = true
	m.lastDeleteID = id
	m.lastDeleteBy = deletedBy
	return m.deleteErr
}

func (m *healthInsuranceCompaniesRepoMock) GetByID(_ context.Context, id []byte) (*health_insurance_companies.HealthInsuranceCompany, error) {
	m.getByIDCalled = true
	m.lastGetByID = id
	return m.getByIDResult, m.getByIDErr
}

func (m *healthInsuranceCompaniesRepoMock) List(_ context.Context, filter, sort string, limit, offset int) ([]health_insurance_companies.HealthInsuranceCompany, int64, error) {
	m.listCalled = true
	m.lastListFilter = filter
	m.lastListSort = sort
	m.lastListLimit = limit
	m.lastListOffset = offset
	return m.listResult, m.listTotal, m.listErr
}

func (m *healthInsuranceCompaniesRepoMock) ResolveStateDetails(_ context.Context, _ []string) (map[string]health_insurance_companies.StateDetail, error) {
	return m.resolveResult, m.resolveErr
}

func (m *healthInsuranceCompaniesRepoMock) ResolveLandDetails(_ context.Context, _ []string) (map[string]health_insurance_companies.LandDetail, error) {
	return m.resolveLandResult, m.resolveErr
}

func TestNewHealthInsuranceCompaniesService(t *testing.T) {
	repo := &healthInsuranceCompaniesRepoMock{}
	svc := NewHealthInsuranceCompaniesService(repo)
	if svc == nil || svc.repo == nil {
		t.Fatalf("expected service and repo initialized")
	}
}

func TestHealthInsuranceCompaniesServiceCreate(t *testing.T) {
	repo := &healthInsuranceCompaniesRepoMock{}
	svc := NewHealthInsuranceCompaniesService(repo)
	row := &health_insurance_companies.HealthInsuranceCompany{Name: "Name"}

	if err := svc.Create(context.Background(), row); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if !repo.createCalled || repo.lastCreate != row {
		t.Fatalf("expected Create forwarded to repo")
	}

	repo.createErr = errors.New("create failed")
	if err := svc.Create(context.Background(), row); err == nil {
		t.Fatalf("expected error from repo")
	}
}

func TestHealthInsuranceCompaniesServiceUpdate(t *testing.T) {
	repo := &healthInsuranceCompaniesRepoMock{}
	svc := NewHealthInsuranceCompaniesService(repo)
	row := &health_insurance_companies.HealthInsuranceCompany{Name: "Updated"}

	if err := svc.Update(context.Background(), row); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if !repo.updateCalled || repo.lastUpdate != row {
		t.Fatalf("expected Update forwarded to repo")
	}

	repo.updateErr = errors.New("update failed")
	if err := svc.Update(context.Background(), row); err == nil {
		t.Fatalf("expected error from repo")
	}
}

func TestHealthInsuranceCompaniesServiceDelete(t *testing.T) {
	repo := &healthInsuranceCompaniesRepoMock{}
	svc := NewHealthInsuranceCompaniesService(repo)
	id := []byte("id")
	deletedBy := []byte("by")

	if err := svc.Delete(context.Background(), id, deletedBy); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if !repo.deleteCalled {
		t.Fatalf("expected Delete forwarded to repo")
	}
	if string(repo.lastDeleteID) != string(id) || string(repo.lastDeleteBy) != string(deletedBy) {
		t.Fatalf("expected delete params forwarded")
	}

	repo.deleteErr = errors.New("delete failed")
	if err := svc.Delete(context.Background(), id, deletedBy); err == nil {
		t.Fatalf("expected error from repo")
	}
}

func TestHealthInsuranceCompaniesServiceGetByID(t *testing.T) {
	expected := &health_insurance_companies.HealthInsuranceCompany{Name: "A"}
	repo := &healthInsuranceCompaniesRepoMock{getByIDResult: expected}
	svc := NewHealthInsuranceCompaniesService(repo)
	id := []byte("id")

	got, err := svc.GetByID(context.Background(), id)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if got != expected {
		t.Fatalf("unexpected result")
	}
	if !repo.getByIDCalled || string(repo.lastGetByID) != string(id) {
		t.Fatalf("expected GetByID forwarded")
	}

	repo.getByIDErr = errors.New("get failed")
	if _, err := svc.GetByID(context.Background(), id); err == nil {
		t.Fatalf("expected error from repo")
	}
}

func TestHealthInsuranceCompaniesServiceList(t *testing.T) {
	repo := &healthInsuranceCompaniesRepoMock{
		listResult: []health_insurance_companies.HealthInsuranceCompany{{Name: "A"}},
		listTotal:  1,
	}
	svc := NewHealthInsuranceCompaniesService(repo)

	rows, total, err := svc.List(context.Background(), "find", "name", 10, 20)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(rows) != 1 || total != 1 {
		t.Fatalf("unexpected list result")
	}
	if !repo.listCalled {
		t.Fatalf("expected List forwarded to repo")
	}
	if repo.lastListFilter != "find" || repo.lastListSort != "name ASC" || repo.lastListLimit != 10 || repo.lastListOffset != 20 {
		t.Fatalf("unexpected list args forwarded")
	}

	rows, total, err = svc.List(context.Background(), "find", "sortkey", 10, 20)
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if len(rows) != 1 || total != 1 {
		t.Fatalf("unexpected list result")
	}
	if repo.lastListSort != "is_active DESC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN 0 ELSE 1 END ASC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN sortkey END ASC, name ASC" {
		t.Fatalf("unexpected sortkey args forwarded: %q", repo.lastListSort)
	}

	repo.listErr = errors.New("list failed")
	if _, _, err := svc.List(context.Background(), "", "unknown", 1, 0); err == nil {
		t.Fatalf("expected error from repo")
	}
}

func TestNormalizeHealthInsuranceCompaniesSort(t *testing.T) {
	cases := map[string]string{
		"name":           "name ASC",
		"-name":          "name DESC",
		"sortkey":        "is_active DESC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN 0 ELSE 1 END ASC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN sortkey END ASC, name ASC",
		"-sortkey":       "is_active DESC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN 0 ELSE 1 END ASC, CASE WHEN is_active = 1 AND sortkey IS NOT NULL AND sortkey >= 0 THEN sortkey END DESC, name ASC",
		"state":          "state ASC",
		"-state":         "state DESC",
		"address":        "address ASC",
		"-address":       "address DESC",
		"mobile_number":  "mobile_number ASC",
		"-mobile_number": "mobile_number DESC",
		"email":          "email ASC",
		"-email":         "email DESC",
		"note":           "note ASC",
		"-note":          "note DESC",
		"is_active":      "is_active ASC",
		"-is_active":     "is_active DESC",
		"created_at":     "created_at ASC",
		"-created_at":    "created_at DESC",
		"updated_at":     "updated_at ASC",
		"-updated_at":    "updated_at DESC",
		"unknown":        "",
		"":               "",
	}

	for in, want := range cases {
		if got := normalizeHealthInsuranceCompaniesSort(in); got != want {
			t.Fatalf("normalizeHealthInsuranceCompaniesSort(%q) = %q, want %q", in, got, want)
		}
	}
}

func TestHealthInsuranceCompaniesServiceResolveStateDetails(t *testing.T) {
	want := map[string]health_insurance_companies.StateDetail{
		"berlin": {FederalStateID: "id1", FederalState: "Berlin", LandName: "Deutschland", LandISOCode: "DE"},
	}
	repo := &healthInsuranceCompaniesRepoMock{resolveResult: want}
	svc := NewHealthInsuranceCompaniesService(repo)

	got, err := svc.ResolveStateDetails(context.Background(), []string{"Berlin"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if got["berlin"].FederalStateID != "id1" {
		t.Fatalf("unexpected resolve result")
	}
}

func TestHealthInsuranceCompaniesServiceResolveLandDetails(t *testing.T) {
	want := map[string]health_insurance_companies.LandDetail{
		"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d": {LandID: "018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d", LandName: "Deutschland", LandISOCode: "DE"},
	}
	repo := &healthInsuranceCompaniesRepoMock{resolveLandResult: want}
	svc := NewHealthInsuranceCompaniesService(repo)

	got, err := svc.ResolveLandDetails(context.Background(), []string{"018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"})
	if err != nil {
		t.Fatalf("unexpected error: %v", err)
	}
	if got["018f3a5c-7a6f-7c2a-8d4a-4b5b1f6c8e9d"].LandName != "Deutschland" {
		t.Fatalf("unexpected land resolve result")
	}
}
