package handler

import (
	"baron-sso-backend/internal/domain"
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/gofiber/fiber/v2"
	"github.com/stretchr/testify/require"
)

type fakeDataIntegrityChecker struct {
	calls        int
	listCalls    int
	deleteCalls  int
	deletedIDs   []string
	report       domain.DataIntegrityReport
	orphans      []domain.OrphanUserLoginID
	deleteResult domain.DeleteOrphanUserLoginIDsResult
	err          error
}

func (f *fakeDataIntegrityChecker) CheckDataIntegrity(ctx context.Context) (domain.DataIntegrityReport, error) {
	f.calls++
	return f.report, f.err
}

func (f *fakeDataIntegrityChecker) ListOrphanUserLoginIDs(ctx context.Context) ([]domain.OrphanUserLoginID, error) {
	f.listCalls++
	return f.orphans, f.err
}

func (f *fakeDataIntegrityChecker) DeleteOrphanUserLoginIDs(ctx context.Context, ids []string) (domain.DeleteOrphanUserLoginIDsResult, error) {
	f.deleteCalls++
	f.deletedIDs = append([]string(nil), ids...)
	return f.deleteResult, f.err
}

func TestAdminHandler_GetDataIntegrityRequiresSuperAdmin(t *testing.T) {
	checker := &fakeDataIntegrityChecker{}
	h := &AdminHandler{IntegrityChecker: checker}
	app := fiber.New()
	app.Use(func(c *fiber.Ctx) error {
		c.Locals("user_profile", &domain.UserProfileResponse{ID: "tenant-admin", Role: domain.RoleTenantAdmin})
		return c.Next()
	})
	app.Get("/api/v1/admin/integrity", h.GetDataIntegrity)

	req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/integrity", nil)
	resp, err := app.Test(req)
	require.NoError(t, err)
	require.Equal(t, http.StatusForbidden, resp.StatusCode)
	require.Equal(t, 0, checker.calls)
}

func TestAdminHandler_GetDataIntegrityReturnsReportForSuperAdmin(t *testing.T) {
	checkedAt := time.Date(2026, 5, 14, 0, 0, 0, 0, time.UTC)
	checker := &fakeDataIntegrityChecker{
		report: domain.DataIntegrityReport{
			Status:    domain.DataIntegrityStatusFail,
			CheckedAt: checkedAt,
			Summary: domain.DataIntegritySummary{
				TotalChecks: 1,
				Failures:    1,
			},
			Sections: []domain.DataIntegritySection{
				{
					Key:    "tenant_integrity",
					Label:  "테넌트 정합성",
					Status: domain.DataIntegrityStatusFail,
					Checks: []domain.DataIntegrityCheck{
						{
							Key:      "duplicate_tenant_slugs",
							Label:    "중복 테넌트 slug",
							Status:   domain.DataIntegrityStatusFail,
							Count:    1,
							Severity: "error",
						},
					},
				},
			},
		},
	}
	h := &AdminHandler{IntegrityChecker: checker}
	app := fiber.New()
	app.Use(func(c *fiber.Ctx) error {
		c.Locals("user_profile", &domain.UserProfileResponse{ID: "super", Role: domain.RoleSuperAdmin})
		return c.Next()
	})
	app.Get("/api/v1/admin/integrity", h.GetDataIntegrity)

	req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/integrity", nil)
	resp, err := app.Test(req)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, resp.StatusCode)
	require.Equal(t, 1, checker.calls)

	var body domain.DataIntegrityReport
	require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
	require.Equal(t, domain.DataIntegrityStatusFail, body.Status)
	require.Equal(t, int64(1), body.Summary.Failures)
	require.Len(t, body.Sections, 1)
	require.Equal(t, "tenant_integrity", body.Sections[0].Key)
}

func TestAdminHandler_ListOrphanUserLoginIDsReturnsTargetsForSuperAdmin(t *testing.T) {
	checker := &fakeDataIntegrityChecker{
		orphans: []domain.OrphanUserLoginID{
			{
				ID:       "login-id-1",
				UserID:   "user-1",
				TenantID: "tenant-1",
				FieldKey: "emp_id",
				LoginID:  "EMP001",
				Reasons:  []string{"missing_tenant"},
			},
		},
	}
	h := &AdminHandler{IntegrityChecker: checker}
	app := fiber.New()
	app.Use(func(c *fiber.Ctx) error {
		c.Locals("user_profile", &domain.UserProfileResponse{ID: "super", Role: domain.RoleSuperAdmin})
		return c.Next()
	})
	app.Get("/api/v1/admin/integrity/orphan-user-login-ids", h.ListOrphanUserLoginIDs)

	req := httptest.NewRequest(http.MethodGet, "/api/v1/admin/integrity/orphan-user-login-ids", nil)
	resp, err := app.Test(req)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, resp.StatusCode)
	require.Equal(t, 1, checker.listCalls)

	var body struct {
		Items []domain.OrphanUserLoginID `json:"items"`
		Total int                        `json:"total"`
	}
	require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
	require.Equal(t, 1, body.Total)
	require.Equal(t, "login-id-1", body.Items[0].ID)
	require.Equal(t, []string{"missing_tenant"}, body.Items[0].Reasons)
}

func TestAdminHandler_DeleteOrphanUserLoginIDsRequiresSuperAdminAndDeletesSelectedTargets(t *testing.T) {
	checker := &fakeDataIntegrityChecker{
		deleteResult: domain.DeleteOrphanUserLoginIDsResult{
			DeletedCount: 1,
			Deleted: []domain.OrphanUserLoginID{
				{ID: "login-id-1", LoginID: "EMP001", Reasons: []string{"missing_user"}},
			},
			SkippedIDs: []string{"valid-login-id"},
		},
	}
	h := &AdminHandler{IntegrityChecker: checker}
	app := fiber.New()
	app.Use(func(c *fiber.Ctx) error {
		c.Locals("user_profile", &domain.UserProfileResponse{ID: "super", Role: domain.RoleSuperAdmin})
		return c.Next()
	})
	app.Delete("/api/v1/admin/integrity/orphan-user-login-ids", h.DeleteOrphanUserLoginIDs)

	req := httptest.NewRequest(http.MethodDelete, "/api/v1/admin/integrity/orphan-user-login-ids", strings.NewReader(`{"ids":["login-id-1","valid-login-id"]}`))
	req.Header.Set("Content-Type", "application/json")
	resp, err := app.Test(req)
	require.NoError(t, err)
	require.Equal(t, http.StatusOK, resp.StatusCode)
	require.Equal(t, 1, checker.deleteCalls)
	require.Equal(t, []string{"login-id-1", "valid-login-id"}, checker.deletedIDs)

	var body domain.DeleteOrphanUserLoginIDsResult
	require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
	require.Equal(t, int64(1), body.DeletedCount)
	require.Equal(t, []string{"valid-login-id"}, body.SkippedIDs)
}

func TestAdminHandler_DeleteOrphanUserLoginIDsRejectsTenantAdmin(t *testing.T) {
	checker := &fakeDataIntegrityChecker{}
	h := &AdminHandler{IntegrityChecker: checker}
	app := fiber.New()
	app.Use(func(c *fiber.Ctx) error {
		c.Locals("user_profile", &domain.UserProfileResponse{ID: "tenant-admin", Role: domain.RoleTenantAdmin})
		return c.Next()
	})
	app.Delete("/api/v1/admin/integrity/orphan-user-login-ids", h.DeleteOrphanUserLoginIDs)

	req := httptest.NewRequest(http.MethodDelete, "/api/v1/admin/integrity/orphan-user-login-ids", strings.NewReader(`{"ids":["login-id-1"]}`))
	req.Header.Set("Content-Type", "application/json")
	resp, err := app.Test(req)
	require.NoError(t, err)
	require.Equal(t, http.StatusForbidden, resp.StatusCode)
	require.Equal(t, 0, checker.deleteCalls)
}
