import { useCallback } from 'react';
import { usePersSDK } from '../providers/PersSDKProvider';
import { SubmissionResult, useTransactionSigner, OnStatusUpdateFn } from './useTransactionSigner';
import type { 
  RedemptionCreateRequestDTO, 
  RedemptionDTO, 
  RedemptionRedeemDTO, 
  RedemptionRedeemRequestResponseDTO,
  RedemptionTypeDTO,
  PaginatedResponseDTO,
  RedemptionRedeemIncludeRelation,
  RedemptionIncludeRelation,
  ProcessRecordStatus,
  DynamicContext,
  SortOrder
} from '@explorins/pers-sdk';
import type { RedemptionRedeemQueryParams, RedemptionQueryParams } from '@explorins/pers-sdk/redemption';
import type { PaginationOptions } from '@explorins/pers-sdk';

// Re-export for consumers (new names + deprecated aliases)
export type { RedemptionRedeemQueryParams, RedemptionQueryParams } from '@explorins/pers-sdk/redemption';
/** @deprecated Use RedemptionRedeemQueryParams instead */
export type RedemptionRedeemFilters = RedemptionRedeemQueryParams;
/** @deprecated Use RedemptionQueryParams instead */
export type RedemptionFilterOptions = RedemptionQueryParams;

/**
 * Options for fetching redemptions with filtering and pagination
 */
export interface GetRedemptionsOptions {
  /** Filter by active status (Admin only - regular users always see active offers) */
  active?: boolean;
  /** Free text search across name, description, tags (case-insensitive) */
  search?: string;
  /** Filter by tag(s) - single tag or array for OR match */
  tags?: string | string[];
  /** Filter redemptions starting on or after this date */
  startDate?: Date | string;
  /** Filter redemptions ending on or before this date */
  endDate?: Date | string;
  /** Relations to include: 'redeemCount' */
  include?: RedemptionIncludeRelation[];
  /** Page number (1-indexed) */
  page?: number;
  /** Results per page */
  limit?: number;
  /** Field to sort by */
  sortBy?: 'name' | 'createdAt' | 'startDate';
  /** Sort direction */
  sortOrder?: SortOrder;
}

export const useRedemptions = () => {
  const { sdk, isInitialized, isAuthenticated } = usePersSDK();
  const { signAndSubmitTransactionWithJWT, isSignerAvailable, currentStatus, statusMessage } = useTransactionSigner();

  /**
   * Get redemption offers based on user permissions
   * 
   * **Regular Users:** Automatically see only active redemptions available for purchase
   * **Administrators:** Can see all redemptions and filter by active/inactive status
   * 
   * The backend enforces permission-based filtering automatically.
   * 
   * @param options - Filter and pagination options
   * @param options.active - Filter by active status (Admin only)
   * @param options.search - Free text search across name, description, tags
   * @param options.startDate - Filter redemptions starting on or after this date
   * @param options.endDate - Filter redemptions ending on or before this date
   * @param options.include - Relations to include: 'redeemCount'
   * @param options.page - Page number (1-indexed)
   * @param options.limit - Results per page
   * @param options.sortBy - Field to sort by ('name' | 'createdAt')
   * @param options.sortOrder - Sort direction ('ASC' | 'DESC')
   * 
   * @example Basic usage
   * ```typescript
   * const { data: offers } = await getRedemptions();
   * ```
   * 
   * @example Filter by date range
   * ```typescript
   * const { data: dateFiltered } = await getRedemptions({ 
   *   startDate: '2026-05-01', 
   *   endDate: '2026-06-30' 
   * });
   * ```
   * 
   * @example Get redemptions with redeem count
   * ```typescript
   * const { data: offers } = await getRedemptions({ include: ['redeemCount'] });
   * offers.forEach(o => console.log(`${o.name}: ${o.redeemCount} redeems`));
   * ```
   * 
   * @example Search with pagination
   * ```typescript
   * const { data, pagination } = await getRedemptions({ 
   *   search: 'discount',
   *   page: 1, 
   *   limit: 10,
   *   sortBy: 'name',
   *   sortOrder: 'ASC'
   * });
   * ```
   */
  const getRedemptions = useCallback(async (options?: GetRedemptionsOptions): Promise<PaginatedResponseDTO<RedemptionDTO>> => {
    if (!isInitialized || !sdk) {
      throw new Error('SDK not initialized. Call initialize() first.');
    }
    
    try {
      // Convert Date to ISO string for API compatibility
      const apiOptions = options ? {
        ...options,
        startDate: options.startDate instanceof Date ? options.startDate.toISOString() : options.startDate,
        endDate: options.endDate instanceof Date ? options.endDate.toISOString() : options.endDate,
      } : undefined;
      
      const result = await sdk.redemptions.getRedemptions(apiOptions);
      return result;
    } catch (error) {
      console.error('Failed to fetch redemptions:', error);
      throw error;
    }
  }, [sdk, isInitialized]);

  /**
   * Get user's redemption history with optional filters, pagination and include relations
   * 
   * @param filters - Optional filters (redemptionId, status)
   * @param options - Pagination options (page, limit, sortBy, sortOrder)
   * @param include - Relations to include: 'redemption', 'user', 'business', 'transactions'
   * @returns Promise resolving to paginated redemption history
   * 
   * @example
   * ```typescript
   * // Get first page of user redemptions
   * const { data: redeems, pagination } = await getUserRedemptions({}, { page: 1, limit: 10 });
   * console.log(`Page ${pagination.page} of ${pagination.pages}`);
   * 
   * // Get only completed redemptions
   * import { ProcessRecordStatus } from '@explorins/pers-shared';
   * const { data: completed } = await getUserRedemptions({ status: ProcessRecordStatus.COMPLETED });
   * 
   * // Get user redemptions with full redemption details
   * const { data: redeems } = await getUserRedemptions({}, {}, ['redemption', 'business']);
   * redeems.forEach(redeem => {
   *   console.log('Redemption:', redeem.included?.redemption?.name);
   *   console.log('Business:', redeem.included?.business?.displayName);
   * });
   * ```
   */
  const getUserRedemptions = useCallback(async (
    filters?: { redemptionId?: string; status?: ProcessRecordStatus },
    options?: PaginationOptions,
    include?: RedemptionRedeemIncludeRelation[]
  ): Promise<PaginatedResponseDTO<RedemptionRedeemDTO>> => {
    if (!isInitialized || !sdk) {
      throw new Error('SDK not initialized. Call initialize() first.');
    }
    if (!isAuthenticated) {
      console.warn('SDK not authenticated. getUserRedemptions requires authentication.');
      return { data: [], pagination: { page: 1, limit: 0, total: 0, pages: 0, hasNext: false, hasPrev: false } };
    }
    
    try {
      const result = await sdk.redemptions.getUserRedemptions(filters, options, include);
      return result;
    } catch (error) {
      console.error('Failed to fetch user redemptions:', error);
      throw error;
    }
  }, [sdk, isInitialized, isAuthenticated]);

  /**
   * Redeem a redemption offer with optional dynamic context
   * 
   * ## When to use `redeem()` vs `createTransaction()`
   * 
   * | Use Case | Method | Auto-Signs |
   * |----------|--------|------------|
   * | **Voucher redemption** | `redeem()` | ✅ Yes |
   * | **Reward redemption** | `redeem()` | ✅ Yes |
   * | **Direct token transfer** | `createTransaction()` | ⚠️ Depends |
   * | **POS burn with QR** | `buildPOSBurnRequest()` | ❌ Manual |
   * 
   * **Use `redeem()` for voucher/reward redemptions.** It automatically:
   * 1. Creates redemption record
   * 2. Triggers passkey/biometric prompt  
   * 3. Signs the transaction
   * 4. Submits to blockchain
   * 5. Returns result with `transactionHash`
   * 
   * **No admin approval required** - The "PENDING" state is brief (seconds). 
   * The user's passkey signature IS the approval.
   * 
   * Supports dynamic context for personalized NFT generation when the redemption
   * issues ERC721 tokens. Context can include validity dates, AI prompt placeholders,
   * and custom metadata.
   * 
   * @param redemptionId - ID of the redemption to redeem
   * @param options - Optional settings
   * @param options.context - Dynamic context for NFT personalization (validity dates, AI prompts)
   * @param options.onStatusUpdate - Callback for transaction signing status updates
   * @returns Promise resolving to redemption result with `transactionHash` on success
   * 
   * @example Basic redemption (most common)
   * ```typescript
   * const { redeem } = useRedemptions();
   * 
   * const result = await redeem('redemption-123', {
   *   onStatusUpdate: (status, message) => {
   *     console.log(`Status: ${status} - ${message}`);
   *   }
   * });
   * 
   * if (result.transactionHash) {
   *   console.log('Redemption complete!', result.transactionHash);
   * }
   * ```
   * 
   * @example Redemption with dynamic context (personalized NFT)
   * ```typescript
   * const result = await redeem('event-ticket-123', {
   *   context: {
   *     // Validity - token valid for event duration
   *     validityDate: new Date().toISOString(),
   *     validityDuration: 8,  // 8 hours
   *     
   *     // AI prompt context - available as {{context.xxx}} in prompts
   *     attendeeName: 'John Doe',
   *     ticketTier: 'VIP',
   *     seatSection: 'A12',
   *   }
   * });
   * ```
   * 
   * @example Hotel stay NFT with check-in/check-out dates
   * ```typescript
   * const result = await redeem('hotel-nft-456', {
   *   context: {
   *     validityDate: '2026-04-15T14:00:00Z',      // Check-in
   *     validityEndDate: '2026-04-20T11:00:00Z',   // Check-out
   *     guestName: 'Jane Smith',
   *     roomNumber: '305',
   *     roomType: 'Suite',
   *   }
   * });
   * ```
   * 
   * @see {@link useTransactions.createTransaction | createTransaction()} for direct transfers without redemption flow
   */
  const redeem = useCallback(async (
    redemptionId: string, 
    options?: { context?: DynamicContext; onStatusUpdate?: OnStatusUpdateFn }
  ): Promise<RedemptionRedeemRequestResponseDTO> => {
    if (!isInitialized || !sdk) {
      throw new Error('SDK not initialized. Call initialize() first.');
    }
    if (!isAuthenticated) {
      throw new Error('SDK not authenticated. redeem requires authentication.');
    }
    
    try {
      const result = await sdk.redemptions.redeem(redemptionId, options?.context);

      const txToken = result.senderTransaction?.actionable?.authToken;
      if (txToken && isSignerAvailable) {
        try {
          const signingResult: SubmissionResult = await signAndSubmitTransactionWithJWT(txToken, options?.onStatusUpdate);
          if (signingResult.success) {
            // Return enhanced result with signing information
            return {
              ...result,
              transactionHash: signingResult.transactionHash,
              // signature: signingResult.signature,
              isSigned: true,
              signedAt: new Date().toISOString()
            } as RedemptionRedeemRequestResponseDTO;
          } else {
            console.error('Transaction signing failed:', signingResult.error);
            throw new Error(signingResult.error || 'Transaction signing failed');
          }
        } catch (signingError) {
          console.error('Blockchain signing error:', signingError);
          throw new Error(`Transaction signing failed: ${signingError}`);
        }
      } else if (txToken && !isSignerAvailable) {
        console.warn('Transaction requires signature but signer is not available');
        throw new Error('Transaction requires signature but blockchain signer is not initialized');
      }
      
      // Return original result if no signing was required
      return result;
    } catch (error) {
      console.error('Failed to redeem redemption:', error);
      throw error;
    }
  }, [sdk, isInitialized, isAuthenticated, signAndSubmitTransactionWithJWT, isSignerAvailable]);

  // Admin methods

  /**
   * Admin: Get all redemption redeems with filters and include relations
   * 
   * Retrieves all redemption redeems across the platform with filtering.
   * 
   * @param filters - Optional filters for user, redemption, date range, and pagination
   * @param filters.userId - Filter by user ID
   * @param filters.redemptionId - Filter by redemption offer ID
   * @param filters.businessId - Filter by business ID (admin only)
   * @param filters.status - Filter by processing status (PENDING, PROCESSING, COMPLETED, FAILED)
   * @param filters.dateFrom - Filter redeems created on or after this date (ISO 8601 format)
   * @param filters.dateTo - Filter redeems created on or before this date (ISO 8601 format)
   * @param filters.page - Page number (1-indexed)
   * @param filters.limit - Items per page
   * @param filters.sortBy - Field to sort by
   * @param filters.sortOrder - Sort direction
   * @param include - Relations to include: 'redemption', 'user', 'business'
   * @returns Promise resolving to paginated redemption redeems
   * 
   * @example
   * ```typescript
   * // Get all redeems with user details
   * const { data: redeems } = await getRedemptionRedeems({}, ['user', 'redemption']);
   * 
   * // Filter by specific user
   * const { data: userRedeems } = await getRedemptionRedeems(
   *   { userId: 'user-123' },
   *   ['redemption']
   * );
   * 
   * // Filter by date range
   * const { data } = await getRedemptionRedeems({
   *   dateFrom: '2026-01-01',
   *   dateTo: '2026-03-31'
   * }, ['user', 'redemption']);
   * ```
   */
  const getRedemptionRedeems = useCallback(async (
    filters?: RedemptionRedeemQueryParams,
    include?: RedemptionRedeemIncludeRelation[]
  ): Promise<PaginatedResponseDTO<RedemptionRedeemDTO>> => {
    if (!isInitialized || !sdk) {
      throw new Error('SDK not initialized. Call initialize() first.');
    }
    
    try {
      const result = await sdk.redemptions.getRedemptionRedeems(filters, include);
      return result;
    } catch (error) {
      console.error('Failed to fetch redemption redeems:', error);
      throw error;
    }
  }, [sdk, isInitialized]);

  const createRedemption = useCallback(async (redemptionData: RedemptionCreateRequestDTO): Promise<RedemptionDTO> => {
    if (!isInitialized || !sdk) {
      throw new Error('SDK not initialized. Call initialize() first.');
    }
    
    try {
      const result = await sdk.redemptions.createRedemption(redemptionData);
      return result;
    } catch (error) {
      console.error('Failed to create redemption offer:', error);
      throw error;
    }
  }, [sdk, isInitialized]);

  const getRedemptionTypes = useCallback(async (): Promise<PaginatedResponseDTO<RedemptionTypeDTO>> => {
    if (!isInitialized || !sdk) {
      throw new Error('SDK not initialized. Call initialize() first.');
    }
    
    try {
      const result = await sdk.redemptions.getRedemptionTypes();
      return result;
    } catch (error) {
      console.error('Failed to fetch redemption types:', error);
      throw error;
    }
  }, [sdk, isInitialized]);

  const updateRedemption = useCallback(async (redemptionId: string, redemptionData: RedemptionCreateRequestDTO): Promise<RedemptionDTO> => {
    if (!isInitialized || !sdk) {
      throw new Error('SDK not initialized. Call initialize() first.');
    }
    
    try {
      const result = await sdk.redemptions.updateRedemption(redemptionId, redemptionData);
      return result;
    } catch (error) {
      console.error('Failed to update redemption:', error);
      throw error;
    }
  }, [sdk, isInitialized]);

  const toggleRedemptionStatus = useCallback(async (redemptionId: string): Promise<RedemptionDTO> => {
    if (!isInitialized || !sdk) {
      throw new Error('SDK not initialized. Call initialize() first.');
    }
    
    try {
      const result = await sdk.redemptions.toggleRedemptionStatus(redemptionId);
      return result;
    } catch (error) {
      console.error('Failed to toggle redemption status:', error);
      throw error;
    }
  }, [sdk, isInitialized]);

  /**
   * Get a specific redemption by ID
   * 
   * Retrieves detailed information about a specific redemption offer.
   * 
   * @param id - Redemption UUID
   * @param include - Optional relations to include: 'redeemCount'
   * @returns Promise resolving to the redemption
   * 
   * @example Basic usage
   * ```typescript
   * const redemption = await getRedemptionById('7baf4d39-0bd6-45ca-9c66-8c0d655767c3');
   * console.log(redemption.name);
   * ```
   * 
   * @example With redeem count
   * ```typescript
   * const redemption = await getRedemptionById('redemption-123', ['redeemCount']);
   * console.log(`${redemption.name} has been redeemed ${redemption.redeemCount} times`);
   * ```
   */
  const getRedemptionById = useCallback(async (
    id: string,
    include?: RedemptionIncludeRelation[]
  ): Promise<RedemptionDTO> => {
    if (!isInitialized || !sdk) {
      throw new Error('SDK not initialized. Call initialize() first.');
    }
    
    try {
      const result = await sdk.redemptions.getRedemptionById(id, include);
      return result;
    } catch (error) {
      console.error('Failed to fetch redemption by ID:', error);
      throw error;
    }
  }, [sdk, isInitialized]);

  return {
    getRedemptions,
    getRedemptionById,
    getUserRedemptions,
    redeem,
    getRedemptionTypes,
    getRedemptionRedeems,
    createRedemption,
    updateRedemption,
    toggleRedemptionStatus,
    isAvailable: isInitialized && !!sdk?.redemptions,
    // Expose signing status for UI feedback
    signingStatus: currentStatus,
    signingStatusMessage: statusMessage,
  };
};

export type RedemptionHook = ReturnType<typeof useRedemptions>;