/**
 * Hypen Tagged Template Literal
 *
 * Enables single-file components with inline UI templates.
 *
 * The `hypen` tagged template preserves ${state.x} and ${item.x} bindings
 * instead of interpolating them, allowing you to write:
 *
 * @example
 * ```typescript
 * import { app, hypen, state } from "@hypen-space/core";
 *
 * export default app
 *   .defineState({ count: 0 })
 *   .onAction("increment", ({ state }) => {
 *     state.count += 1;
 *   })
 *   .ui(hypen`
 *     Column {
 *       Text("Count: ${state.count}")
 *       Button { Text("+") }
 *         .onClick("@actions.increment")
 *     }
 *   `);
 * ```
 */

/**
 * Creates a proxy that captures property access as a binding path string.
 *
 * When used in a template literal, the proxy's toString() returns the
 * full binding expression (e.g., "${state.user.name}").
 *
 * @example
 * ```typescript
 * const state = createBindingProxy('state');
 * `${state.user.name}` // Returns: "${state.user.name}"
 * ```
 */
function createBindingProxy(root: string): any {
  const handler: ProxyHandler<object> = {
    get(_, prop: string | symbol): any {
      // Handle Symbol.toPrimitive, toString, and valueOf for string conversion
      if (
        prop === Symbol.toPrimitive ||
        prop === "toString" ||
        prop === "valueOf"
      ) {
        return () => `\${${root}}`;
      }

      // Handle other Symbol properties (e.g., Symbol.toStringTag)
      if (typeof prop === "symbol") {
        return undefined;
      }

      // Handle JSON.stringify
      if (prop === "toJSON") {
        return () => `\${${root}}`;
      }

      // Chain to nested property: state.user -> state.user.name
      return createBindingProxy(`${root}.${prop}`);
    },

    // Support for `in` operator
    has() {
      return true;
    },

    // Support for Object.keys() - return empty to avoid enumeration issues
    ownKeys() {
      return [];
    },

    getOwnPropertyDescriptor() {
      return {
        configurable: true,
        enumerable: true,
      };
    },
  };

  return new Proxy({} as any, handler);
}

/**
 * Proxy for state bindings.
 *
 * Use in hypen templates to create reactive state bindings:
 *
 * @example
 * ```typescript
 * hypen`Text("Hello, ${state.user.name}")`
 * // Produces: Text("Hello, ${state.user.name}")
 * ```
 */
export const state: any = createBindingProxy("state");

/**
 * Proxy for item bindings in list iteration.
 *
 * Use inside List components to reference the current item:
 *
 * @example
 * ```typescript
 * hypen`
 *   List(@state.items) {
 *     Text("${item.name}: ${item.price}")
 *   }
 * `
 * ```
 */
export const item: any = createBindingProxy("item");

/**
 * Proxy for index in list iteration.
 *
 * Use inside List components to reference the current index:
 *
 * @example
 * ```typescript
 * hypen`
 *   List(@state.items) {
 *     Text("Item #${index}: ${item.name}")
 *   }
 * `
 * ```
 */
export const index: any = {
  [Symbol.toPrimitive]: () => "${index}",
  toString: () => "${index}",
  valueOf: () => "${index}",
  toJSON: () => "${index}",
};

/**
 * Tagged template literal for Hypen DSL templates.
 *
 * Works with the `state`, `item`, and `index` binding proxies to preserve
 * binding syntax in the output string.
 *
 * @example
 * ```typescript
 * import { hypen, state, item } from "@hypen-space/core";
 *
 * // Simple state binding
 * const t1 = hypen`Text("Count: ${state.count}")`;
 * // Result: 'Text("Count: ${state.count}")'
 *
 * // Nested state binding
 * const t2 = hypen`Text("Hello, ${state.user.profile.name}")`;
 * // Result: 'Text("Hello, ${state.user.profile.name}")'
 *
 * // List with item binding
 * const t3 = hypen`
 *   List(@state.products) {
 *     Text("${item.name} - $${item.price}")
 *   }
 * `;
 *
 * // Complex expressions (use regular JS interpolation for static values)
 * const title = "My App";
 * const t4 = hypen`Text("${title}: ${state.count}")`;
 * // Result: 'Text("My App: ${state.count}")'
 * ```
 *
 * @param strings - Template literal string parts
 * @param expressions - Interpolated expressions (proxies return binding strings)
 * @returns The template string with bindings preserved
 */
export function hypen(
  strings: TemplateStringsArray,
  ...expressions: unknown[]
): string {
  let result = strings[0];

  for (let i = 0; i < expressions.length; i++) {
    const expr = expressions[i];

    // Convert expression to string
    // Binding proxies will return "${state.x}" via their toString()
    result += String(expr);
    result += strings[i + 1]!;
  }

  return result!.trim();
}

/**
 * Type helper for defining state shape.
 * Use with state proxy for better IDE support in complex scenarios.
 *
 * @example
 * ```typescript
 * type MyState = { user: { name: string; age: number } };
 * const typedState = state as StateProxy<MyState>;
 * ```
 */
export type StateProxy<T> = {
  [K in keyof T]: T[K] extends object
    ? StateProxy<T[K]> & { toString(): string }
    : { toString(): string };
} & { toString(): string };

/**
 * Type helper for item proxy in lists.
 */
export type ItemProxy<T> = StateProxy<T>;
