﻿# MessageHub + EIP Conversation Engine
## Full Flow Documentation — Current Working State + What to Change

---

## Table of Contents

1. [System Overview — Two Modes](#1-system-overview--two-modes)
2. [Mode 1 — Action Flow (Button Press → Approval → Follow-up)](#2-mode-1--action-flow)
3. [Mode 2 — Conversation Flow (Keyword → Chat)](#3-mode-2--conversation-flow)
4. [Webhook Entry — How Both Modes Are Routed](#4-webhook-entry--routing-decision)
5. [MessageHub Generator — How Templates Are Built and Sent](#5-messagehub-generator)
6. [EIP Engine — Phase-by-Phase Execution](#6-eip-engine-phases)
7. [Flow JSON Standard Format](#7-flow-json-standard-format)
8. [Current Working Code — What Each File Does](#8-current-working-code)
9. [What Is Working Correctly Today](#9-what-is-working-correctly-today)
10. [What Needs to Change — Targeted Fixes](#10-what-needs-to-change)

---

## 1. System Overview — Two Modes

The system works on two completely separate trigger paths. Both paths enter the same EIP engine but for different reasons.

```
PLATFORM (WhatsApp / Teams / Telegram / Slack)
            |
            |--- User sends TEXT message ("Hi", "Help")
            |         → MODE 2: Conversation Flow
            |
            |--- User presses BUTTON ("Accept", "Reject", "Forward")
                      → MODE 1: Action Flow
```

**Mode 1** is triggered when the user clicks an approval button on a template message that was sent by the MessageHub Generator. It processes the action, updates the database, and optionally sends a follow-up template or a simple confirmation message.

**Mode 2** is triggered when the user sends any free text. The EIP engine matches keywords and runs a configured conversation flow — asking questions, calling APIs, presenting choices, and responding based on user input.

Both modes are fully database-driven. No message content, no flow logic, and no routing is hardcoded in the application code.

---

## 2. Mode 1 — Action Flow

### Complete End-to-End Flow

```
STEP 1:  Business module calls POST /MessageHubGenerator/Generate
         Body: { EntityId: 1042 }
         Header: Login (UserId, TenantId)

STEP 2:  GenerateMessageHubEndpoint (SL) validates EntityId and Login
         Delegates to MessageHubGeneratorBLL.GenerateAsync()

STEP 3:  MessageHubGeneratorDAL runs GET_TEMPLATES_BY_ENTITYID
         Returns all active templates for EntityId 1042
         Each template has: TemplateId, Subject, BodyHtml, ActionType

STEP 4:  DAL runs GET_MESSAGE_HUB_PATCH_QUERY (large context query)
         Fetches ~120 columns from MUSER + MEMPLOYEE + OU + Party +
         Address + Location + City/State/Country + Latest Call + Visitor Pass
         Result: flat dictionary of { ColumnAlias → Value }

STEP 5:  DAL scans BodyHtml for ##PLACEHOLDER## patterns
         Filters dictionary to only used keys
         Replaces every ##KEY## with matched value from dictionary
         Builds MessageHubDTO with patched message, recipient, parameters

STEP 6:  MessageHubDTO has buttons in its body JSON:
         {
           "to": "##UserPrimaryMobile##",
           "body": "Call ##CallReferenceNumber## assigned to ##EmployeeName##",
           "buttons": [
             { "title": "Accept",  "payload": "ACCEPT_1042_8800"  },
             { "title": "Reject",  "payload": "REJECT_1042_8800"  },
             { "title": "Forward", "payload": "FORWARD_1042_8800" }
           ]
         }
         NOTE: payload format is   ACTION_TemplateId_ObjectId

STEP 7:  MessageHubGeneratorEngine routes by ActionType
         ActionType 7 → WhatsAppPlatform.SendWhatsappTemplate()
         Sends to Celitix or Meta API
         Message delivered to user's WhatsApp with 3 buttons

--- USER TAPS "ACCEPT" BUTTON ---

STEP 8:  WhatsApp sends webhook to your server
         POST /MessageHub/Webhook
         Payload: { Type: "button", Button: { Text: "Accept", Payload: "ACCEPT_1042_8800" }, From: "919876543210" }

STEP 9:  MessageHubWebhookBLL.MessageHubWebhook() receives it
         Calls IsButtonMessage() → confirms Type == "button"
         Extracts payload string: "ACCEPT_1042_8800"

STEP 10: TryBuildTemplateApprovalDTO() parses the payload
         Split by "_" → Action=ACCEPT, TemplateId=1042, ObjectId=8800
         Looks up MMAILTEMPLATE.FLOWCODE for TemplateId 1042
         FlowCode = "WA_TICKET_ASSIGNMENT"
         Builds TemplateApprovalDTO with real TemplateId, ObjectId, UserId=From

STEP 11: TemplateApprovalBLL.UpdateTemplateApproval() called
         Maps TemplateApprovalDTO → CallApprovalDTO
         Validates: CallId, UserId, ClientId, ApprovalStatus
         Calls DAL: TemplateApprovalDAL.AcceptTemplateApproval()
         SQL updates TCALL: STATUS=1, ACTIONTAKEN='ACCEPT'
         Returns rows affected

STEP 12: Builds EIPConversationDTO from CallApprovalDTO
         TenantId, ChannelType=WhatsApp, UserIdentifier=From,
         FlowCode="WA_TICKET_ASSIGNMENT",
         Message = serialized action payload

STEP 13: EIPConversationEngine.ProcessIncomingMessageAsync() called
         Phase 1: Normalize — parse ChannelType, UserIdentifier, TenantId
         Phase 2: Route — FlowCode already known, skip keyword matching
         Phase 3: Flow — load WA_TICKET_ASSIGNMENT flow from DB
                  Find EXTERNAL_ACTION step (WAIT_DECISION)
                  Read DecisionMap["ACCEPT"] → "UPDATE_STATUS_ACCEPT"
                  Execute step UPDATE_STATUS_ACCEPT (CAPABILITY)
         Phase 4: Capability — CALL_STATUS_UPDATE runs DB update (or confirms it)
         Phase 5: Action — optional LOG or follow-up trigger
         Phase 6: Response — send SuccessMessage back to user

STEP 14: Platform sends reply to user WhatsApp:
         "Ticket has been accepted successfully."
         OR triggers another template via MessageHubGeneratorBLL.GenerateAsync()
         if the flow JSON has a TRIGGER_MESSAGEHUB action step
```

### What Happens After Accept — Two Options

**Option A — Simple Confirmation Message**

The EIP flow step has a `SuccessMessage` field. After the capability executes, Phase 6 sends that message directly back to the user on the same platform. No new template is triggered.

```json
"UPDATE_STATUS_ACCEPT": {
  "StepType": "CAPABILITY",
  "CapabilityCode": "CALL_STATUS_UPDATE",
  "SuccessMessage": "Ticket has been accepted successfully.",
  "NextStepCode": "END"
}
```

**Option B — Trigger a Different Template**

The EIP flow step has a `TRIGGER_MESSAGEHUB` action that calls `MessageHubGeneratorBLL.GenerateAsync()` with a configured `EntityId`. This sends a completely new template to one or more recipients — for example notifying the requester that their ticket was accepted, or notifying the next approver.

```json
"UPDATE_STATUS_ACCEPT": {
  "StepType": "CAPABILITY",
  "CapabilityCode": "CALL_STATUS_UPDATE",
  "NextStepCode": "NOTIFY_REQUESTER"
},
"NOTIFY_REQUESTER": {
  "StepType": "ACTION",
  "ActionType": "TRIGGER_MESSAGEHUB",
  "EntityId": 1043,
  "NextStepCode": "END"
}
```

This is fully configurable from the flow JSON in the database. Adding a new follow-up notification for any approval scenario requires only a DB change — no code change.

---

## 3. Mode 2 — Conversation Flow

### Complete End-to-End Flow

```
STEP 1:  User sends "Hi" on WhatsApp

STEP 2:  WhatsApp webhook fires to your server
         POST /MessageHub/Webhook
         Payload: { Type: "text", Text: { Body: "Hi" }, From: "919876543210" }

STEP 3:  MessageHubWebhookBLL.MessageHubWebhook() receives it
         Calls IsButtonMessage() → returns false (not a button)
         Routes to EIPConversationBLL.HandleIncomingConversationAsync()
         Passes: UserIdentifier=From, TenantId, ChannelType=WhatsApp, Message="Hi"

STEP 4:  EIPConversationEngine — Phase 1: Normalize
         Builds NormalizedContext:
         { ChannelType: WhatsApp, UserIdentifier: "919876543210",
           TenantId: "client123", Message: "Hi" }

STEP 5:  EIPConversationEngine — Phase 2: Route
         No FlowCode known — must resolve from keywords
         Loads routing rules for TenantId + ChannelType from DB
         Matches "Hi" against Triggers keywords
         Match found → FlowCode = "GOODBOOKS_MAIN_FLOW", StartStepCode = "STEP_0"

STEP 6:  EIPConversationEngine — Phase 3: Flow Execution
         Load flow JSON for "GOODBOOKS_MAIN_FLOW" from DB
         Execute STEP_0 (MESSAGE): send "Hello! Welcome to GoodBooks ERP."
         AutoAdvance = true → move to STEP_1

STEP 7:  STEP_1 (INPUT): send "Please enter your contact number:"
         Engine saves session state:
         { UserId: "919876543210", FlowCode: "GOODBOOKS_MAIN_FLOW",
           CurrentStepCode: "STEP_1", Context: {} }
         Waits for next user message

STEP 8:  User replies "9876543210"
         Webhook fires again
         Engine loads session for this UserIdentifier + TenantId
         CurrentStepCode = "STEP_1"
         Validates input against Regex: ^[0-9]{10}$  → passes
         Stores in Context["PhoneNumber"] = "9876543210"
         Advances to STEP_2

STEP 9:  STEP_2 (ACTION CALL_API):
         Resolves URL: "http://localhost:5000/EIPContact/GetEIPContact/?PhoneNumber=9876543210"
         Calls API, stores response in Context["ContactResponse"]
         AutoAdvance → STEP_3

STEP 10: STEP_3 (CONDITIONAL):
         Checks Context["ContactResponse.Body[0].Name"]
         EXISTS → branch to STEP_4
         NOT EXISTS → branch to STEP_5

STEP 11: STEP_4 (MESSAGE):
         Resolves: "Hi {ContactResponse.Body[0].Name} — How are you?"
         Sends: "Hi Rajan Kumar — How are you?"
         AutoAdvance → STEP_6

STEP 12: STEP_6 (CHOICE):
         Sends: "What would you like to do today?"
         Options: Check Modules → 10, Visit Website → 20, Contact Support → 50
         Saves CurrentStepCode = "STEP_6"
         Waits for user choice

STEP 13: User replies "Check Modules"
         Engine routes to STEP_10
         Continues flow...

STEP 14: Eventually reaches STEP_99 (END)
         Sends "Thank you for using GoodBooks ERP. Have a great day!"
         Clears session state for this user
```

### Session State — How the Engine Remembers Where the User Is

For Mode 2 the engine must persist session between messages. Each user has one active session at a time per tenant and channel.

```
Session Key:   UserIdentifier + "_" + TenantId + "_" + ChannelType
Session Value: {
    FlowCode: "GOODBOOKS_MAIN_FLOW",
    CurrentStepCode: "STEP_1",
    Context: {
        "PhoneNumber": "9876543210",
        "ContactResponse": { ... }
    },
    StartedAt: "2024-01-15T10:00:00Z",
    LastActivityAt: "2024-01-15T10:02:00Z"
}
```

Session storage can be Redis, a DB table, or in-memory cache with sliding expiry. Recommended expiry: 30 minutes of inactivity.

Mode 1 does NOT use sessions. Each button press is fully self-contained — the payload carries everything needed.

---

## 4. Webhook Entry — Routing Decision

This is the single entry point for all incoming platform messages.

```
POST /MessageHub/Webhook
        |
        | MessageHubWebhookBLL.MessageHubWebhook()
        |
        v
For each message in webhookDTO.Messages:
        |
        |-- IsButtonMessage() ?
        |       |
        |      YES → Mode 1 (Action Flow)
        |       |    Parse payload: "ACCEPT_1042_8800"
        |       |    → Action=ACCEPT, TemplateId=1042, ObjectId=8800
        |       |    Lookup FLOWCODE from MMAILTEMPLATE where TemplateId=1042
        |       |    Call TemplateApprovalBLL.UpdateTemplateApproval()
        |       |    → EIP Engine with FlowCode
        |       |
        |      NO  → Mode 2 (Conversation Flow)
        |            Build EIPConversationDTO from message text
        |            Call EIPConversationBLL.HandleIncomingConversationAsync()
        |            → EIP Engine with keyword matching
        |
        v
Return result
```

Both paths pass through `EIPConversationEngine.ProcessIncomingMessageAsync()`. The difference is:

- Mode 1 arrives with a known `FlowCode` — routing phase is skipped, engine goes straight to flow execution and finds the `EXTERNAL_ACTION` step.
- Mode 2 arrives with message text — routing phase matches keywords to resolve `FlowCode`, then executes from `StartStepCode`.

---

## 5. MessageHub Generator

### Role in the System

The MessageHub Generator has one job: given an `EntityId`, find all configured templates for that entity, patch them with real data, and send them to the correct platform. It is called in two situations:

- Directly by a business module (any ERP screen) to trigger a notification
- By the EIP Action Engine (Phase 5) as a follow-up after an approval action

### How Templates Are Built

```
EntityId (e.g., 1042)
    |
    v
Query: GET_TEMPLATES_BY_ENTITYID
    Joins: MEVENTTYPE + MENTITY + MMAILTEMPLATE + MACTION +
           MEVENTTYPEACTION + MEVENTTYPEACTIONDETAIL
    Returns: All active template-action combinations for this EntityId
    |
    v
Query: GET_MESSAGE_HUB_PATCH_QUERY
    Joins: MUSER + MEMPLOYEE + MCONTACT + OU + PARTY + PARTYBRANCH +
           PERIOD + ADDRESS + LOCATION + CITY + STATE + COUNTRY +
           TCALL (OUTER APPLY latest) + TVISITORPASS (OUTER APPLY latest) +
           TVISITORPUNCH (OUTER APPLY latest)
    Result: ~120 column flat dictionary
    |
    v
Per template:
    1. Scan BodyHtml for ##PLACEHOLDER## using regex
    2. Filter dictionary to only keys used in this template
    3. Replace ##KEY## with dictionary value
    4. Extract recipient from patched JSON "to" field
       Fallback → UserPrimaryMobile → UserPrimaryMail
    5. Normalize mobile number to Indian format (91 + 10 digits)
    6. Build MessageHubDTO with all fields set
    |
    v
MessageHubGeneratorEngine routes by ActionType:
    0  → Postman (debug log only)
    7  → WhatsAppPlatform (Celitix or Meta)
    8  → TeamsPlatform (Webhook)
    9  → TelegramPlatform (Bot API)
    10 → SlackPlatform (Incoming Webhook)
    |
    v
Platform sends message
Failed messages → OutBox (Dapr) for async retry
```

### Template Body Format (Standard)

All templates in `MMAILTEMPLATE.BODYHTML` must follow this JSON format:

```json
{
  "to": "##UserPrimaryMobile##",
  "body": "Dear ##EmployeeName##, your call ##CallReferenceNumber## has been assigned to you.",
  "buttons": [
    { "title": "Accept",  "payload": "ACCEPT_##MailTemplateId##_##CallObjectId##"  },
    { "title": "Reject",  "payload": "REJECT_##MailTemplateId##_##CallObjectId##"  },
    { "title": "Forward", "payload": "FORWARD_##MailTemplateId##_##CallObjectId##" }
  ]
}
```

When the DAL patches this template, `##MailTemplateId##` becomes `1042` and `##CallObjectId##` becomes `8800`. The final button payloads are `ACCEPT_1042_8800` etc. — carrying everything the webhook needs to process the action.

Templates without buttons (simple notifications) omit the `buttons` array entirely. The EIP engine is not involved for those — they send and complete with no further interaction.

---

## 6. EIP Engine Phases

### Phase 1 — Channel Normalization

Input: raw webhook payload + LoginDTO

Output: NormalizedContext
- ChannelType (WhatsApp / Teams / Telegram / Slack / SMS / Postman)
- UserIdentifier (phone number or user ID on the platform)
- TenantId (from payload or LoginDTO)
- Message (the text or button payload)
- FlowCode (if already known — Mode 1 only)

Validations: payload not null, LoginDTO not null, TenantId resolvable, UserIdentifier valid.

### Phase 2 — Routing

Only executed for Mode 2. Mode 1 skips this phase as FlowCode is already known.

For Mode 2: loads routing rules from DB for TenantId + ChannelType. Matches incoming message text against keyword lists. Returns FlowCode and StartStepCode. If no match, uses DefaultFallbackMessage.

### Phase 3 — Flow Execution

Loads flow JSON from DB by FlowCode. Parses step dictionary. Executes from StartStepCode (Mode 2) or from the saved CurrentStepCode of the session (Mode 2 resume) or from the EXTERNAL_ACTION step (Mode 1).

Step types handled:
- MESSAGE — send text, optionally auto-advance
- INPUT — send prompt, wait for user reply, validate, store in Context
- ACTION (CALL_API) — call external HTTP API, store response in Context
- ACTION (TRIGGER_MESSAGEHUB) — call MessageHubGeneratorBLL.GenerateAsync()
- CONDITIONAL — evaluate Context value, branch by operator (EXISTS / EQUALS / IN)
- CHOICE — send options, wait for user selection, route to mapped step
- EXTERNAL_ACTION — for Mode 1: read DecisionMap, route by button action value
- CAPABILITY — delegate to Phase 4
- END — terminate flow, clear session

Context variable resolution in message text: `{ContextKey.Path}` is replaced with the stored value before sending.

### Phase 4 — Capability Execution

Executes named business logic registered in `MEIPCAPABILITY`. Examples:
- `CALL_STATUS_UPDATE` — updates TCALL status in DB
- `SEND_OTP` — generates and sends OTP
- `VALIDATE_OTP` — verifies entered OTP
- `RISK_ASSESS` — evaluates risk level for the context

Returns success/failure and optional output stored back into Context.

### Phase 5 — Action Execution

Executes system actions registered in `MEIPACTION`. Examples:
- `LOG_MESSAGE` — audit log entry
- `TRIGGER_MESSAGEHUB` — calls MessageHubGeneratorBLL.GenerateAsync(EntityId) to send a follow-up template
- `CALL_API` — HTTP call to external system
- `DB_UPDATE` — direct DB operation

The `TRIGGER_MESSAGEHUB` action type is the bridge that allows the EIP engine to re-enter the MessageHub Generator after an approval. This keeps both systems decoupled — the EIP engine does not know how messages are sent, and MessageHub does not know what flows are running.

### Phase 6 — Response Generation

Selects the correct channel handler by ChannelType. Builds the response payload. Delivers via the platform API.

For Mode 1: sends SuccessMessage from the flow step that just executed.
For Mode 2: sends whatever Message is configured on the current step.

Supported channel handlers: PostmanPlatform, WhatsAppPlatform, TeamsPlatform, TelegramPlatform, SlackPlatform, SMSPlatform, EmailPlatform.

---

## 7. Flow JSON Standard Format

All flows stored in the database use this format. Steps are a dictionary (object map) keyed by StepCode — never an array. This prevents duplicate key issues.

### Mode 1 Flow Example — WA_TICKET_ASSIGNMENT

```json
{
  "FlowCode": "WA_TICKET_ASSIGNMENT",
  "StartStepCode": "SEND_MESSAGE",
  "Steps": {
    "SEND_MESSAGE": {
      "StepCode": "SEND_MESSAGE",
      "StepType": "MESSAGE",
      "Message": "Your ticket has been sent for approval.",
      "NextStepCode": "WAIT_DECISION",
      "AutoAdvance": true
    },
    "WAIT_DECISION": {
      "StepCode": "WAIT_DECISION",
      "StepType": "EXTERNAL_ACTION",
      "DecisionMap": {
        "ACCEPT":  "UPDATE_STATUS_ACCEPT",
        "REJECT":  "UPDATE_STATUS_REJECT",
        "FORWARD": "UPDATE_STATUS_FORWARD"
      }
    },
    "UPDATE_STATUS_ACCEPT": {
      "StepCode": "UPDATE_STATUS_ACCEPT",
      "StepType": "CAPABILITY",
      "CapabilityCode": "CALL_STATUS_UPDATE",
      "SuccessMessage": "Ticket accepted successfully.",
      "NextStepCode": "NOTIFY_REQUESTER"
    },
    "UPDATE_STATUS_REJECT": {
      "StepCode": "UPDATE_STATUS_REJECT",
      "StepType": "CAPABILITY",
      "CapabilityCode": "CALL_STATUS_UPDATE",
      "SuccessMessage": "Ticket rejected.",
      "NextStepCode": "END"
    },
    "UPDATE_STATUS_FORWARD": {
      "StepCode": "UPDATE_STATUS_FORWARD",
      "StepType": "CAPABILITY",
      "CapabilityCode": "CALL_STATUS_UPDATE",
      "SuccessMessage": "Ticket forwarded to next approver.",
      "NextStepCode": "NOTIFY_NEXT_APPROVER"
    },
    "NOTIFY_REQUESTER": {
      "StepCode": "NOTIFY_REQUESTER",
      "StepType": "ACTION",
      "ActionType": "TRIGGER_MESSAGEHUB",
      "EntityId": 1043,
      "NextStepCode": "END"
    },
    "NOTIFY_NEXT_APPROVER": {
      "StepCode": "NOTIFY_NEXT_APPROVER",
      "StepType": "ACTION",
      "ActionType": "TRIGGER_MESSAGEHUB",
      "EntityId": 1044,
      "NextStepCode": "END"
    },
    "END": {
      "StepCode": "END",
      "StepType": "END"
    }
  }
}
```

### Mode 2 Flow Example — GOODBOOKS_MAIN_FLOW

```json
{
  "FlowCode": "GOODBOOKS_MAIN_FLOW",
  "StartStepCode": "WELCOME",
  "DefaultFallbackMessage": "Sorry, I did not understand. Type MAIN MENU to restart.",
  "Triggers": [
    {
      "Keywords": ["HI", "HELLO", "HEY", "START", "MAIN MENU"],
      "StartStepCode": "WELCOME"
    },
    {
      "Keywords": ["HELP", "SUPPORT"],
      "StartStepCode": "SUPPORT_MENU"
    }
  ],
  "Steps": {
    "WELCOME": {
      "StepCode": "WELCOME",
      "StepType": "MESSAGE",
      "Message": "Hello! Welcome to GoodBooks ERP.",
      "NextStepCode": "ASK_PHONE",
      "AutoAdvance": true
    },
    "ASK_PHONE": {
      "StepCode": "ASK_PHONE",
      "StepType": "INPUT",
      "Message": "Please enter your contact number:",
      "ContextKey": "PhoneNumber",
      "Validation": {
        "Required": true,
        "Regex": "^[0-9]{10}$",
        "ErrorMessage": "Please enter a valid 10-digit phone number."
      },
      "NextStepCode": "FETCH_CONTACT"
    },
    "FETCH_CONTACT": {
      "StepCode": "FETCH_CONTACT",
      "StepType": "ACTION",
      "ActionType": "CALL_API",
      "Message": "Fetching your details...",
      "ContextKey": "ContactResponse",
      "ApiConfig": {
        "Url": "http://localhost:5000/EIPContact/GetEIPContact/?PhoneNumber={PhoneNumber}",
        "Method": "GET",
        "Headers": {}
      },
      "NextStepCode": "CHECK_CONTACT",
      "AutoAdvance": true
    },
    "CHECK_CONTACT": {
      "StepCode": "CHECK_CONTACT",
      "StepType": "CONDITIONAL",
      "ConditionKey": "ContactResponse.Body[0].Name",
      "Conditions": [
        { "Operator": "EXISTS", "NextStepCode": "GREET_BY_NAME" }
      ],
      "DefaultNextStepCode": "GREET_GENERIC"
    },
    "GREET_BY_NAME": {
      "StepCode": "GREET_BY_NAME",
      "StepType": "MESSAGE",
      "Message": "Hi {ContactResponse.Body[0].Name}! How can I help you today?",
      "NextStepCode": "MAIN_MENU",
      "AutoAdvance": true
    },
    "GREET_GENERIC": {
      "StepCode": "GREET_GENERIC",
      "StepType": "MESSAGE",
      "Message": "Hi! How can I help you today?",
      "NextStepCode": "MAIN_MENU",
      "AutoAdvance": true
    },
    "MAIN_MENU": {
      "StepCode": "MAIN_MENU",
      "StepType": "CHOICE",
      "Message": "What would you like to do?",
      "Choices": {
        "Check Modules": "MODULES_LIST",
        "Contact Support": "SUPPORT_MENU",
        "Visit Website": "WEBSITE_REDIRECT"
      }
    },
    "MODULES_LIST": {
      "StepCode": "MODULES_LIST",
      "StepType": "MESSAGE",
      "Message": "Available modules:\n1. Accounting\n2. Inventory\n3. HR\n4. Sales",
      "NextStepCode": "MAIN_MENU",
      "AutoAdvance": false
    },
    "SUPPORT_MENU": {
      "StepCode": "SUPPORT_MENU",
      "StepType": "CHOICE",
      "Message": "Choose support type:",
      "Choices": {
        "Technical":      "TICKET_TECHNICAL",
        "Billing":        "TICKET_BILLING",
        "General":        "TICKET_GENERAL",
        "Main Menu":      "MAIN_MENU"
      }
    },
    "TICKET_TECHNICAL": {
      "StepCode": "TICKET_TECHNICAL",
      "StepType": "ACTION",
      "ActionType": "CALL_API",
      "Message": "A technical ticket has been created.",
      "NextStepCode": "MAIN_MENU"
    },
    "TICKET_BILLING": {
      "StepCode": "TICKET_BILLING",
      "StepType": "ACTION",
      "ActionType": "CALL_API",
      "Message": "A billing ticket has been created.",
      "NextStepCode": "MAIN_MENU"
    },
    "TICKET_GENERAL": {
      "StepCode": "TICKET_GENERAL",
      "StepType": "ACTION",
      "ActionType": "CALL_API",
      "Message": "A general inquiry ticket has been created.",
      "NextStepCode": "MAIN_MENU"
    },
    "END": {
      "StepCode": "END",
      "StepType": "END",
      "Message": "Thank you for using GoodBooks ERP. Have a great day!"
    }
  }
}
```

---

## 8. Current Working Code

### What Each File Does

**GenerateMessageHubEndpoint.cs (SL)**
FastEndpoints POST handler. Validates EntityId and Login header. Delegates to BLL. Returns wrapped result. Working correctly.

**MessageHubGeneratorQB.cs (DAL Queries)**
Three SQL queries: fetch templates by EntityId, insert to TMESSAGEHUB, large context patch query. The context query joins 15 tables and returns ~120 columns. Working correctly. The BODYHTML-as-JSON assumption needs documentation.

**MessageHubGeneratorEngine.cs (Engine)**
Routes MessageHubDTO by ActionType integer to the correct platform. Has Polly retry (3x exponential). Working correctly.

**MessageHubGeneratorBLL.cs (BLL)**
Orchestrates: fetch messages from DAL, loop and send via engine, collect results, publish failed to OutBox. Working correctly.

**MessageHubGeneratorDAL.cs (DAL)**
Core template processing: fetch templates, resolve payload, extract placeholders, patch, build DTOs. Working correctly. Mobile normalization is India-specific.

**MessageHubWebhookBLL.cs (Webhook BLL)**
Receives webhook, detects button vs text, maps to TemplateApprovalDTO, calls TemplateApprovalBLL. Currently skips non-button messages (needs to route to EIP for Mode 2). Has broken TemplateID=0 and TicketNumber=null issues.

**TemplateApprovalBLL.cs (Approval BLL)**
Maps DTO, validates, calls DAL for DB update, builds EIP payload, calls EIP engine. Working correctly except CancellationToken.None hardcoded and DAL return value ignored.

**TemplateApprovalDAL.cs (Approval DAL)**
Three methods: AcceptTemplateApproval, RejectTemplateApproval, ForwardTemplate. Each runs SQL and returns rows affected. Clean and correct.

**WhatsAppPlatform.cs**
Supports Celitix and Meta providers. Template and conversation modes. Dynamic parameters from DTO. Polly retry at HTTP level. Working correctly.

**TeamsPlatform.cs, TelegramPlatform.cs, SlackPlatform.cs**
Webhook-based sends. Template and conversation modes. Working correctly.

**PostmanPlatform.cs**
Debug/test only. Logs and returns success. No actual send. Working correctly.

**EmailPlatform.cs, SMSPlatform.cs**
Not wired into the Generator engine (no ActionType assigned). Work independently for conversation sends.

---

## 9. What Is Working Correctly Today

- MessageHub Generator end-to-end: EntityId → fetch templates → patch data → send to platform
- All five platforms (WhatsApp, Teams, Telegram, Slack, Postman) receive and send template messages
- WhatsApp dual-provider support (Celitix + Meta) with config flag switching
- Polly retry at engine level and HTTP level
- OutBox fallback for failed messages
- TemplateApprovalDAL correctly updates TCALL on Accept, Reject, Forward
- EIP engine phase architecture is correctly structured
- Flow JSON with EXTERNAL_ACTION + DecisionMap is the correct mechanism for button routing
- Both flow JSON formats for Mode 1 and Mode 2 are defined

---

## 10. What Needs to Change

### Fix 1 — Button Payload Must Carry TemplateId and ObjectId

**File: MessageHubGeneratorDAL.cs**

When building buttons in template JSON, the placeholder should resolve to:
```
ACCEPT_##MailTemplateId##_##CallObjectId##
```

After patching this becomes `ACCEPT_1042_8800`.

Also add `FLOWCODE` column to `MMAILTEMPLATE`:

```sql
ALTER TABLE MMAILTEMPLATE ADD FLOWCODE VARCHAR(100) NULL;
```

This column stores which EIP flow to execute when a button on this template is pressed.

---

### Fix 2 — Parse TemplateId and ObjectId from Webhook Payload

**File: MessageHubWebhookBLL.cs — TryBuildTemplateApprovalDTO()**

Replace the hardcoded values:

```csharp
// CURRENT (BROKEN)
TemplateID = 0,
TicketNumber = null,
FlowCode = "WA_TICKET_ASSIGNMENT",  // hardcoded
TenantId = -1399999958,             // hardcoded

// CHANGE TO
// payload = "ACCEPT_1042_8800"
var parts = payload.Split('_');
// parts[0] = action, parts[1] = templateId, parts[2] = objectId
int templateId = int.Parse(parts[1]);
int objectId = int.Parse(parts[2]);

// Look up FlowCode from DB
string flowCode = await _templateLookupDAL.GetFlowCodeByTemplateId(templateId);
string tenantId = loginDTO.ClientId.ToString();

TemplateID = templateId,
TicketNumber = objectId.ToString(),
FlowCode = flowCode,
TenantId = loginDTO.ClientId,
```

---

### Fix 3 — Route Non-Button Messages to EIP Conversation Engine

**File: MessageHubWebhookBLL.cs — MessageHubWebhook()**

```csharp
// CURRENT (BROKEN - skips text messages)
if (!IsButtonMessage(message, out var payload))
{
    _logger.LogInformation("Skipping non-actionable message");
    continue;  // <-- THIS DROPS MODE 2 MESSAGES
}

// CHANGE TO
if (!IsButtonMessage(message, out var payload))
{
    // Route to Mode 2 — Conversation Flow
    var eipDto = new EIPConversationDTO
    {
        TenantId = loginDTO.ClientId.ToString(),
        ChannelType = EIPChannelType.WhatsApp,
        UserIdentifier = message.From,
        Message = message.Text?.Body ?? string.Empty,
        FlowCode = null  // will be resolved by routing engine
    };
    await _eipConversationBLL.HandleIncomingConversationAsync(eipDto, loginDTO, ct);
    continue;
}
```

---

### Fix 4 — Validate DAL Rows Affected in TemplateApprovalBLL

**File: TemplateApprovalBLL.cs — ExecuteDalActionAsync()**

```csharp
// CURRENT (ignores result)
await _templateApprovalDAL.AcceptTemplateApproval(dalDto, loginDTO);

// CHANGE TO
var rowsAffected = await _templateApprovalDAL.AcceptTemplateApproval(dalDto, loginDTO);
if (rowsAffected == 0)
{
    _logger.LogWarning("AcceptTemplateApproval: zero rows updated for TicketNumber={TicketNumber}", dalDto.TicketNumber);
    throw new InvalidOperationException($"No record found for TicketNumber {dalDto.TicketNumber}");
}
```

---

### Fix 5 — Add TRIGGER_MESSAGEHUB Action Type to EIP Action Engine

**File: EIP Action Engine (Phase 5)**

Register a new action handler for `ActionType = "TRIGGER_MESSAGEHUB"`:

```csharp
case "TRIGGER_MESSAGEHUB":
    int entityId = actionStep.EntityId;
    await _messageHubGeneratorBLL.GenerateAsync(entityId, loginDTO, ct);
    break;
```

The flow JSON step carries the `EntityId` to pass:

```json
{
  "StepCode": "NOTIFY_REQUESTER",
  "StepType": "ACTION",
  "ActionType": "TRIGGER_MESSAGEHUB",
  "EntityId": 1043,
  "NextStepCode": "END"
}
```

This is the bridge that allows any approval flow to trigger any follow-up template without code changes.

---

### Fix 6 — Add Session Storage for Mode 2

Add a session table or Redis cache for conversation state:

```sql
CREATE TABLE TEIUSERSESSION (
    SESSIONID       UNIQUEIDENTIFIER DEFAULT NEWID() PRIMARY KEY,
    USERIDENTIFIER  VARCHAR(100) NOT NULL,
    TENANTID        VARCHAR(100) NOT NULL,
    CHANNELTYPE     INT NOT NULL,
    FLOWCODE        VARCHAR(100) NOT NULL,
    CURRENTSTEPCODE VARCHAR(100) NOT NULL,
    CONTEXTJSON     NVARCHAR(MAX) NULL,
    STARTEDAT       DATETIME NOT NULL DEFAULT GETDATE(),
    LASTACTIVITYAT  DATETIME NOT NULL DEFAULT GETDATE(),
    STATUS          INT NOT NULL DEFAULT 1
);

CREATE INDEX IX_TEIUSERSESSION_USER
    ON TEIUSERSESSION (USERIDENTIFIER, TENANTID, CHANNELTYPE)
    WHERE STATUS = 1;
```

Session expires after 30 minutes of inactivity. When a new trigger keyword arrives for a user with an active session, the old session is closed and a new one starts.

---

### Fix 7 — Unify Flow JSON to String StepCode Dictionary Format

All flows must use the dictionary format (Format 3). The flat array format with duplicate StepCode strings (Format 2) must not be used. The integer StepId format (Format 1) is only for Mode 2 flows and must be converted to string StepCode format for consistency.

Use a single parser in the EIP flow engine that handles the dictionary format only.

---

### Fix 8 — Remove All Hardcoded Values

| File | Hardcoded Value | Replace With |
|---|---|---|
| MessageHubWebhookBLL | TenantId = -1399999958 | loginDTO.ClientId |
| MessageHubWebhookBLL | FlowCode = "WA_TICKET_ASSIGNMENT" | DB lookup by TemplateId |
| MessageHubWebhookBLL | TemplateID = 0 | Parsed from button payload |
| MessageHubWebhookBLL | TicketNumber = null | Parsed from button payload |
| TemplateApprovalBLL | CancellationToken.None | Pass ct from caller |

---

### Fix 9 — Add FLOWCODE Column to MMAILTEMPLATE

```sql
ALTER TABLE MMAILTEMPLATE ADD FLOWCODE VARCHAR(100) NULL;
```

When a template has buttons, `FLOWCODE` must be set. The webhook uses this to know which EIP flow to execute when a button on this template is pressed. If `FLOWCODE` is null, the button press is treated as a simple approval with no follow-up flow.

---

## Summary Table

| # | What | File | Status |
|---|---|---|---|
| 1 | Button payload carries TemplateId + ObjectId | MessageHubGeneratorDAL | CHANGE NEEDED |
| 2 | Parse TemplateId/ObjectId from webhook payload | MessageHubWebhookBLL | CHANGE NEEDED |
| 3 | Route text messages to EIP conversation engine | MessageHubWebhookBLL | CHANGE NEEDED |
| 4 | Validate DAL rows affected | TemplateApprovalBLL | CHANGE NEEDED |
| 5 | TRIGGER_MESSAGEHUB action type in EIP engine | EIP Action Engine | ADD NEW |
| 6 | Session storage for Mode 2 | DB + EIP BLL | ADD NEW |
| 7 | Unify flow JSON to dictionary format | All flow JSON | STANDARDIZE |
| 8 | Remove all hardcoded TenantId / FlowCode | MessageHubWebhookBLL | CHANGE NEEDED |
| 9 | FLOWCODE column on MMAILTEMPLATE | DB migration | ADD NEW |
| — | MessageHub Generator core flow | All generator files | WORKING CORRECTLY |
| — | All platform sends (WhatsApp/Teams/Telegram/Slack) | Platform files | WORKING CORRECTLY |
| — | TemplateApprovalDAL Accept/Reject/Forward | TemplateApprovalDAL | WORKING CORRECTLY |
| — | EIP engine phase architecture | EIP Engine | WORKING CORRECTLY |
| — | WhatsApp dual-provider (Celitix + Meta) | WhatsAppPlatform | WORKING CORRECTLY |
| — | OutBox fallback for failed messages | MessageHubGeneratorBLL | WORKING CORRECTLY |