import { createLogger } from "./logger";

/**
 * Summarize args for logging to avoid huge log entries.
 * - Arrays: show length only
 * - Long strings: truncate
 * - Objects with conversationHistory: summarize
 */
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function summarizeArgs(args: any[]): any[] {
  return args.map((arg) => summarizeValue(arg));
}

// eslint-disable-next-line @typescript-eslint/no-explicit-any
function summarizeValue(value: any, depth = 0): any {
  if (depth > 2) return "[...]";

  if (value === null || value === undefined) return value;

  if (Array.isArray(value)) {
    return `[Array(${value.length})]`;
  }

  if (typeof value === "string" && value.length > 100) {
    return value.substring(0, 100) + "...";
  }

  if (typeof value === "object") {
    // Summarize known large fields
    const summarized: Record<string, unknown> = {};
    for (const [key, val] of Object.entries(value)) {
      if (key === "conversationHistory" && Array.isArray(val)) {
        summarized[key] = `[${val.length} messages]`;
      } else if (key === "proposedChanges" && Array.isArray(val)) {
        // Log full proposedChanges for debugging AI issues
        summarized[key] = val.map((p) => summarizeValue(p, depth + 1));
      } else if (Array.isArray(val)) {
        summarized[key] = `[Array(${val.length})]`;
      } else if (typeof val === "object" && val !== null) {
        summarized[key] = summarizeValue(val, depth + 1);
      } else if (typeof val === "string" && val.length > 100) {
        summarized[key] = val.substring(0, 100) + "...";
      } else {
        summarized[key] = val;
      }
    }
    return summarized;
  }

  return value;
}

export function Logged(name: string) {
  const log = createLogger(name);

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  return function <T extends { new (...args: any[]): any }>(Constructor: T) {
    return class extends Constructor {
      // eslint-disable-next-line @typescript-eslint/no-explicit-any
      constructor(...args: any[]) {
        super(...args);

        // Return a Proxy that intercepts method calls lazily
        return new Proxy(this, {
          get(target, propKey, receiver) {
            const original = Reflect.get(target, propKey, receiver);

            if (typeof original !== "function" || propKey === "constructor") {
              return original;
            }

            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            const originalFn = original as (...args: any[]) => any;

            // eslint-disable-next-line @typescript-eslint/no-explicit-any
            return function (this: any, ...methodArgs: any[]) {
              const start = performance.now();
              const method = String(propKey);

              // Summarize args to avoid huge log entries
              log.debug(
                { method, args: summarizeArgs(methodArgs) },
                `${method} started`,
              );

              const logCompletion = (err?: unknown) => {
                const duration = Math.round(performance.now() - start);
                if (err) {
                  const message =
                    err instanceof Error ? err.message : String(err);
                  log.error(
                    { method, duration, error: message },
                    `${method} failed`,
                  );
                } else {
                  log.info({ method, duration }, `${method} completed`);
                }
              };

              try {
                const result = originalFn.apply(this, methodArgs);

                // Check if async - preserves sync/async nature of method
                if (result instanceof Promise) {
                  return result
                    .then((val) => {
                      logCompletion();
                      return val;
                    })
                    .catch((err) => {
                      logCompletion(err);
                      throw err;
                    });
                }

                // Synchronous completion
                logCompletion();
                return result;
              } catch (err) {
                logCompletion(err);
                throw err;
              }
            };
          },
        });
      }
    };
  };
}
