package handler

import (
	"baron-sso-backend/internal/domain"
	"baron-sso-backend/internal/service"
	"bytes"
	"context"
	"errors"
	"io"
	"log/slog"
	"net/http/httptest"
	"testing"

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

func TestWorksmobileHandlerRejectsNonHanmacTenant(t *testing.T) {
	h := NewWorksmobileHandler(&fakeWorksmobileAdminService{
		overview: service.WorksmobileTenantOverview{
			Tenant: domain.Tenant{ID: "tenant-1", Slug: "other"},
		},
	})
	app := fiber.New()
	app.Get("/tenants/:tenantId/worksmobile", h.GetOverview)

	resp, err := app.Test(httptest.NewRequest("GET", "/tenants/tenant-1/worksmobile", nil))

	require.NoError(t, err)
	require.Equal(t, fiber.StatusNotFound, resp.StatusCode)
}

func TestWorksmobileHandlerReturnsOverviewForHanmacTenant(t *testing.T) {
	h := NewWorksmobileHandler(&fakeWorksmobileAdminService{
		overview: service.WorksmobileTenantOverview{
			Tenant: domain.Tenant{ID: "hanmac-id", Slug: "hanmac-family"},
			Config: service.WorksmobileConfigSummary{
				Enabled: true,
			},
		},
	})
	app := fiber.New()
	app.Get("/tenants/:tenantId/worksmobile", h.GetOverview)

	resp, err := app.Test(httptest.NewRequest("GET", "/tenants/hanmac-id/worksmobile", nil))

	require.NoError(t, err)
	require.Equal(t, fiber.StatusOK, resp.StatusCode)
}

func TestWorksmobileHandlerDownloadsInitialPasswordCSV(t *testing.T) {
	h := NewWorksmobileHandler(&fakeWorksmobileAdminService{
		credentials: []service.WorksmobileInitialPasswordCredential{
			{Email: "user@hanmaceng.co.kr", InitialPassword: "Aa1!Aa1!Aa1!Aa1!", Status: "processed"},
		},
	})
	app := fiber.New()
	app.Get("/tenants/:tenantId/worksmobile/initial-passwords.csv", h.DownloadInitialPasswordsCSV)

	resp, err := app.Test(httptest.NewRequest("GET", "/tenants/hanmac-id/worksmobile/initial-passwords.csv", nil))
	require.NoError(t, err)
	require.Equal(t, fiber.StatusOK, resp.StatusCode)
	require.Contains(t, resp.Header.Get("Content-Disposition"), "worksmobile_initial_passwords.csv")
	body, err := io.ReadAll(resp.Body)
	require.NoError(t, err)
	require.Contains(t, string(body), "email,initialPassword,status,lastError")
	require.Contains(t, string(body), "user@hanmaceng.co.kr,Aa1!Aa1!Aa1!Aa1!,processed,")
}

func TestWorksmobileHandlerLogsActionFailures(t *testing.T) {
	var logs bytes.Buffer
	previous := slog.Default()
	slog.SetDefault(slog.New(slog.NewJSONHandler(&logs, nil)))
	t.Cleanup(func() {
		slog.SetDefault(previous)
	})

	h := NewWorksmobileHandler(&fakeWorksmobileAdminService{
		syncUserErr: errors.New("works user sync failed"),
	})
	app := fiber.New()
	app.Post("/tenants/:tenantId/worksmobile/users/:userId/sync", h.SyncUser)

	resp, err := app.Test(httptest.NewRequest("POST", "/tenants/hanmac-id/worksmobile/users/user-1/sync", nil))

	require.NoError(t, err)
	require.Equal(t, fiber.StatusInternalServerError, resp.StatusCode)
	require.Contains(t, logs.String(), "worksmobile admin operation failed")
	require.Contains(t, logs.String(), "sync_user")
	require.Contains(t, logs.String(), "works user sync failed")
}

type fakeWorksmobileAdminService struct {
	overview    service.WorksmobileTenantOverview
	credentials []service.WorksmobileInitialPasswordCredential
	syncUserErr error
}

func (f *fakeWorksmobileAdminService) GetTenantOverview(ctx context.Context, tenantID string) (service.WorksmobileTenantOverview, error) {
	return f.overview, nil
}

func (f *fakeWorksmobileAdminService) GetComparison(ctx context.Context, tenantID string, includeMatched bool) (service.WorksmobileComparison, error) {
	return service.WorksmobileComparison{}, nil
}

func (f *fakeWorksmobileAdminService) EnqueueBackfillDryRun(ctx context.Context, tenantID string) (service.WorksmobileBackfillDryRun, error) {
	return service.WorksmobileBackfillDryRun{}, nil
}

func (f *fakeWorksmobileAdminService) EnqueueOrgUnitSync(ctx context.Context, tenantID, orgUnitID string) (*domain.WorksmobileOutbox, error) {
	return &domain.WorksmobileOutbox{ID: "job-orgunit", ResourceID: orgUnitID}, nil
}

func (f *fakeWorksmobileAdminService) EnqueueOrgUnitDelete(ctx context.Context, tenantID, orgUnitID string) (*domain.WorksmobileOutbox, error) {
	return &domain.WorksmobileOutbox{ID: "job-orgunit-delete", ResourceID: orgUnitID, Action: domain.WorksmobileActionDelete}, nil
}

func (f *fakeWorksmobileAdminService) EnqueueUserSync(ctx context.Context, tenantID, userID string) (*domain.WorksmobileOutbox, error) {
	if f.syncUserErr != nil {
		return nil, f.syncUserErr
	}
	return &domain.WorksmobileOutbox{ID: "job-user", ResourceID: userID}, nil
}

func (f *fakeWorksmobileAdminService) RetryJob(ctx context.Context, tenantID, jobID string) (*domain.WorksmobileOutbox, error) {
	return &domain.WorksmobileOutbox{ID: jobID}, nil
}

func (f *fakeWorksmobileAdminService) ListInitialPasswordCredentials(ctx context.Context, tenantID string) ([]service.WorksmobileInitialPasswordCredential, error) {
	return f.credentials, nil
}
