package auth_test

import (
	"context"
	"testing"

	"github.com/rede5/gohorsejobs/backend/internal/core/domain/entity"
	"github.com/rede5/gohorsejobs/backend/internal/core/dto"
	"github.com/rede5/gohorsejobs/backend/internal/core/usecases/auth"
)

// --- Mocks ---

type MockUserRepo struct {
	SaveFunc        func(ctx context.Context, user *entity.User) (*entity.User, error)
	FindByEmailFunc func(ctx context.Context, email string) (*entity.User, error)
}

func (m *MockUserRepo) Save(ctx context.Context, user *entity.User) (*entity.User, error) {
	if m.SaveFunc != nil {
		return m.SaveFunc(ctx, user)
	}
	return user, nil
}
func (m *MockUserRepo) FindByEmail(ctx context.Context, email string) (*entity.User, error) {
	if m.FindByEmailFunc != nil {
		return m.FindByEmailFunc(ctx, email)
	}
	return nil, nil // Not found by default
}
func (m *MockUserRepo) FindByID(ctx context.Context, id string) (*entity.User, error) {
	return nil, nil
}
func (m *MockUserRepo) FindAllByTenant(ctx context.Context, tenantID string, l, o int) ([]*entity.User, int, error) {
	return nil, 0, nil
}
func (m *MockUserRepo) Update(ctx context.Context, user *entity.User) (*entity.User, error) {
	return nil, nil
}
func (m *MockUserRepo) Delete(ctx context.Context, id string) error { return nil }
func (m *MockUserRepo) LinkGuestApplications(ctx context.Context, email string, userID string) error {
	return nil
}

type MockCompanyRepo struct {
	SaveFunc func(ctx context.Context, company *entity.Company) (*entity.Company, error)
}

func (m *MockCompanyRepo) Save(ctx context.Context, company *entity.Company) (*entity.Company, error) {
	if m.SaveFunc != nil {
		return m.SaveFunc(ctx, company)
	}
	return company, nil
}
func (m *MockCompanyRepo) FindByID(ctx context.Context, id string) (*entity.Company, error) {
	return nil, nil
}
func (m *MockCompanyRepo) FindAll(ctx context.Context) ([]*entity.Company, error) {
	return nil, nil
}
func (m *MockCompanyRepo) Update(ctx context.Context, company *entity.Company) (*entity.Company, error) {
	return nil, nil
}
func (m *MockCompanyRepo) Delete(ctx context.Context, id string) error { return nil }

type MockAuthService struct {
	HashPasswordFunc  func(password string) (string, error)
	GenerateTokenFunc func(userID, tenantID string, roles []string) (string, error)
}

func (m *MockAuthService) HashPassword(password string) (string, error) {
	if m.HashPasswordFunc != nil {
		return m.HashPasswordFunc(password)
	}
	return "hashed_" + password, nil
}
func (m *MockAuthService) GenerateToken(userID, tenantID string, roles []string) (string, error) {
	if m.GenerateTokenFunc != nil {
		return m.GenerateTokenFunc(userID, tenantID, roles)
	}
	return "mock_token", nil
}
func (m *MockAuthService) VerifyPassword(hash, password string) bool { return true }
func (m *MockAuthService) ValidateToken(token string) (map[string]interface{}, error) {
	return nil, nil
}

// --- Tests ---

func TestRegisterCandidateUseCase_Execute(t *testing.T) {
	t.Run("Success", func(t *testing.T) {
		userRepo := &MockUserRepo{
			FindByEmailFunc: func(ctx context.Context, email string) (*entity.User, error) {
				return nil, nil // Email not found
			},
			SaveFunc: func(ctx context.Context, user *entity.User) (*entity.User, error) {
				user.ID = "new-user-id"
				return user, nil
			},
		}
		companyRepo := &MockCompanyRepo{}
		authSvc := &MockAuthService{}

		uc := auth.NewRegisterCandidateUseCase(userRepo, companyRepo, authSvc, nil)

		input := dto.RegisterCandidateRequest{
			Name:     "John Doe",
			Email:    "john@example.com",
			Password: "password123",
			Username: "johndoe",
			Phone:    "+1234567890",
		}

		resp, err := uc.Execute(context.Background(), input)

		if err != nil {
			t.Fatalf("Expected no error, got %v", err)
		}
		if resp == nil {
			t.Fatal("Expected response, got nil")
		}
		if resp.User.Email != input.Email {
			t.Errorf("Expected email %s, got %s", input.Email, resp.User.Email)
		}
		if resp.Token != "mock_token" {
			t.Errorf("Expected token mock_token, got %s", resp.Token)
		}
	})

	t.Run("EmailAlreadyExists", func(t *testing.T) {
		userRepo := &MockUserRepo{
			FindByEmailFunc: func(ctx context.Context, email string) (*entity.User, error) {
				return &entity.User{ID: "existing"}, nil // Found
			},
		}
		companyRepo := &MockCompanyRepo{}
		authSvc := &MockAuthService{}
		uc := auth.NewRegisterCandidateUseCase(userRepo, companyRepo, authSvc, nil)

		_, err := uc.Execute(context.Background(), dto.RegisterCandidateRequest{Email: "exists@example.com", Password: "123"})

		if err == nil {
			t.Error("Expected error, got nil")
		}
		if err.Error() != "email already registered" {
			t.Errorf("Expected 'email already registered', got %v", err)
		}
	})

	t.Run("MetadataSaved", func(t *testing.T) {
		// Verify if username/phone ends up in metadata
		var capturedUser *entity.User
		userRepo := &MockUserRepo{
			SaveFunc: func(ctx context.Context, user *entity.User) (*entity.User, error) {
				capturedUser = user
				return user, nil
			},
		}
		companyRepo := &MockCompanyRepo{}
		authSvc := &MockAuthService{}
		uc := auth.NewRegisterCandidateUseCase(userRepo, companyRepo, authSvc, nil)

		uc.Execute(context.Background(), dto.RegisterCandidateRequest{Username: "coder", Phone: "999"})

		if capturedUser == nil {
			t.Fatal("User not saved")
		}
		if capturedUser.Metadata["username"] != "coder" {
			t.Errorf("Expected metadata username 'coder', got %v", capturedUser.Metadata["username"])
		}
		if capturedUser.Metadata["phone"] != "999" {
			t.Errorf("Expected metadata phone '999', got %v", capturedUser.Metadata["phone"])
		}
	})

	t.Run("CompanyCreatedForCandidate", func(t *testing.T) {
		var capturedCompany *entity.Company
		userRepo := &MockUserRepo{}
		companyRepo := &MockCompanyRepo{
			SaveFunc: func(ctx context.Context, company *entity.Company) (*entity.Company, error) {
				capturedCompany = company
				return company, nil
			},
		}
		authSvc := &MockAuthService{}
		uc := auth.NewRegisterCandidateUseCase(userRepo, companyRepo, authSvc, nil)

		uc.Execute(context.Background(), dto.RegisterCandidateRequest{Name: "Test User", Email: "test@test.com", Password: "123"})

		if capturedCompany == nil {
			t.Fatal("Company not created")
		}
		if capturedCompany.Name != "Candidate - Test User" {
			t.Errorf("Expected company name 'Candidate - Test User', got %s", capturedCompany.Name)
		}
	})
}
