/**
 * Temporal Workflow Integration
 *
 * Main export for API routes to interact with Temporal workflows.
 * Provides helper functions for common workflow operations.
 *
 * @module lib/temporal
 *
 * @example
 * // Usage in API route:
 * import { startBookingWorkflow } from '@/lib/temporal';
 *
 * export async function POST(req: Request) {
 *   const data = await req.json();
 *   const workflowId = await startBookingWorkflow(data);
 *   return Response.json({ workflowId });
 * }
 */

import {
  getClient,
  getWorkflowClient,
  getConfig,
  getClientState,
  isHealthy,
  disconnect,
  reconnect,
} from '@/temporal/client';
import type { WorkflowHandle } from '@temporalio/client';
import { logger } from '@/lib/logger';

// ============================================================================
// Types
// ============================================================================

/**
 * Booking workflow input data
 */
export interface BookingInput {
  /** Unique booking identifier */
  bookingId: string;
  /** Customer email address */
  email: string;
  /** Customer full name */
  fullName: string;
  /** Customer phone number */
  telephone: string;
  /** Appointment date (ISO string) */
  appointmentDate: string;
  /** Appointment time (HH:mm format) */
  appointmentTime: string;
  /** Meeting duration in minutes */
  duration: string;
  /** Customer's timezone (IANA format) */
  timezone: string;
  /** Language preference */
  language: 'ar' | 'en';
  /** Special requirements or notes */
  specialRequirements?: string;
  /** Organization ID for multi-tenant support */
  organizationId?: string;
}

/**
 * Stripe webhook event data for payment workflows
 */
export interface StripeEvent {
  /** Stripe event ID */
  id: string;
  /** Event type (e.g., 'checkout.session.completed') */
  type: string;
  /** API version */
  api_version: string | null;
  /** Event creation timestamp */
  created: number;
  /** Event data object */
  data: {
    object: Record<string, unknown>;
    previous_attributes?: Record<string, unknown>;
  };
  /** Livemode flag */
  livemode: boolean;
  /** Pending webhooks count */
  pending_webhooks: number;
  /** Request information */
  request: {
    id: string | null;
    idempotency_key: string | null;
  } | null;
}

/**
 * Workflow execution status
 */
export interface WorkflowStatus {
  /** Workflow ID */
  workflowId: string;
  /** Run ID */
  runId: string;
  /** Current status */
  status: 'RUNNING' | 'COMPLETED' | 'FAILED' | 'CANCELLED' | 'TERMINATED' | 'TIMED_OUT' | 'CONTINUED_AS_NEW' | 'UNKNOWN';
  /** Start time */
  startTime: Date | null;
  /** Close time (if completed) */
  closeTime: Date | null;
  /** Workflow result (if completed) */
  result?: unknown;
  /** Error message (if failed) */
  error?: string;
}

/**
 * Signal payload types
 */
export interface SignalPayload {
  [key: string]: unknown;
}

/**
 * Common workflow options
 */
export interface WorkflowOptions {
  /** Custom task queue (overrides default) */
  taskQueue?: string;
  /** Workflow execution timeout in seconds */
  workflowExecutionTimeout?: number;
  /** Workflow run timeout in seconds */
  workflowRunTimeout?: number;
  /** Workflow task timeout in seconds */
  workflowTaskTimeout?: number;
  /** Search attributes for visibility */
  searchAttributes?: Record<string, unknown>;
  /** Memo for additional metadata */
  memo?: Record<string, unknown>;
  /** Retry policy */
  retry?: {
    maximumAttempts?: number;
    initialInterval?: string;
    maximumInterval?: string;
    backoffCoefficient?: number;
    nonRetryableErrorTypes?: string[];
  };
}

// ============================================================================
// Workflow ID Generation
// ============================================================================

/**
 * Generate a unique workflow ID with prefix and timestamp
 */
function generateWorkflowId(prefix: string, identifier?: string): string {
  const timestamp = Date.now().toString(36);
  const random = Math.random().toString(36).substring(2, 8);
  const id = identifier ? `${identifier}-` : '';
  return `${prefix}-${id}${timestamp}-${random}`;
}

// ============================================================================
// Helper Functions
// ============================================================================

/**
 * Start a booking workflow for demo appointment management
 *
 * @param data - Booking input data
 * @param options - Optional workflow configuration
 * @returns Workflow ID
 *
 * @example
 * const workflowId = await startBookingWorkflow({
 *   bookingId: 'bk_123',
 *   email: 'customer@example.com',
 *   fullName: 'John Doe',
 *   telephone: '+974123456789',
 *   appointmentDate: '2026-02-01',
 *   appointmentTime: '10:00',
 *   duration: '30',
 *   timezone: 'Asia/Qatar',
 *   language: 'en',
 * });
 */
export async function startBookingWorkflow(
  data: BookingInput,
  options?: WorkflowOptions
): Promise<string> {
  try {
    const client = await getWorkflowClient();
    const config = getConfig();

    const workflowId = generateWorkflowId('booking', data.bookingId);

    const handle = await client.start('bookingWorkflow', {
      taskQueue: options?.taskQueue || config.taskQueues.bookings,
      workflowId,
      args: [data],
      workflowExecutionTimeout: options?.workflowExecutionTimeout
        ? `${options.workflowExecutionTimeout}s`
        : '7d', // 7 days default for bookings
      memo: {
        bookingId: data.bookingId,
        email: data.email,
        appointmentDate: data.appointmentDate,
        ...options?.memo,
      },
      searchAttributes: {
        CustomerId: [data.email],
        BookingId: [data.bookingId],
        ...options?.searchAttributes,
      },
      retry: options?.retry,
    });

    logger.info('[Temporal] Booking workflow started', {
      workflowId: handle.workflowId,
      runId: handle.firstExecutionRunId,
      bookingId: data.bookingId,
    });

    return handle.workflowId;
  } catch (error) {
    logger.error('[Temporal] Failed to start booking workflow', {
      bookingId: data.bookingId,
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
}

/**
 * Start a payment processing workflow from Stripe webhook
 *
 * @param event - Stripe webhook event
 * @param options - Optional workflow configuration
 * @returns Workflow ID
 *
 * @example
 * const workflowId = await startPaymentWorkflow(stripeEvent);
 */
export async function startPaymentWorkflow(
  event: StripeEvent,
  options?: WorkflowOptions
): Promise<string> {
  try {
    const client = await getWorkflowClient();
    const config = getConfig();

    const workflowId = generateWorkflowId('payment', event.id);

    const handle = await client.start('paymentWorkflow', {
      taskQueue: options?.taskQueue || config.taskQueues.payments,
      workflowId,
      args: [event],
      workflowExecutionTimeout: options?.workflowExecutionTimeout
        ? `${options.workflowExecutionTimeout}s`
        : '1h', // 1 hour default for payments
      memo: {
        eventId: event.id,
        eventType: event.type,
        ...options?.memo,
      },
      searchAttributes: {
        StripeEventId: [event.id],
        EventType: [event.type],
        ...options?.searchAttributes,
      },
      retry: options?.retry || {
        maximumAttempts: 3,
        initialInterval: '5s',
        maximumInterval: '2m',
        backoffCoefficient: 2,
      },
    });

    logger.info('[Temporal] Payment workflow started', {
      workflowId: handle.workflowId,
      runId: handle.firstExecutionRunId,
      eventId: event.id,
      eventType: event.type,
    });

    return handle.workflowId;
  } catch (error) {
    logger.error('[Temporal] Failed to start payment workflow', {
      eventId: event.id,
      eventType: event.type,
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
}

/**
 * Get the status of a workflow execution
 *
 * @param workflowId - The workflow ID to check
 * @returns Workflow status information
 *
 * @example
 * const status = await getWorkflowStatus('booking-bk_123-abc123');
 * if (status.status === 'COMPLETED') {
 *   console.log('Result:', status.result);
 * }
 */
export async function getWorkflowStatus(
  workflowId: string
): Promise<WorkflowStatus> {
  try {
    const client = await getWorkflowClient();
    const handle = client.getHandle(workflowId);
    const description = await handle.describe();

    let result: unknown = undefined;
    let error: string | undefined = undefined;

    // Try to get result if workflow is completed
    if (description.status.name === 'COMPLETED') {
      try {
        result = await handle.result();
      } catch (e) {
        // Result might not be available
        logger.debug('[Temporal] Could not fetch workflow result', {
          workflowId,
          error: e instanceof Error ? e.message : String(e),
        });
      }
    }

    // Get error message if workflow failed
    if (description.status.name === 'FAILED') {
      error = description.raw?.workflowExecutionInfo?.closeStatus?.toString() || 'Unknown error';
    }

    const status: WorkflowStatus = {
      workflowId: description.workflowId,
      runId: description.runId,
      status: description.status.name as WorkflowStatus['status'],
      startTime: description.startTime,
      closeTime: description.closeTime,
      result,
      error,
    };

    return status;
  } catch (error) {
    logger.error('[Temporal] Failed to get workflow status', {
      workflowId,
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
}

/**
 * Cancel a running workflow
 *
 * @param workflowId - The workflow ID to cancel
 *
 * @example
 * await cancelWorkflow('booking-bk_123-abc123');
 */
export async function cancelWorkflow(workflowId: string): Promise<void> {
  try {
    const client = await getWorkflowClient();
    const handle = client.getHandle(workflowId);
    await handle.cancel();

    logger.info('[Temporal] Workflow cancelled', { workflowId });
  } catch (error) {
    logger.error('[Temporal] Failed to cancel workflow', {
      workflowId,
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
}

/**
 * Terminate a running workflow (immediate stop, no cleanup)
 *
 * @param workflowId - The workflow ID to terminate
 * @param reason - Optional reason for termination
 *
 * @example
 * await terminateWorkflow('booking-bk_123-abc123', 'Admin requested termination');
 */
export async function terminateWorkflow(
  workflowId: string,
  reason?: string
): Promise<void> {
  try {
    const client = await getWorkflowClient();
    const handle = client.getHandle(workflowId);
    await handle.terminate(reason || 'Terminated via API');

    logger.info('[Temporal] Workflow terminated', { workflowId, reason });
  } catch (error) {
    logger.error('[Temporal] Failed to terminate workflow', {
      workflowId,
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
}

/**
 * Send a signal to a running workflow
 *
 * @param workflowId - The workflow ID to signal
 * @param signal - Signal name
 * @param data - Signal payload data
 *
 * @example
 * // Signal a booking workflow to reschedule
 * await signalWorkflow('booking-bk_123-abc123', 'reschedule', {
 *   newDate: '2026-02-15',
 *   newTime: '14:00',
 * });
 */
export async function signalWorkflow(
  workflowId: string,
  signal: string,
  data: SignalPayload
): Promise<void> {
  try {
    const client = await getWorkflowClient();
    const handle = client.getHandle(workflowId);
    await handle.signal(signal, data);

    logger.info('[Temporal] Signal sent to workflow', {
      workflowId,
      signal,
    });
  } catch (error) {
    logger.error('[Temporal] Failed to signal workflow', {
      workflowId,
      signal,
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
}

/**
 * Query a workflow for current state
 *
 * @param workflowId - The workflow ID to query
 * @param query - Query name
 * @param args - Query arguments
 * @returns Query result
 *
 * @example
 * const bookingState = await queryWorkflow('booking-bk_123-abc123', 'getBookingState');
 */
export async function queryWorkflow<T = unknown>(
  workflowId: string,
  query: string,
  ...args: unknown[]
): Promise<T> {
  try {
    const client = await getWorkflowClient();
    const handle = client.getHandle(workflowId);
    const result = await handle.query<T>(query, ...args);

    logger.debug('[Temporal] Query executed', {
      workflowId,
      query,
    });

    return result;
  } catch (error) {
    logger.error('[Temporal] Failed to query workflow', {
      workflowId,
      query,
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
}

/**
 * Get workflow handle for advanced operations
 *
 * @param workflowId - The workflow ID
 * @returns Workflow handle
 */
export async function getWorkflowHandle(
  workflowId: string
): Promise<WorkflowHandle> {
  const client = await getWorkflowClient();
  return client.getHandle(workflowId);
}

// ============================================================================
// Health and Connection Management
// ============================================================================

/**
 * Check if Temporal client is healthy and connected
 *
 * @returns True if healthy, false otherwise
 */
export async function isTemporalHealthy(): Promise<boolean> {
  return isHealthy();
}

/**
 * Get the current client connection state
 */
export function getTemporalClientState() {
  return getClientState();
}

/**
 * Gracefully disconnect from Temporal
 * Call this during application shutdown
 */
export async function disconnectTemporal(): Promise<void> {
  return disconnect();
}

/**
 * Reconnect to Temporal after connection loss
 */
export async function reconnectTemporal(): Promise<void> {
  return reconnect();
}

// ============================================================================
// Start Generic Workflow
// ============================================================================

/**
 * Start a generic workflow by name
 *
 * @param workflowName - Name of the workflow to start
 * @param args - Arguments to pass to the workflow
 * @param options - Workflow options
 * @returns Workflow ID
 *
 * @example
 * const workflowId = await startWorkflow('syncDataWorkflow', [{ userId: '123' }], {
 *   taskQueue: 'sync',
 * });
 */
export async function startWorkflow(
  workflowName: string,
  args: unknown[],
  options?: WorkflowOptions & { workflowId?: string }
): Promise<string> {
  try {
    const client = await getWorkflowClient();
    const config = getConfig();

    const workflowId = options?.workflowId || generateWorkflowId(workflowName);

    const handle = await client.start(workflowName, {
      taskQueue: options?.taskQueue || config.taskQueue,
      workflowId,
      args,
      workflowExecutionTimeout: options?.workflowExecutionTimeout
        ? `${options.workflowExecutionTimeout}s`
        : '24h',
      memo: options?.memo,
      searchAttributes: options?.searchAttributes,
      retry: options?.retry,
    });

    logger.info('[Temporal] Workflow started', {
      workflowName,
      workflowId: handle.workflowId,
      runId: handle.firstExecutionRunId,
    });

    return handle.workflowId;
  } catch (error) {
    logger.error('[Temporal] Failed to start workflow', {
      workflowName,
      error: error instanceof Error ? error.message : String(error),
    });
    throw error;
  }
}

// ============================================================================
// Exports
// ============================================================================

export {
  getClient,
  getWorkflowClient,
  getConfig,
};

/**
 * Temporal workflow utilities for API routes
 */
const temporalUtils = {
  // Booking workflows
  startBookingWorkflow,
  // Payment workflows
  startPaymentWorkflow,
  // Generic workflow operations
  startWorkflow,
  getWorkflowStatus,
  cancelWorkflow,
  terminateWorkflow,
  signalWorkflow,
  queryWorkflow,
  getWorkflowHandle,
  // Health and connection
  isTemporalHealthy,
  getTemporalClientState,
  disconnectTemporal,
  reconnectTemporal,
  // Client access
  getClient,
  getWorkflowClient,
  getConfig,
};

export default temporalUtils;
