package handler

import (
	"baron-sso-backend/internal/service"
	"bytes"
	"context"
	"encoding/csv"
	"errors"
	"log/slog"
	"strings"

	"github.com/gofiber/fiber/v2"
)

type WorksmobileHandler struct {
	Service service.WorksmobileAdminService
}

func NewWorksmobileHandler(svc service.WorksmobileAdminService) *WorksmobileHandler {
	return &WorksmobileHandler{Service: svc}
}

func (h *WorksmobileHandler) GetOverview(c *fiber.Ctx) error {
	overview, err := h.Service.GetTenantOverview(c.Context(), strings.TrimSpace(c.Params("tenantId")))
	if err != nil {
		return worksmobileGuardError(c, err, "get_overview")
	}
	if !worksmobileOverviewAllowed(overview) {
		return errorJSON(c, fiber.StatusNotFound, "worksmobile is only available for hanmac-family root tenant")
	}
	return c.JSON(overview)
}

func (h *WorksmobileHandler) GetComparison(c *fiber.Ctx) error {
	includeMatched := strings.EqualFold(strings.TrimSpace(c.Query("includeMatched")), "true")
	comparison, err := h.Service.GetComparison(c.Context(), strings.TrimSpace(c.Params("tenantId")), includeMatched)
	if err != nil {
		return worksmobileGuardError(c, err, "get_comparison")
	}
	return c.JSON(comparison)
}

func (h *WorksmobileHandler) OAuthCallback(c *fiber.Ctx) error {
	return c.Type("html").SendString("<!doctype html><html><body>Worksmobile OAuth callback reachable</body></html>")
}

func (h *WorksmobileHandler) BackfillDryRun(c *fiber.Ctx) error {
	result, err := h.Service.EnqueueBackfillDryRun(c.Context(), strings.TrimSpace(c.Params("tenantId")))
	if err != nil {
		return worksmobileGuardError(c, err, "backfill_dry_run")
	}
	return c.JSON(result)
}

func (h *WorksmobileHandler) SyncOrgUnit(c *fiber.Ctx) error {
	orgUnitID := strings.TrimSpace(c.Params("orgUnitId"))
	job, err := h.Service.EnqueueOrgUnitSync(c.Context(), strings.TrimSpace(c.Params("tenantId")), orgUnitID)
	if err != nil {
		return worksmobileGuardError(c, err, "sync_orgunit", "org_unit_id", orgUnitID)
	}
	return c.Status(fiber.StatusAccepted).JSON(job)
}

func (h *WorksmobileHandler) DeleteOrgUnit(c *fiber.Ctx) error {
	orgUnitID := strings.TrimSpace(c.Params("orgUnitId"))
	job, err := h.Service.EnqueueOrgUnitDelete(c.Context(), strings.TrimSpace(c.Params("tenantId")), orgUnitID)
	if err != nil {
		return worksmobileGuardError(c, err, "delete_orgunit", "org_unit_id", orgUnitID)
	}
	return c.Status(fiber.StatusAccepted).JSON(job)
}

func (h *WorksmobileHandler) SyncUser(c *fiber.Ctx) error {
	userID := strings.TrimSpace(c.Params("userId"))
	job, err := h.Service.EnqueueUserSync(c.Context(), strings.TrimSpace(c.Params("tenantId")), userID)
	if err != nil {
		return worksmobileGuardError(c, err, "sync_user", "user_id", userID)
	}
	return c.Status(fiber.StatusAccepted).JSON(job)
}

func (h *WorksmobileHandler) RetryJob(c *fiber.Ctx) error {
	jobID := strings.TrimSpace(c.Params("jobId"))
	job, err := h.Service.RetryJob(c.Context(), strings.TrimSpace(c.Params("tenantId")), jobID)
	if err != nil {
		return worksmobileGuardError(c, err, "retry_job", "job_id", jobID)
	}
	return c.JSON(job)
}

func (h *WorksmobileHandler) DownloadInitialPasswordsCSV(c *fiber.Ctx) error {
	credentials, err := h.Service.ListInitialPasswordCredentials(c.Context(), strings.TrimSpace(c.Params("tenantId")))
	if err != nil {
		return worksmobileGuardError(c, err, "download_initial_passwords")
	}

	var buf bytes.Buffer
	writer := csv.NewWriter(&buf)
	if err := writer.Write([]string{"email", "initialPassword", "status", "lastError"}); err != nil {
		return errorJSON(c, fiber.StatusInternalServerError, err.Error())
	}
	for _, credential := range credentials {
		if err := writer.Write([]string{credential.Email, credential.InitialPassword, credential.Status, credential.LastError}); err != nil {
			return errorJSON(c, fiber.StatusInternalServerError, err.Error())
		}
	}
	writer.Flush()
	if err := writer.Error(); err != nil {
		return errorJSON(c, fiber.StatusInternalServerError, err.Error())
	}

	c.Set(fiber.HeaderContentType, "text/csv; charset=utf-8")
	c.Set(fiber.HeaderContentDisposition, `attachment; filename="worksmobile_initial_passwords.csv"`)
	return c.Send(buf.Bytes())
}

func worksmobileOverviewAllowed(overview service.WorksmobileTenantOverview) bool {
	return overview.Tenant.Slug == service.HanmacFamilyTenantSlug && overview.Tenant.ParentID == nil
}

func worksmobileGuardError(c *fiber.Ctx, err error, operation string, attrs ...any) error {
	if err == nil {
		return nil
	}
	logAttrs := []any{
		"operation", operation,
		"tenant_id", strings.TrimSpace(c.Params("tenantId")),
		"path", c.Path(),
		"error", err,
	}
	logAttrs = append(logAttrs, attrs...)
	if errors.Is(err, context.Canceled) {
		slog.Warn("worksmobile admin operation failed", logAttrs...)
		return errorJSON(c, fiber.StatusRequestTimeout, err.Error())
	}
	slog.Error("worksmobile admin operation failed", logAttrs...)
	if strings.Contains(err.Error(), "hanmac-family root") {
		return errorJSON(c, fiber.StatusNotFound, err.Error())
	}
	return errorJSON(c, fiber.StatusInternalServerError, err.Error())
}
