package dialectsfcrm

import (
	"bytes"
	"context"
	"encoding/json"
	"errors"
	"net/http"
	"strings"
	"time"

	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
)

type connectorWebhookSecretListItem struct {
	ID            string  `json:"id"`
	OwnerPack     string  `json:"ownerPack"`
	Name          string  `json:"name"`
	Algorithm     string  `json:"algorithm"`
	Description   *string `json:"description,omitempty"`
	Masked        string  `json:"masked"`
	LastRotatedAt *string `json:"lastRotatedAt,omitempty"`
}

type connectorWebhookSecretRotateRequest struct {
	SecretValue *string `json:"secretValue"`
}

type connectorWebhookSecretRotateResponse struct {
	ID        string `json:"id"`
	Masked    string `json:"masked"`
	RotatedAt string `json:"rotatedAt"`
}

// ConnectorWebhookSecretsListNeutralHandler handles
// GET /connector-webhooks/secrets[?namespace=X].
func ConnectorWebhookSecretsListNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	secrets := deps.Contracts.ConnectorWebhookSecrets
	if secrets == nil {
		return connectorWebhookSecretsSimpleError(http.StatusInternalServerError, "QUERY_FAILED"), nil
	}
	items, err := secrets.ListConnectorWebhookSecrets(ctx, firstQuery(req, "namespace"))
	if err != nil {
		return connectorWebhookSecretsErrorResponse(err), nil
	}

	out := make([]connectorWebhookSecretListItem, 0, len(items))
	for _, item := range items {
		dto := connectorWebhookSecretListItem{
			ID:          item.ID,
			OwnerPack:   item.OwnerPack,
			Name:        item.Name,
			Algorithm:   item.Algorithm,
			Description: item.Description,
			Masked:      item.MaskedHint,
		}
		if item.LastRotatedAt != nil {
			value := item.LastRotatedAt.UTC().Format(time.RFC3339)
			dto.LastRotatedAt = &value
		}
		out = append(out, dto)
	}
	return jsonResponse(http.StatusOK, map[string]any{"webhookSecrets": out}), nil
}

// ConnectorWebhookSecretsRotateNeutralHandler handles
// POST /connector-webhooks/secrets/:id/rotate.
func ConnectorWebhookSecretsRotateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	defID := ""
	if req != nil {
		defID = strings.TrimSpace(req.Params["id"])
	}
	if defID == "" {
		return connectorWebhookSecretsSimpleError(http.StatusBadRequest, "MISSING_ID"), nil
	}

	var body connectorWebhookSecretRotateRequest
	if req == nil {
		return connectorWebhookSecretsInvalidBodyResponse("EOF"), nil
	}
	if err := json.NewDecoder(bytes.NewReader(req.Body)).Decode(&body); err != nil {
		return connectorWebhookSecretsInvalidBodyResponse(err.Error()), nil
	}
	if body.SecretValue == nil || *body.SecretValue == "" {
		return connectorWebhookSecretsInvalidBodyResponse("Key: 'rotateRequest.SecretValue' Error:Field validation for 'SecretValue' failed on the 'required' tag"), nil
	}

	secrets := deps.Contracts.ConnectorWebhookSecrets
	if secrets == nil {
		return connectorWebhookSecretsSimpleError(http.StatusInternalServerError, "QUERY_FAILED"), nil
	}
	result, err := secrets.RotateConnectorWebhookSecret(ctx, defID, *body.SecretValue)
	if err != nil {
		return connectorWebhookSecretsErrorResponse(err), nil
	}
	if result == nil {
		return connectorWebhookSecretsSimpleError(http.StatusInternalServerError, "UPSERT_FAILED"), nil
	}

	return &dialect.Response{
		Status: http.StatusOK,
		Body: connectorWebhookSecretRotateResponse{
			ID:        result.ID,
			Masked:    result.MaskedHint,
			RotatedAt: result.RotatedAt.UTC().Format(time.RFC3339),
		},
	}, nil
}

func connectorWebhookSecretsErrorResponse(err error) *dialect.Response {
	var secretErr *contract.ConnectorWebhookSecretsError
	if !errors.As(err, &secretErr) {
		return connectorWebhookSecretsSimpleError(http.StatusInternalServerError, "QUERY_FAILED")
	}
	switch secretErr.Kind {
	case contract.ConnectorWebhookSecretsErrorQueryFailed:
		return connectorWebhookSecretsSimpleError(http.StatusInternalServerError, "QUERY_FAILED")
	case contract.ConnectorWebhookSecretsErrorScanFailed:
		return connectorWebhookSecretsSimpleError(http.StatusInternalServerError, "SCAN_FAILED")
	case contract.ConnectorWebhookSecretsErrorRowsFailed:
		return connectorWebhookSecretsSimpleError(http.StatusInternalServerError, "ROWS_FAILED")
	case contract.ConnectorWebhookSecretsErrorEmptySecretValue:
		return connectorWebhookSecretsSimpleError(http.StatusBadRequest, "EMPTY_SECRET_VALUE")
	case contract.ConnectorWebhookSecretsErrorSecretTooShort:
		minLength := secretErr.MinLength
		if minLength == 0 {
			minLength = 16
		}
		return &dialect.Response{
			Status: http.StatusBadRequest,
			Body: map[string]any{
				"error":     "SECRET_TOO_SHORT",
				"minLength": minLength,
			},
		}
	case contract.ConnectorWebhookSecretsErrorEncryptionUnconfigured:
		detail := secretErr.Detail
		if detail == "" {
			detail = "webhook secret rotation requires NEXUSCRM_NC_MASTER_KEY"
		}
		return &dialect.Response{
			Status: http.StatusInternalServerError,
			Body: map[string]any{
				"error":  "ENCRYPTION_UNCONFIGURED",
				"detail": detail,
			},
		}
	case contract.ConnectorWebhookSecretsErrorEncryptionFailed:
		return connectorWebhookSecretsSimpleError(http.StatusInternalServerError, "ENCRYPTION_FAILED")
	case contract.ConnectorWebhookSecretsErrorWebhookSecretNotFound:
		return connectorWebhookSecretsSimpleError(http.StatusNotFound, "WEBHOOK_SECRET_NOT_FOUND")
	case contract.ConnectorWebhookSecretsErrorUpsertFailed:
		return connectorWebhookSecretsSimpleError(http.StatusInternalServerError, "UPSERT_FAILED")
	default:
		return connectorWebhookSecretsSimpleError(http.StatusInternalServerError, "QUERY_FAILED")
	}
}

func connectorWebhookSecretsSimpleError(status int, code string) *dialect.Response {
	return &dialect.Response{Status: status, Body: map[string]any{"error": code}}
}

func connectorWebhookSecretsInvalidBodyResponse(detail string) *dialect.Response {
	resp := map[string]any{"error": "INVALID_BODY"}
	if detail != "" {
		resp["detail"] = detail
	}
	return &dialect.Response{Status: http.StatusBadRequest, Body: resp}
}
