// src/lisp/primitives/arithmetic.ts
// Arithmetic Primitives: +, -, *, /, mod, abs, floor, ceil, etc.

import type { SExpr } from '../SExpr';
import type { IPrimitiveDefinition } from './mccarthy';

/**
 * ADD - Sum numbers
 * (+ 1 2 3) → 6
 */
export const add: IPrimitiveDefinition = {
  name: '+',
  handler: async (args, evaluate) => {
    let sum = 0;
    for (const arg of args) {
      sum += await evaluate(arg) as number;
    }
    return sum;
  },
};

/**
 * SUBTRACT - Difference
 * (- 10 3) → 7
 * (- 5) → -5 (negation)
 */
export const subtract: IPrimitiveDefinition = {
  name: '-',
  handler: async (args, evaluate) => {
    if (args.length === 0) return 0;
    const first = await evaluate(args[0]) as number;
    if (args.length === 1) return -first;
    let result = first;
    for (let i = 1; i < args.length; i++) {
      result -= await evaluate(args[i]) as number;
    }
    return result;
  },
};

/**
 * MULTIPLY - Product
 * (* 2 3 4) → 24
 */
export const multiply: IPrimitiveDefinition = {
  name: '*',
  handler: async (args, evaluate) => {
    let product = 1;
    for (const arg of args) {
      product *= await evaluate(arg) as number;
    }
    return product;
  },
};

/**
 * DIVIDE - Quotient
 * (/ 12 3) → 4
 * (/ 5) → 0.2 (reciprocal)
 */
export const divide: IPrimitiveDefinition = {
  name: '/',
  handler: async (args, evaluate) => {
    if (args.length === 0) return 0;
    const first = await evaluate(args[0]) as number;
    if (args.length === 1) return 1 / first;
    let result = first;
    for (let i = 1; i < args.length; i++) {
      result /= await evaluate(args[i]) as number;
    }
    return result;
  },
};

/**
 * MODULO - Remainder
 * (mod 10 3) → 1
 */
export const mod: IPrimitiveDefinition = {
  name: 'mod',
  aliases: ['%', 'remainder'],
  handler: async (args, evaluate) => {
    const a = await evaluate(args[0]) as number;
    const b = await evaluate(args[1]) as number;
    return a % b;
  },
};

/**
 * ABS - Absolute value
 * (abs -5) → 5
 */
export const abs: IPrimitiveDefinition = {
  name: 'abs',
  handler: async (args, evaluate) => {
    const val = await evaluate(args[0]) as number;
    return Math.abs(val);
  },
};

/**
 * MIN - Minimum value
 * (min 3 1 4 1 5) → 1
 */
export const min: IPrimitiveDefinition = {
  name: 'min',
  handler: async (args, evaluate) => {
    const values = await Promise.all(args.map(a => evaluate(a))) as number[];
    return Math.min(...values);
  },
};

/**
 * MAX - Maximum value
 * (max 3 1 4 1 5) → 5
 */
export const max: IPrimitiveDefinition = {
  name: 'max',
  handler: async (args, evaluate) => {
    const values = await Promise.all(args.map(a => evaluate(a))) as number[];
    return Math.max(...values);
  },
};

/**
 * FLOOR - Round down
 * (floor 3.7) → 3
 */
export const floor: IPrimitiveDefinition = {
  name: 'floor',
  handler: async (args, evaluate) => {
    return Math.floor(await evaluate(args[0]) as number);
  },
};

/**
 * CEIL - Round up
 * (ceil 3.2) → 4
 */
export const ceil: IPrimitiveDefinition = {
  name: 'ceil',
  aliases: ['ceiling'],
  handler: async (args, evaluate) => {
    return Math.ceil(await evaluate(args[0]) as number);
  },
};

/**
 * ROUND - Round to nearest
 * (round 3.5) → 4
 */
export const round: IPrimitiveDefinition = {
  name: 'round',
  handler: async (args, evaluate) => {
    return Math.round(await evaluate(args[0]) as number);
  },
};

/**
 * SQRT - Square root
 * (sqrt 16) → 4
 */
export const sqrt: IPrimitiveDefinition = {
  name: 'sqrt',
  handler: async (args, evaluate) => {
    return Math.sqrt(await evaluate(args[0]) as number);
  },
};

/**
 * POW - Exponentiation
 * (pow 2 10) → 1024
 */
export const pow: IPrimitiveDefinition = {
  name: 'pow',
  aliases: ['expt', '**'],
  handler: async (args, evaluate) => {
    const base = await evaluate(args[0]) as number;
    const exp = await evaluate(args[1]) as number;
    return Math.pow(base, exp);
  },
};

/**
 * EXP - e^x
 * (exp 1) → 2.718...
 */
export const exp: IPrimitiveDefinition = {
  name: 'exp',
  handler: async (args, evaluate) => {
    return Math.exp(await evaluate(args[0]) as number);
  },
};

/**
 * LOG - Natural logarithm
 * (log 2.718) → ~1
 */
export const log: IPrimitiveDefinition = {
  name: 'log',
  handler: async (args, evaluate) => {
    return Math.log(await evaluate(args[0]) as number);
  },
};

/**
 * LOG10 - Base-10 logarithm
 * (log10 100) → 2
 */
export const log10: IPrimitiveDefinition = {
  name: 'log10',
  handler: async (args, evaluate) => {
    return Math.log10(await evaluate(args[0]) as number);
  },
};

/**
 * SIN, COS, TAN - Trigonometric functions
 */
export const sin: IPrimitiveDefinition = {
  name: 'sin',
  handler: async (args, evaluate) => Math.sin(await evaluate(args[0]) as number),
};

export const cos: IPrimitiveDefinition = {
  name: 'cos',
  handler: async (args, evaluate) => Math.cos(await evaluate(args[0]) as number),
};

export const tan: IPrimitiveDefinition = {
  name: 'tan',
  handler: async (args, evaluate) => Math.tan(await evaluate(args[0]) as number),
};

/**
 * RANDOM - Random number
 * (random) → 0.0-1.0
 * (random 10) → 0-9 (integer)
 */
export const random: IPrimitiveDefinition = {
  name: 'random',
  handler: async (args, evaluate) => {
    if (args.length === 0) return Math.random();
    const max = await evaluate(args[0]) as number;
    return Math.floor(Math.random() * max);
  },
};

/**
 * All arithmetic primitives
 */
export const arithmeticPrimitives: IPrimitiveDefinition[] = [
  add,
  subtract,
  multiply,
  divide,
  mod,
  abs,
  min,
  max,
  floor,
  ceil,
  round,
  sqrt,
  pow,
  exp,
  log,
  log10,
  sin,
  cos,
  tan,
  random,
];
