/**
 * JSONFIRST TypeScript SDK v1.0
 * Universal Intent Protocol
 * https://jsonfirst.com/developers
 *
 * Install: npm install jsonfirst  (or copy this file directly)
 * Requires: TypeScript ≥4.7 or JavaScript (ES2020+)
 *
 * @example
 * import { JSONFIRSTClient } from "./jsonfirst";
 * const client = new JSONFIRSTClient({ apiKey: "jf_xxxx" });
 * const result = await client.convert("Send email to Marie about Thursday meeting");
 * console.log(result.firstJDON?.action.normalized); // "send"
 */

// ── Types ─────────────────────────────────────────────────────────────────────

export type ExecutionMode =
  | "ANTI_CREDIT_WASTE_V2"   // 0 LLM calls — pure local parsing (default)
  | "EXPRESS_ROUTE"          // Ultra-low latency, optimized for real-time
  | "STRICT_PROTOCOL"        // Temperature=0, maximum validation
  | "PERFORMANCE_MAX";       // Deep analysis, full confidence scoring

export interface JDONAction {
  raw: string;
  normalized: string;
}

export interface JDONObject {
  type: string;
  raw_tokens: string[];
  category?: string;
  description?: string;
}

export interface JDONDomain {
  primary: string;
  secondary?: string;
  confidence?: number;
}

/** A single JSON Data Object of Nature — the atomic unit of JSONFIRST. */
export interface JDON {
  jdon_id: string;
  confidence: number;
  action: JDONAction;
  object: JDONObject;
  domain: JDONDomain;
  constraints: Record<string, unknown>;
}

export interface AutocorrectEntry {
  from: string;
  to: string;
  type: "spelling" | "grammar" | "abbreviation";
}

export interface JSONFIRSTInput {
  raw_text: string;
  cleaned_text: string;
  language: string;
  autocorrect: {
    enabled: boolean;
    confidence: number;
    corrections: AutocorrectEntry[];
  };
}

export interface JSONFIRSTExecution {
  parsable: boolean;
  executable: boolean;
  mode: string;
  state: "DRAFT" | "VALIDATED" | "EXECUTION_REQUESTED" | "EXECUTED_SUCCESS" | "ERROR";
  on_success: string;
  on_error: string;
}

export interface JSONFIRSTExecutionMode {
  name: ExecutionMode;
  enforced: boolean;
  prefix?: string;
  status_message?: string;
}

/** Full response from the JSONFIRST API. */
export interface JSONFIRSTResponseData {
  spec: string;
  version: string;
  engine: string;
  input: JSONFIRSTInput;
  jdons: JDON[];
  tokens: {
    raw: string[];
    normalized: Record<string, string>;
  };
  execution: JSONFIRSTExecution;
  execution_mode: JSONFIRSTExecutionMode;
}

/** Enriched response wrapper with convenience helpers. */
export class JSONFIRSTResponse implements JSONFIRSTResponseData {
  spec: string;
  version: string;
  engine: string;
  input: JSONFIRSTInput;
  jdons: JDON[];
  tokens: { raw: string[]; normalized: Record<string, string> };
  execution: JSONFIRSTExecution;
  execution_mode: JSONFIRSTExecutionMode;

  constructor(data: JSONFIRSTResponseData) {
    Object.assign(this, data);
    this.spec = data.spec;
    this.version = data.version;
    this.engine = data.engine;
    this.input = data.input;
    this.jdons = data.jdons;
    this.tokens = data.tokens;
    this.execution = data.execution;
    this.execution_mode = data.execution_mode;
  }

  /** The primary JDON extracted from the intent. */
  get firstJDON(): JDON | null {
    return this.jdons[0] ?? null;
  }

  /** Whether the intent can be directly executed by an agent. */
  get isExecutable(): boolean {
    return this.execution.executable;
  }

  /** Active execution mode name. */
  get mode(): ExecutionMode {
    return this.execution_mode.name;
  }

  /** Serializable plain object. */
  toJSON(): JSONFIRSTResponseData {
    return { ...this };
  }
}

export class JSONFIRSTError extends Error {
  constructor(
    public readonly statusCode: number,
    message: string,
    public readonly body?: string
  ) {
    super(`JSONFIRST API error ${statusCode}: ${message}`);
    this.name = "JSONFIRSTError";
  }
}

export interface ConvertOptions {
  /** Execution mode (default: ANTI_CREDIT_WASTE_V2 — 0 LLM calls). */
  mode?: ExecutionMode;
  /** Input language hint ("fr" or "en"). Default: "fr". */
  language?: string;
}

export interface JSONFIRSTClientOptions {
  /** Your JSONFIRST API key (from https://jsonfirst.com/app). */
  apiKey: string;
  /** Base URL (default: https://jsonfirst.com). */
  baseUrl?: string;
  /** Request timeout in ms (default: 30000). */
  timeout?: number;
}

// ── Client ────────────────────────────────────────────────────────────────────

/**
 * Official TypeScript/JavaScript SDK for the JSONFIRST Universal Intent Protocol v3.0.
 *
 * Converts natural language intentions into structured, executable JSON objects (JDONs).
 * Supports 4 execution modes including ANTI_CREDIT_WASTE_V2 (0 LLM calls, zero API cost).
 *
 * Works in Node.js, browsers, Deno, Bun, and edge runtimes (uses native fetch).
 *
 * @example
 * const client = new JSONFIRSTClient({ apiKey: "jf_xxxx" });
 *
 * // Basic usage
 * const result = await client.convert("Deploy backend to production");
 * console.log(result.firstJDON?.action.normalized); // "deploy"
 *
 * // Pipeline usage
 * const jdon = await client.getJDON("Create support ticket for billing issue");
 * if (jdon && jdon.confidence > 0.8) {
 *   await executeAction(jdon);
 * }
 */
export class JSONFIRSTClient {
  private readonly apiKey: string;
  private readonly baseUrl: string;
  private readonly timeout: number;
  private readonly sdkVersion = "1.0.0";

  constructor(options: JSONFIRSTClientOptions) {
    this.apiKey = options.apiKey;
    this.baseUrl = (options.baseUrl ?? "https://jsonfirst.com").replace(/\/$/, "");
    this.timeout = options.timeout ?? 30_000;
  }

  /**
   * Convert natural language intent to a structured JDON object.
   *
   * This is the core method of the JSONFIRST protocol.
   * By default, uses ANTI_CREDIT_WASTE_V2 mode — zero LLM calls,
   * pure deterministic parsing. No API credit consumed on your LLM provider.
   *
   * @param text - The natural language intent to convert.
   * @param options - Execution options.
   * @returns Typed JSONFIRSTResponse with JDON objects.
   *
   * @example
   * const result = await client.convert("Send weekly report to team every Monday at 9am");
   * const jdon = result.firstJDON!;
   * // jdon.action.normalized → "send"
   * // jdon.domain.primary → "scheduling"
   * // jdon.constraints.frequency → "weekly"
   * // jdon.constraints.time → "09:00"
   */
  async convert(text: string, options: ConvertOptions = {}): Promise<JSONFIRSTResponse> {
    const { mode = "ANTI_CREDIT_WASTE_V2", language = "fr" } = options;

    const controller = new AbortController();
    const timer = setTimeout(() => controller.abort(), this.timeout);

    try {
      const response = await fetch(`${this.baseUrl}/api/jsonfirst`, {
        method: "POST",
        headers: {
          "Content-Type": "application/json",
          Authorization: `Bearer ${this.apiKey}`,
          "X-SDK-Version": `typescript/${this.sdkVersion}`,
        },
        body: JSON.stringify({ text, mode, language }),
        signal: controller.signal,
      });

      if (!response.ok) {
        const body = await response.text().catch(() => "");
        throw new JSONFIRSTError(response.status, response.statusText, body);
      }

      const data = (await response.json()) as JSONFIRSTResponseData;
      return new JSONFIRSTResponse(data);
    } finally {
      clearTimeout(timer);
    }
  }

  /** Convert with STRICT_PROTOCOL mode (temperature=0, maximum validation). */
  async convertStrict(text: string, language = "fr"): Promise<JSONFIRSTResponse> {
    return this.convert(text, { mode: "STRICT_PROTOCOL", language });
  }

  /** Convert with EXPRESS_ROUTE mode (ultra-low latency, optimized for production). */
  async convertFast(text: string, language = "fr"): Promise<JSONFIRSTResponse> {
    return this.convert(text, { mode: "EXPRESS_ROUTE", language });
  }

  /**
   * Convenience: returns the first JDON directly.
   *
   * @example
   * const jdon = await client.getJDON("Archive all invoices from 2024");
   * console.log(jdon?.action.normalized);     // "archive"
   * console.log(jdon?.constraints.year);      // "2024"
   * console.log(jdon?.confidence.toFixed(2)); // "0.94"
   */
  async getJDON(
    text: string,
    options: ConvertOptions = {}
  ): Promise<JDON | null> {
    const result = await this.convert(text, options);
    return result.firstJDON;
  }

  /**
   * Batch convert multiple intents in parallel.
   *
   * @example
   * const results = await client.convertBatch([
   *   "Send invoice to client@example.com",
   *   "Schedule meeting for next Tuesday at 3pm",
   *   "Create Jira ticket for login bug",
   * ]);
   */
  async convertBatch(
    texts: string[],
    options: ConvertOptions = {}
  ): Promise<JSONFIRSTResponse[]> {
    return Promise.all(texts.map((text) => this.convert(text, options)));
  }
}

// ── Default export ─────────────────────────────────────────────────────────────
export default JSONFIRSTClient;

// ── Quick usage example ────────────────────────────────────────────────────────
// To run: npx ts-node jsonfirst.ts
async function example() {
  const client = new JSONFIRSTClient({ apiKey: "your_jsonfirst_api_key" });

  // Single conversion (0 LLM calls)
  const result = await client.convert(
    "Send weekly performance report to the marketing team every Monday at 9am"
  );
  const jdon = result.firstJDON!;
  console.log("Action:", jdon.action.normalized);         // "send"
  console.log("Confidence:", jdon.confidence.toFixed(2)); // "0.92"
  console.log("Mode:", result.mode);                      // "ANTI_CREDIT_WASTE_V2"

  // Batch conversion
  const batch = await client.convertBatch([
    "Deploy backend to production",
    "Create support ticket for billing issue",
    "Archive Q4 2024 invoices",
  ]);
  batch.forEach((r) => console.log(r.firstJDON?.action.normalized));
  // "deploy", "create", "archive"
}
