package dialectsfcrm

import (
	"context"
	"encoding/json"
	"errors"
	"log"
	"net/http"
	"regexp"

	"github.com/nexuscrm/dialect-sf-crm/wire"
	"github.com/nexuscrm/shared/pkg/contract"
	"github.com/nexuscrm/shared/pkg/dialect"
)

const formulaViewGenericInternalErrorMessage = "An internal server error occurred. Contact your system administrator."

var formulaViewObjectNameRe = regexp.MustCompile(`^[a-zA-Z][a-zA-Z0-9_]*$`)

func FormulaViewStatusNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	objectName, resp := formulaViewObjectName(req)
	if resp != nil {
		return resp, nil
	}
	maintenance, resp := formulaViewMaintenanceFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	result, err := maintenance.FormulaViewStatus(ctx, objectName)
	if err != nil {
		return formulaViewMaintenanceErrorResponse(err), nil
	}
	if result == nil {
		result = &contract.FormulaViewStatusResult{Object: objectName, ViewName: "v_" + objectName}
	}
	return jsonResponse(http.StatusOK, map[string]any{
		"object":             result.Object,
		"viewExists":         result.ViewExists,
		"viewName":           result.ViewName,
		"activeFormulaCount": result.ActiveFormulaCount,
		"frozenFormulaCount": result.FrozenFormulaCount,
		"resolvedTableName":  result.ResolvedTableName,
	}), nil
}

func FormulaViewDDLNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	objectName, resp := formulaViewObjectName(req)
	if resp != nil {
		return resp, nil
	}
	maintenance, resp := formulaViewMaintenanceFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	result, err := maintenance.PreviewFormulaViewDDL(ctx, objectName)
	if err != nil {
		return formulaViewMaintenanceErrorResponse(err), nil
	}
	if result == nil {
		result = &contract.FormulaViewDDLResult{Object: objectName}
	}
	return jsonResponse(http.StatusOK, map[string]any{
		"object": result.Object,
		"ddl":    result.DDL,
	}), nil
}

func FormulaViewRebuildNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	objectName, resp := formulaViewObjectName(req)
	if resp != nil {
		return resp, nil
	}
	maintenance, resp := formulaViewMaintenanceFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	result, err := maintenance.RebuildFormulaView(ctx, objectName)
	if err != nil {
		return formulaViewMaintenanceErrorResponse(err), nil
	}
	if result == nil {
		result = &contract.FormulaViewRebuildResult{Object: objectName, ViewName: "v_" + objectName}
	}
	return jsonResponse(http.StatusOK, map[string]any{
		"success":  true,
		"object":   result.Object,
		"viewName": result.ViewName,
		"message":  "View rebuilt successfully",
	}), nil
}

func FormulaViewRebuildAllNeutralHandler(ctx context.Context, _ *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	maintenance, resp := formulaViewMaintenanceFromDeps(deps)
	if resp != nil {
		return resp, nil
	}
	if _, err := maintenance.RebuildAllFormulaViews(ctx); err != nil {
		return formulaViewMaintenanceErrorResponse(err), nil
	}
	return jsonResponse(http.StatusOK, map[string]any{
		"success": true,
		"message": "All formula views rebuilt",
	}), nil
}

func FormulaViewValidateNeutralHandler(ctx context.Context, req *dialect.Request, deps dialect.Deps) (*dialect.Response, error) {
	objectName, resp := formulaViewObjectName(req)
	if resp != nil {
		return resp, nil
	}
	maintenance, resp := formulaViewMaintenanceFromDeps(deps)
	if resp != nil {
		return resp, nil
	}

	var body struct {
		Formula      string `json:"formula"`
		ReturnType   string `json:"returnType"`
		FieldAPIName string `json:"fieldApiName"`
	}
	if req == nil {
		return &dialect.Response{
			Status: http.StatusBadRequest,
			Body:   wire.SFErrorResponse(wire.SFCodeInvalidRequest, "invalid request body: EOF"),
		}, nil
	}
	if err := json.Unmarshal(req.Body, &body); err != nil {
		return &dialect.Response{
			Status: http.StatusBadRequest,
			Body:   wire.SFErrorResponse(wire.SFCodeInvalidRequest, "invalid request body: "+err.Error()),
		}, nil
	}
	if body.Formula == "" {
		return &dialect.Response{
			Status: http.StatusBadRequest,
			Body:   wire.SFErrorResponse(wire.SFCodeInvalidRequest, "formula is required"),
		}, nil
	}

	result, err := maintenance.ValidateFormulaView(ctx, contract.FormulaViewValidationRequest{
		ObjectName:   objectName,
		Formula:      body.Formula,
		ReturnType:   body.ReturnType,
		FieldAPIName: body.FieldAPIName,
	})
	if err != nil {
		return formulaViewMaintenanceErrorResponse(err), nil
	}
	if result == nil {
		result = &contract.FormulaViewValidationResult{}
	}
	if !result.Valid {
		return jsonResponse(http.StatusOK, map[string]any{
			"valid": false,
			"error": result.Error,
		}), nil
	}
	return jsonResponse(http.StatusOK, map[string]any{
		"valid":            true,
		"compiledSQL":      result.CompiledSQL,
		"referencedFields": result.ReferencedFields,
		"crossTableRefs":   result.CrossTableRefs,
	}), nil
}

func formulaViewObjectName(req *dialect.Request) (string, *dialect.Response) {
	if req == nil || req.Params == nil || req.Params["objectName"] == "" {
		return "", formulaViewInvalidRequest("objectName is required")
	}
	name := req.Params["objectName"]
	if !formulaViewObjectNameRe.MatchString(name) {
		return "", formulaViewInvalidRequest("objectName contains invalid characters")
	}
	return name, nil
}

func formulaViewMaintenanceFromDeps(deps dialect.Deps) (contract.MetadataMaintenance, *dialect.Response) {
	if deps.Contracts.MetadataMaintenance == nil {
		log.Printf("formula view maintenance contract unavailable: metadata maintenance contract is not configured")
		return nil, formulaViewServiceUnavailableResponse()
	}
	return deps.Contracts.MetadataMaintenance, nil
}

func formulaViewMaintenanceErrorResponse(err error) *dialect.Response {
	var maintenanceErr *contract.MetadataMaintenanceError
	if errors.As(err, &maintenanceErr) {
		message := maintenanceErr.Error()
		switch maintenanceErr.Kind {
		case contract.MetadataMaintenanceErrorUnavailable:
			logFormulaViewMaintenanceError("formula view maintenance unavailable", maintenanceErr)
			return formulaViewServiceUnavailableResponse()
		case contract.MetadataMaintenanceErrorInvalid:
			return &dialect.Response{Status: http.StatusBadRequest, Body: wire.SFErrorResponse(wire.SFCodeInvalidRequest, message)}
		case contract.MetadataMaintenanceErrorNotFound:
			return &dialect.Response{Status: http.StatusNotFound, Body: wire.SFErrorResponse(wire.SFCodeNotFound, message)}
		case contract.MetadataMaintenanceErrorInternal:
			logFormulaViewMaintenanceError("formula view maintenance internal error", maintenanceErr)
			return internalErrorResponse(formulaViewGenericInternalErrorMessage)
		}
		logFormulaViewMaintenanceError("formula view maintenance error with unknown kind", maintenanceErr)
		return internalErrorResponse(formulaViewGenericInternalErrorMessage)
	}
	log.Printf("formula view maintenance error: %v", err)
	return internalErrorResponse(formulaViewGenericInternalErrorMessage)
}

func logFormulaViewMaintenanceError(message string, err error) {
	var maintenanceErr *contract.MetadataMaintenanceError
	if errors.As(err, &maintenanceErr) && maintenanceErr.Cause != nil {
		log.Printf("%s: %v (cause: %v)", message, maintenanceErr, maintenanceErr.Cause)
		return
	}
	log.Printf("%s: %v", message, err)
}

func formulaViewServiceUnavailableResponse() *dialect.Response {
	return &dialect.Response{
		Status: http.StatusServiceUnavailable,
		Body:   wire.SFErrorResponse(wire.SFCodeInternalError, formulaViewGenericInternalErrorMessage),
	}
}

func formulaViewInvalidRequest(message string) *dialect.Response {
	return &dialect.Response{
		Status: http.StatusBadRequest,
		Body:   wire.SFErrorResponse(wire.SFCodeInvalidRequest, message),
	}
}
