# @acorex/modules/identifier-management

Pattern-based **Identifier** infrastructure for ACoreX (UI-first).  
Supports **dynamic, DI-driven tokens**, mockable in **IndexedDB**, and ready to switch to **API** later.

> ℹ️ Backend services are **not** implemented here. Where needed, this README points to the backend contracts that should be implemented later.

---

## ✅ Scope (UI-first)

- Provide **UI-layer services** to define, preview, and generate identifiers from **patterns**.
- Tokens are **DI services** (async-capable) and can be added like **plugins**.
- Works in **Mock mode** (IndexedDB/Dexie). Can switch to **API mode** via connectivity.
- Admin can define **Rules** (pattern + scopes + reset policy) and reuse them anywhere.

---

## 🧱 High-level Architecture

```
[UI Forms] ──▶ AXPIdentifierService (Port)
                ├─ adapter: ApiIdentifierProvider (@acorex/connectivity/api in future)
                └─ adapter: MockIdentifierProvider (@acorex/connectivity/mock)

 Token Registry (DI, multi-provider)
   ├─ Built-in tokens (date, seq, field, const, tenant, entity, check)
   ├─ Custom tokens via plugins (slug, base36, lookup:*, etc.)
   └─ Priority/override

 Pattern Engine
   ├─ compile(pattern) -> tokens
   └─ evaluate(tokens, ctx) -> string  (async, ordered)

 Sequence Provider
   ├─ mock in IndexedDB (Hi/Lo optional)
   └─ future: backend authoritative

 Rule Store (UI)
   ├─ mock store in IndexedDB
   └─ future: CRUD via backend API
```

---

## 📦 Public API (UI)

### 1) Requests & Results

```ts
export interface AXPIdentifierRequest {
  entity: string; // e.g., 'AXPInvoice'
  tenantId?: string;
  appId?: string;
  mode: 'preview' | 'commit'; // preview = no sequence consumption
  payload?: Record<string, any>; // fields for tokens (e.g. { worksite: 'HQ' })
}

export interface AXPIdentifierResult {
  code: string;
  ruleId: string;
  scopeHash: string;
  consumed: boolean; // true if commit consumed sequence
  meta?: Record<string, any>;
}
```

### 2) Rule Definition (UI)

```ts
export interface AXPIdentifierRule {
  id: string;
  name: string;
  entity: string; // e.g., 'AXPInvoice'
  pattern: string; // e.g., 'INV-{date:YYYYMM}-{tenant}-{seq:6}'
  enabled: boolean;
  priority: number;
  scopeConfig: {
    byTenant?: boolean;
    byApp?: boolean;
    period?: 'none' | 'daily' | 'monthly' | 'yearly' | { format: string };
    customKeys?: string[]; // keys read from request.payload
    resetPolicy?: 'never' | 'daily' | 'monthly' | 'yearly' | { format: string };
    minValue?: number; // sequence min (usually 0)
    blockSize?: number; // Hi/Lo block size (0 to disable)
  };
}
```

### 3) Main Port (UI)

```ts
export abstract class AXPIdentifierService {
  abstract generate(req: AXPIdentifierRequest): Promise<AXPIdentifierResult>;
  abstract peek(req: AXPIdentifierRequest): Promise<AXPIdentifierResult>;
  abstract validate(code: string, entity: string, tenantId?: string): Promise<boolean>;
}
```

---

## 🔌 Connectivity Adapters (UI)

Two providers implement `AXPIdentifierService`:

```ts
export interface AXPIdentifierProvider {
  generate(req: AXPIdentifierRequest): Promise<AXPIdentifierResult>;
  peek(req: AXPIdentifierRequest): Promise<AXPIdentifierResult>;
  validate(code: string, entity: string, tenantId?: string): Promise<boolean>;
}
```

- **MockIdentifierProvider** (included): Uses IndexedDB/Dexie for rules, sequences, and logs.
- **ApiIdentifierProvider** (placeholder): Calls backend endpoints (to be implemented later).

A simple **facade** chooses provider by connectivity mode:

---

## 🧩 Token System (DI + async)

### Token Handler

```ts
export interface AXPatternToken {
  name: string; // "date", "seq", "field", ...
  args: string[]; // token args split by ':'
  raw: string; // "{...}"
  index: number; // position inside pattern
}

export interface AXEvalContext {
  pattern: string;
  request: AXPIdentifierRequest;
  cache: Map<string, any>; // per-request memoization
  now: Date;
  services: AXEvalServices;
}

export interface AXEvalServices {
  sequence: AXSequenceProvider; // required by seq-like tokens
  checksum: AXChecksumProvider; // checksum helpers
  lookup?: AXLookupProvider; // optional
  policy?: AXPolicyProvider; // optional
  clock?: AXClockProvider; // optional
}

export interface AXTokenHandler {
  readonly name: string; // token name
  readonly priority?: number; // higher wins on conflict
  validate?(token: AXPatternToken): void | never;
  eval(token: AXPatternToken, ctx: AXEvalContext): Promise<string> | string;
  meta?: {
    title?: string;
    description?: string;
    category?: string;
    examples?: string[];
    argHints?: string[];
    version?: string;
  };
}
```

### Token Registry (multi-provider friendly)

```ts
export interface AXTokenRegistry {
  register(handler: AXTokenHandler): void;
  unregister(name: string): void;
  get(name: string): AXTokenHandler | undefined;
  list(): AXTokenHandler[];
}
```

> You can expose the registry via an Angular `InjectionToken<AXTokenRegistry>` and register handlers using multi-providers, so plugins can add tokens without touching core.

---

## 🧠 Pattern Engine

```ts
export interface AXCompiledPattern {
  pattern: string;
  tokens: AXPatternToken[];
}

export interface AXPatternEngine {
  compile(pattern: string): AXCompiledPattern; // parse + validate via registry
  evaluate(compiled: AXCompiledPattern, ctx: AXEvalContext): Promise<string>; // async ordered eval
}
```

**Behavior:**

- Ordered evaluation (left → right) so tokens like `{check:*}` can compute based on “string so far”.
- The engine updates `ctx.cache.set('__acc__', builtSoFar)` before each token to support checksum tokens.

---

## 🔁 Sequence & Utilities (UI contracts)

```ts
export interface AXSequenceProvider {
  next(scopeHash: string, options?: { pad?: number; consume?: boolean }): Promise<number>;
}

export interface AXChecksumProvider {
  compute(algo: 'luhn' | 'mod11', input: string): string;
}

export interface AXLookupProvider {
  get(key: string, params?: Record<string, any>): Promise<string | undefined>;
}

export interface AXPolicyProvider {
  isAllowed(tokenName: string, request: AXPIdentifierRequest): boolean;
}

export interface AXClockProvider {
  now(): Date;
}
```

> In UI, we provide **mock implementations**. Later, when the backend is ready, the API provider will **delegate** these to the server.

---

## 💾 Mock Storage (IndexedDB)

Dexie schema for UI mock mode:

```ts
import Dexie, { Table } from 'dexie';

export interface AXRuleRow extends AXPIdentifierRule {}
export interface AXSeqRow {
  id: string; // `${ruleId}:${scopeHash}`
  ruleId: string;
  scopeHash: string;
  currentValue: number; // last consumed value
  hi?: number; // Hi/Lo
  next?: number;
  lastResetKey?: string; // e.g. '202508' for monthly
}
export interface AXLogRow {
  id: string; // uuid
  entity: string;
  tenantId?: string;
  code: string;
  createdAt: string; // ISO
}

export class AXPIdentifierDB extends Dexie {
  rules!: Table<AXRuleRow, string>;
  sequences!: Table<AXSeqRow, string>;
  logs!: Table<AXLogRow, string>;
  constructor() {
    super('ACoreXIdentifier');
    this.version(1).stores({
      rules: 'id, entity, enabled, priority',
      sequences: 'id, ruleId, scopeHash',
      logs: 'id, code, entity, tenantId',
    });
  }
}
```

---
