/**
 * Transaction Service - Platform-aware transaction management
 * Uses Firebase abstraction layer for cross-platform support
 */
import { doc, collection } from '../firebase';
import { WalletService } from './walletService';
import { calculateTotalAmountForSending, calculateProcessingFee } from '../utils/feeUtils';
import { useTransactionStore } from '../stores/transactionStore';
import { withGlobalLoading } from '../stores/uiStore';

const snapshotExists = (docSnap: any): boolean => {
  const existsValue = docSnap?.exists;
  if (typeof existsValue === 'function') {
    try {
      return !!existsValue.call(docSnap);
    } catch {
      return false;
    }
  }
  return !!existsValue;
};

// Base transaction interface
interface BaseTransaction {
  id: string;
  uid: string;
  amount: number;
  status: 'pending' | 'completed' | 'failed';
  createdAt: Date;
  updatedAt: Date;
}

// Send money transaction
interface SendTransaction extends BaseTransaction {
  type: 'send';
  recipientName: string;
  recipientPhoneNumber: string;
  recipientType: 'saved' | 'contact';
  recipientId: string;
  note: string;
  fulfilled: boolean;
}

// Receive money transaction
interface ReceiveTransaction extends BaseTransaction {
  type: 'receive';
  senderName: string;
  senderPhoneNumber: string;
  senderType: 'saved' | 'contact';
  senderId: string;
  note: string;
}

// Add cash transaction
interface AddCashTransaction extends BaseTransaction {
  type: 'add_cash';
  cardId: string;
  cardType: string;
  lastFourDigits: string;
}

// Cash out transaction
interface CashOutTransaction extends BaseTransaction {
  type: 'cash_out';
  bankProvider: 'awash' | 'telebirr';
  accountNumber?: string;
  note: string;
}

// Discriminated union for all transaction types
export type Transaction = SendTransaction | ReceiveTransaction | AddCashTransaction | CashOutTransaction;

export class TransactionService {
  /**
   * Create a new transaction when sending money
   */
  static async sendMoney(
    uid: string,
    transactionData: {
      amount: number;
      recipientName: string;
      recipientPhoneNumber: string;
      recipientType: 'saved' | 'contact';
      recipientId: string;
      note: string;
    }
  ): Promise<{ success: boolean; error?: string; transactionId?: string }> {
    return withGlobalLoading(async () => {
      try {
        // Validate input
        if (!transactionData.amount || transactionData.amount <= 0) {
          return { success: false, error: 'Invalid amount' };
        }

        if (!transactionData.recipientName || !transactionData.recipientPhoneNumber) {
          return { success: false, error: 'Recipient information is required' };
        }

        // Check if user has sufficient balance
        const walletResult = await WalletService.getUserWallet(uid);
        if (!walletResult.success) {
          return { success: false, error: 'Failed to get wallet information' };
        }

        const wallet = walletResult.wallet;
        if (!wallet) {
          return { success: false, error: 'Wallet not found' };
        }

        // Calculate total amount including processing fee
        const totalAmountRequired = calculateTotalAmountForSending(transactionData.amount);

        if (wallet.balance < totalAmountRequired) {
          const processingFee = calculateProcessingFee(transactionData.amount);
          return {
            success: false,
            error: `Insufficient balance. Required: $${(totalAmountRequired / 100).toFixed(2)} (including $${(processingFee / 100).toFixed(2)} processing fee). Available: $${(wallet.balance / 100).toFixed(2)}`
          };
        }

        // Generate transaction ID
        const transactionId = `transaction_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

        // Create transaction record
        const newTransaction: SendTransaction = {
          id: transactionId,
          uid,
          amount: transactionData.amount,
          recipientName: transactionData.recipientName,
          recipientPhoneNumber: transactionData.recipientPhoneNumber,
          recipientType: transactionData.recipientType,
          recipientId: transactionData.recipientId,
          note: transactionData.note || '',
          fulfilled: false,
          status: 'pending',
          type: 'send',
          createdAt: new Date(),
          updatedAt: new Date(),
        };

        // Save transaction to Firestore
        const transactionRef = doc('transactions', transactionId);
        await transactionRef.set(newTransaction);

        // Update wallet balance
        const totalAmountToDeduct = calculateTotalAmountForSending(transactionData.amount);
        const newBalance = wallet.balance - totalAmountToDeduct;
        const updateResult = await WalletService.updateWalletBalance(uid, newBalance);

        if (!updateResult.success) {
          return { success: false, error: 'Failed to update wallet balance' };
        }

        // Update transaction status to completed
        await transactionRef.update({
          status: 'completed',
          updatedAt: new Date(),
        });

        console.log('Transaction completed successfully:', transactionId);

        try {
          const transactionStore = useTransactionStore.getState();
          await transactionStore.invalidateTransactions(uid);
          await transactionStore.invalidateTransactionDetail(transactionId);
        } catch (cacheError) {
          console.warn('Failed to refresh transaction cache after sendMoney', cacheError);
        }

        return { success: true, transactionId };
      } catch (error) {
        console.error('Error creating transaction:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to create transaction'
        };
      }
    });
  }

  /**
   * Create a cash out transaction
   */
  static async cashOut(
    uid: string,
    transactionData: {
      amount: number;
      bankProvider: 'awash' | 'telebirr';
      accountNumber?: string;
      note: string;
    }
  ): Promise<{ success: boolean; error?: string; transactionId?: string }> {
    return withGlobalLoading(async () => {
      try {
        // Validate input
        if (!transactionData.amount || transactionData.amount <= 0) {
          return { success: false, error: 'Invalid amount' };
        }

        if (!transactionData.bankProvider) {
          return { success: false, error: 'Bank provider is required' };
        }

        // Check if user has sufficient balance
        const walletResult = await WalletService.getUserWallet(uid);
        if (!walletResult.success) {
          return { success: false, error: 'Failed to get wallet information' };
        }

        const wallet = walletResult.wallet;
        if (!wallet) {
          return { success: false, error: 'Wallet not found' };
        }

        if (wallet.balance < transactionData.amount) {
          return {
            success: false,
            error: `Insufficient balance. Required: $${(transactionData.amount / 100).toFixed(2)}. Available: $${(wallet.balance / 100).toFixed(2)}`
          };
        }

        // Generate transaction ID
        const transactionId = `transaction_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;

        // Create transaction record
        const newTransaction: CashOutTransaction = {
          id: transactionId,
          uid,
          amount: transactionData.amount,
          bankProvider: transactionData.bankProvider,
          note: transactionData.note || '',
          status: 'pending',
          type: 'cash_out',
          createdAt: new Date(),
          updatedAt: new Date(),
          ...(transactionData.accountNumber ? { accountNumber: transactionData.accountNumber } : {}),
        } as CashOutTransaction;

        // Save transaction to Firestore
        const transactionRef = doc('transactions', transactionId);
        await transactionRef.set(newTransaction);

        // Update wallet balance
        const newBalance = wallet.balance - transactionData.amount;
        const updateResult = await WalletService.updateWalletBalance(uid, newBalance);

        if (!updateResult.success) {
          return { success: false, error: 'Failed to update wallet balance' };
        }

        // Update transaction status to completed
        await transactionRef.update({
          status: 'completed',
          updatedAt: new Date(),
        });

        console.log('Cash out transaction completed successfully:', transactionId);

        try {
          const transactionStore = useTransactionStore.getState();
          await transactionStore.invalidateTransactions(uid);
          await transactionStore.invalidateTransactionDetail(transactionId);
        } catch (cacheError) {
          console.warn('Failed to refresh transaction cache after cashOut', cacheError);
        }

        return { success: true, transactionId };
      } catch (error) {
        console.error('Error creating cash out transaction:', error);
        return {
          success: false,
          error: error instanceof Error ? error.message : 'Failed to create cash out transaction'
        };
      }
    });
  }

  /**
   * Get all transactions for a user
   */
  static async getUserTransactions(uid: string): Promise<{ success: boolean; transactions?: Transaction[]; error?: string }> {
    try {
      return { success: true, transactions: [] };
    } catch (error) {
      console.error('Error getting user transactions:', error);
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Failed to get transactions'
      };
    }
  }

  static async getTransactionById(transactionId: string): Promise<{ success: boolean; transaction?: Transaction; error?: string }> {
    try {
      const docRef = doc('transactions', transactionId);
      const docSnap = await docRef.get();

      if (!snapshotExists(docSnap)) {
        return { success: false, error: 'Transaction not found' };
      }

      const data = docSnap.data();
      if (!data) {
        return { success: false, error: 'Transaction data unavailable' };
      }

      const transaction = {
        ...data,
        id: data.id ?? transactionId,
        createdAt: data.createdAt?.toDate ? data.createdAt.toDate() : data.createdAt ?? new Date(),
        updatedAt: data.updatedAt?.toDate ? data.updatedAt.toDate() : data.updatedAt ?? new Date(),
      } as Transaction;

      return { success: true, transaction };
    } catch (error) {
      console.error('Error fetching transaction by id:', error);
      return {
        success: false,
        error: error instanceof Error ? error.message : 'Failed to fetch transaction',
      };
    }
  }
}
