package externaladmin

import (
	"context"
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	adminmiddleware "hyapp-admin-server/internal/middleware"
	"hyapp-admin-server/internal/model"

	"github.com/DATA-DOG/go-sqlmock"
	"github.com/gin-gonic/gin"
	"google.golang.org/grpc"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	userv1 "hyapp.local/api/proto/user/v1"
)

type managerTeamHostClient struct {
	userv1.UserHostServiceClient
	request *userv1.ListManagerTeamAgenciesRequest
	calls   int
}

func (client *managerTeamHostClient) ListManagerTeamAgencies(_ context.Context, request *userv1.ListManagerTeamAgenciesRequest, _ ...grpc.CallOption) (*userv1.ListManagerTeamAgenciesResponse, error) {
	client.calls++
	client.request = request
	return &userv1.ListManagerTeamAgenciesResponse{
		Agencies: []*userv1.ManagerTeamAgency{
			{AgencyId: 101, OwnerUserId: 1001, ParentBdUserId: 2001},
			{AgencyId: 102, OwnerUserId: 1002, ParentBdUserId: 2002},
			{AgencyId: 103, OwnerUserId: 1003, ParentBdUserId: 2003},
		},
		TotalBdLeaders: 4,
		TotalBds:       9,
		Truncated:      true,
	}, nil
}

func TestRetiredMyTeamRouteIsNotExposed(t *testing.T) {
	gin.SetMode(gin.TestMode)
	client := &managerTeamHostClient{}
	handler := New(nil, nil, Config{}, nil, WithUserHostClient(client))
	engine := gin.New()
	group := engine.Group("/api/v1/external")
	group.Use(func(c *gin.Context) {
		c.Set(contextPrincipal, SessionPrincipal{AppCode: "fami", LinkedAppUserID: 42})
		c.Set(adminmiddleware.ContextPermissions, []string{"agency:list"})
		c.Next()
	})
	registerBusinessWhitelist(group, handler, BusinessHandlers{})

	request := httptest.NewRequest(http.MethodGet, "/api/v1/external/admin/my-team/agencies?page=2&page_size=2&manager_user_id=999&user_id=888", nil)
	responseRecorder := httptest.NewRecorder()
	engine.ServeHTTP(responseRecorder, request)
	if responseRecorder.Code != http.StatusNotFound {
		t.Fatalf("status = %d body=%s", responseRecorder.Code, responseRecorder.Body.String())
	}
	if client.calls != 0 {
		t.Fatalf("retired route called owner service %d times", client.calls)
	}
}

func TestValidateParentRejectsManagerWhoseLocalMovedRegion(t *testing.T) {
	adminSQL, adminMock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("new admin sqlmock: %v", err)
	}
	defer adminSQL.Close()
	adminDB, err := gorm.Open(mysql.New(mysql.Config{Conn: adminSQL, SkipInitializeWithVersion: true}), &gorm.Config{})
	if err != nil {
		t.Fatalf("open admin gorm: %v", err)
	}
	userDB, userMock, err := sqlmock.New()
	if err != nil {
		t.Fatalf("new user sqlmock: %v", err)
	}
	defer userDB.Close()

	managerID := uint64(11)
	localID := uint64(9)
	accountColumns := []string{"id", "app_code", "linked_app_user_id", "identity_type", "parent_account_id", "status"}
	adminMock.ExpectQuery("SELECT \\* FROM `external_admin_accounts` WHERE id = \\? AND app_code = \\? AND identity_type = \\? AND status = \\?").
		WithArgs(managerID, "fami", model.ExternalAdminIdentityCountryManager, model.ExternalAdminStatusActive, 1).
		WillReturnRows(sqlmock.NewRows(accountColumns).AddRow(
			managerID, "fami", int64(1100), model.ExternalAdminIdentityCountryManager, localID, model.ExternalAdminStatusActive,
		))
	userMock.ExpectQuery("SELECT u.region_id").
		WithArgs("fami", int64(1100)).
		WillReturnRows(sqlmock.NewRows([]string{"region_id"}).AddRow(int64(12)))
	adminMock.ExpectQuery("SELECT \\* FROM `external_admin_accounts` WHERE id = \\? AND app_code = \\? AND identity_type = \\? AND status = \\?").
		WithArgs(localID, "fami", model.ExternalAdminIdentityLocal, model.ExternalAdminStatusActive, 1).
		WillReturnRows(sqlmock.NewRows(accountColumns).AddRow(
			localID, "fami", int64(900), model.ExternalAdminIdentityLocal, nil, model.ExternalAdminStatusActive,
		))
	userMock.ExpectQuery("SELECT u.region_id").
		WithArgs("fami", int64(900)).
		WillReturnRows(sqlmock.NewRows([]string{"region_id"}).AddRow(int64(13)))

	service := NewService(adminDB, userDB, Config{})
	if _, err := service.validateParentForChild(
		t.Context(),
		adminDB,
		"fami",
		model.ExternalAdminIdentityExternalSuperAdmin,
		&managerID,
		12,
	); !errors.Is(err, ErrInvalidHierarchy) {
		t.Fatalf("validate parent error = %v, want ErrInvalidHierarchy", err)
	}
	if err := adminMock.ExpectationsWereMet(); err != nil {
		t.Fatalf("admin expectations: %v", err)
	}
	if err := userMock.ExpectationsWereMet(); err != nil {
		t.Fatalf("user expectations: %v", err)
	}
}
