// packages/functions/src/shared/errorHandling.ts

/**
 * @fileoverview Shared Error Handling
 * @description Platform-aware error handling that throws in the correct format
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */

import * as v from 'valibot';
import { HttpsError } from 'firebase-functions/v2/https';

import type { ErrorCode } from '@donotdev/core/server';
import { EntityHookError } from '@donotdev/core/server';

/**
 * Custom error class for application-specific errors in functions.
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */
export class DoNotDevError extends Error {
  public readonly code: ErrorCode;
  public readonly details?: Record<string, any>;

  constructor(
    message: string,
    code: ErrorCode = 'unknown',
    details?: Record<string, any>
  ) {
    super(message);
    Object.setPrototypeOf(this, DoNotDevError.prototype);
    this.code = code;
    this.details = details;
    this.name = this.constructor.name;

    if (Error.captureStackTrace) {
      Error.captureStackTrace(this, this.constructor);
    }
  }

  public toString(): string {
    return `${this.name} [${this.code}]: ${this.message}`;
  }

  public toJSON(): object {
    return {
      name: this.name,
      code: this.code,
      message: this.message,
      details: this.details,
    };
  }
}

/**
 * Platform-aware error handling that throws in the correct format
 * @param error - The error to handle
 * @throws Platform-appropriate error
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */
export function handleError(error: unknown): never {
  console.error('Function error:', error);

  // Error classification logic (same for all platforms)
  let code: string;
  let message: string;
  let details: any;

  if (error instanceof DoNotDevError) {
    code = error.code as string;
    message = error.message;
    details = error.details;
  } else if (
    error &&
    typeof error === 'object' &&
    'issues' in error &&
    ((error instanceof Error && error.name === 'ValiError') ||
      (error as any).name === 'ValiError')
  ) {
    code = 'invalid-argument';
    message = 'Validation failed';
    details = { validationErrors: (error as any).issues };
  } else if (
    error &&
    typeof error === 'object' &&
    'type' in error &&
    ((error instanceof Error && error.name === 'EntityHookError') ||
      (error as any).name === 'EntityHookError')
  ) {
    const entityError = error as EntityHookError;
    switch (entityError.type) {
      case 'PERMISSION_DENIED':
        code = 'permission-denied';
        break;
      case 'NOT_FOUND':
        code = 'not-found';
        break;
      case 'ALREADY_EXISTS':
        code = 'already-exists';
        break;
      case 'VALIDATION_ERROR':
        code = 'invalid-argument';
        break;
      case 'NETWORK_ERROR':
        code = 'unavailable';
        break;
      default:
        code = 'internal';
    }
    message = entityError.message;
    details = undefined;
  } else {
    // Preserve original error message for debugging
    code = 'internal';
    message =
      error instanceof Error ? error.message : 'An unexpected error occurred';
    details = {
      originalError:
        error instanceof Error
          ? {
              name: error.name,
              message: error.message,
              stack: error.stack,
            }
          : String(error),
    };
  }

  // Platform-specific error throwing
  if (isFirebaseEnvironment()) {
    throwFirebaseError(code, message, details);
  } else if (isVercelEnvironment()) {
    throwVercelError(code, message, details);
  } else {
    // Fallback - just throw a regular error
    throw new Error(`${code}: ${message}`);
  }
}

/**
 * Detect if we're running in Firebase environment
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */
function isFirebaseEnvironment(): boolean {
  return (
    typeof process !== 'undefined' &&
    (process.env.FUNCTIONS_EMULATOR === 'true' ||
      process.env.FIREBASE_CONFIG !== undefined ||
      process.env.GCLOUD_PROJECT !== undefined)
  );
}

/**
 * Detect if we're running in Vercel environment
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */
function isVercelEnvironment(): boolean {
  return (
    typeof process !== 'undefined' &&
    (process.env.VERCEL === '1' || process.env.VERCEL_ENV !== undefined)
  );
}

/**
 * Throw Firebase-specific error
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */
function throwFirebaseError(
  code: string,
  message: string,
  details?: any
): never {
  // Static import - Firebase is always available in Firebase functions
  throw new HttpsError(code as any, message, details);
}

/**
 * Throw Vercel-specific error
 *
 * @version 0.0.1
 * @since 0.0.1
 * @author AMBROISE PARK Consulting
 */
function throwVercelError(code: string, message: string, details?: any): never {
  // For Vercel, we can throw a regular Error and let the API route handle it
  // Or we could create a custom VercelError class
  const error = new Error(message);
  (error as any).code = code;
  (error as any).details = details;
  throw error;
}
