package service

import (
	"context"
	"errors"
	"testing"

	"wucher/internal/domain/complaint"
	"wucher/internal/shared/pkg/userctx"
)

type stubComplaintRepo struct {
	row             *complaint.Complaint
	open            bool
	unactioned      bool
	active          []complaint.Complaint
	createErr       error
	created         *complaint.Complaint
	markedHeli      []byte
	markedComplaint []byte
	markedFlight    []byte
}

func (s *stubComplaintRepo) Create(_ context.Context, row *complaint.Complaint) error {
	s.created = row
	return s.createErr
}
func (s *stubComplaintRepo) Update(_ context.Context, _ *complaint.Complaint) error { return nil }
func (s *stubComplaintRepo) Delete(_ context.Context, _, _ []byte) error            { return nil }
func (s *stubComplaintRepo) GetByID(_ context.Context, _ []byte) (*complaint.Complaint, error) {
	return s.row, nil
}
func (s *stubComplaintRepo) ListByHelicopter(_ context.Context, _ []byte, _, _ int) ([]complaint.Complaint, int64, error) {
	return s.active, int64(len(s.active)), nil
}
func (s *stubComplaintRepo) List(_ context.Context, _ complaint.ListFilter, _ string, _, _ int) ([]complaint.Complaint, int64, error) {
	return s.active, int64(len(s.active)), nil
}
func (s *stubComplaintRepo) HelicopterHasOpenComplaint(_ context.Context, _ []byte) (bool, error) {
	return s.open, nil
}
func (s *stubComplaintRepo) ActiveGroundingByHelicopterIDs(_ context.Context, _ [][]byte) ([]complaint.Complaint, error) {
	return s.active, nil
}
func (s *stubComplaintRepo) OpenByHelicopterIDs(_ context.Context, _ [][]byte) ([]complaint.Complaint, error) {
	return s.active, nil
}
func (s *stubComplaintRepo) HelicopterHasUnactionedComplaint(_ context.Context, _ []byte) (bool, error) {
	return s.unactioned, nil
}
func (s *stubComplaintRepo) MarkFixedByHelicopter(_ context.Context, helicopterID, _, _ []byte) error {
	s.markedHeli = helicopterID
	return nil
}
func (s *stubComplaintRepo) MarkFixedByComplaint(_ context.Context, complaintID, _, _ []byte) error {
	s.markedComplaint = complaintID
	return nil
}
func (s *stubComplaintRepo) MarkFixedByFlight(_ context.Context, flightID, _, _ []byte) error {
	s.markedFlight = flightID
	return nil
}

func TestComplaintServiceDelegation(t *testing.T) {
	repo := &stubComplaintRepo{
		row:        &complaint.Complaint{},
		open:       true,
		unactioned: true,
		active:     []complaint.Complaint{{}},
	}
	svc := NewComplaintService(repo)
	ctx := context.Background()

	if err := svc.Create(ctx, &complaint.Complaint{}); err != nil {
		t.Fatalf("Create: %v", err)
	}
	if err := svc.Update(ctx, &complaint.Complaint{}); err != nil {
		t.Fatalf("Update: %v", err)
	}
	if err := svc.Delete(ctx, []byte("x"), nil); err != nil {
		t.Fatalf("Delete: %v", err)
	}
	if _, err := svc.GetByID(ctx, []byte("x")); err != nil {
		t.Fatalf("GetByID: %v", err)
	}
	if rows, _, err := svc.ListByHelicopter(ctx, []byte("h"), 10, 0); err != nil || len(rows) != 1 {
		t.Fatalf("ListByHelicopter: %v", err)
	}
	if _, _, err := svc.List(ctx, complaint.ListFilter{}, "", 10, 0); err != nil {
		t.Fatalf("List: %v", err)
	}
	if open, err := svc.HelicopterHasOpenComplaint(ctx, []byte("h")); err != nil || !open {
		t.Fatalf("HelicopterHasOpenComplaint: %v", err)
	}
	if un, err := svc.HelicopterHasUnactionedComplaint(ctx, []byte("h")); err != nil || !un {
		t.Fatalf("HelicopterHasUnactionedComplaint: %v", err)
	}
	if _, err := svc.ActiveGroundingByHelicopterIDs(ctx, [][]byte{[]byte("h")}); err != nil {
		t.Fatalf("ActiveGroundingByHelicopterIDs: %v", err)
	}
	if err := svc.MarkFixedByHelicopter(ctx, []byte("heli"), []byte("easa"), []byte("actor")); err != nil || string(repo.markedHeli) != "heli" {
		t.Fatalf("MarkFixedByHelicopter: %v", err)
	}

	repo.createErr = errors.New("boom")
	if err := svc.Create(ctx, &complaint.Complaint{}); err == nil {
		t.Fatal("expected create error to propagate")
	}
}

type stubNameGetter struct{}

func (stubNameGetter) GetLastName(_ context.Context, id []byte) (string, error) {
	return "Name-" + string(id), nil
}

func TestComplaintServiceResolveUserNames(t *testing.T) {
	svc := NewComplaintService(&stubComplaintRepo{})
	ctx := userctx.WithGetter(context.Background(), stubNameGetter{})

	rows := []complaint.Complaint{
		{ReportedBy: []byte("u1"), MELClassifiedBy: []byte("u1"), NSRDecidedBy: []byte("u2")},
		{}, // all-empty ids skipped
	}
	names, err := svc.ResolveUserNames(ctx, rows)
	if err != nil {
		t.Fatalf("err: %v", err)
	}
	if names["u1"] != "Name-u1" || names["u2"] != "Name-u2" {
		t.Fatalf("unexpected names: %v", names)
	}
	if len(names) != 2 {
		t.Fatalf("dup/empty ids should be deduped/skipped, got %v", names)
	}

	// No getter in context -> no names resolved.
	empty, _ := svc.ResolveUserNames(context.Background(), rows)
	if len(empty) != 0 {
		t.Fatalf("expected no names without getter, got %v", empty)
	}
}
