/**
 * Transaction Store - Platform-aware transaction management
 * Uses Firebase abstraction layer for cross-platform support
 */
import { create } from 'zustand';
import { doc, collection } from '../firebase';
import type { Transaction } from '../services/transactionService';

export interface TransactionCacheEntry {
    transactions: Transaction[];
    loading: boolean;
    error: string | null;
    lastFetched?: number;
}

export interface TransactionDetailCacheEntry {
    transaction: Transaction | null;
    loading: boolean;
    error: string | null;
    lastFetched?: number;
}

interface TransactionStoreState {
    transactionsByUid: Record<string, TransactionCacheEntry>;
    transactionDetails: Record<string, TransactionDetailCacheEntry>;
    ensureSubscription: (uid: string) => void;
    refreshTransactions: (uid: string) => Promise<Transaction[]>;
    invalidateTransactions: (uid: string) => Promise<Transaction[]>;
    removeTransactions: (uid: string) => void;
    ensureTransactionDetail: (transactionId: string) => void;
    refreshTransactionDetail: (transactionId: string) => Promise<Transaction | null>;
    invalidateTransactionDetail: (transactionId: string) => Promise<Transaction | null>;
    removeTransactionDetail: (transactionId: string) => void;
    clearAll: () => void;
}

const buildEntry = (
    existing: TransactionCacheEntry | undefined,
    overrides: Partial<TransactionCacheEntry>
): TransactionCacheEntry => ({
    transactions: existing?.transactions ?? [],
    loading: existing?.loading ?? false,
    error: existing?.error ?? null,
    lastFetched: existing?.lastFetched,
    ...overrides,
});

const buildDetailEntry = (
    existing: TransactionDetailCacheEntry | undefined,
    overrides: Partial<TransactionDetailCacheEntry>
): TransactionDetailCacheEntry => ({
    transaction: existing?.transaction ?? null,
    loading: existing?.loading ?? false,
    error: existing?.error ?? null,
    lastFetched: existing?.lastFetched,
    ...overrides,
});

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

const mapSnapshotToTransactions = (snapshot: any): Transaction[] => {
    if (!snapshot || !snapshot.docs) {
        return [];
    }

    return snapshot.docs.map((docItem: any) => {
        const data = docItem.data();
        return {
            ...data,
            id: data.id ?? docItem.id,
            createdAt: data.createdAt?.toDate ? data.createdAt.toDate() : data.createdAt ?? new Date(),
            updatedAt: data.updatedAt?.toDate ? data.updatedAt.toDate() : data.updatedAt ?? new Date(),
        } as Transaction;
    });
};

const mapDocumentSnapshotToTransaction = (
    transactionId: string,
    docSnap: any
): Transaction | null => {
    if (!snapshotExists(docSnap)) {
        return null;
    }

    const data = docSnap.data();
    if (!data) {
        return null;
    }

    return {
        ...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;
};

export const useTransactionStore = create<TransactionStoreState>((set, get) => ({
    transactionsByUid: {},
    transactionDetails: {},

    ensureSubscription: (uid: string) => {
        if (!uid) {
            return;
        }

        const existing = get().transactionsByUid[uid];
        if (existing?.loading) {
            return;
        }

        if (existing?.lastFetched) {
            return;
        }

        void get().refreshTransactions(uid).catch((error) => {
            set((state) => ({
                transactionsByUid: {
                    ...state.transactionsByUid,
                    [uid]: buildEntry(state.transactionsByUid[uid], {
                        loading: false,
                        error: error instanceof Error ? error.message : 'Failed to fetch transactions',
                    }),
                },
            }));
        });
    },

    refreshTransactions: async (uid: string) => {
        if (!uid) {
            return [];
        }

        set((state) => ({
            transactionsByUid: {
                ...state.transactionsByUid,
                [uid]: buildEntry(state.transactionsByUid[uid], {
                    loading: true,
                    error: null,
                }),
            },
        }));

        try {
            const transactionsCollection = collection('transactions');
            const snapshot = await transactionsCollection
                .where('uid', '==', uid)
                .orderBy('createdAt', 'desc')
                .get();

            const transactions = mapSnapshotToTransactions(snapshot);

            set((state) => {
                const nextDetails = { ...state.transactionDetails };
                transactions.forEach((transaction) => {
                    const detailEntry = nextDetails[transaction.id];
                    if (detailEntry) {
                        nextDetails[transaction.id] = buildDetailEntry(detailEntry, {
                            transaction,
                            loading: false,
                            error: null,
                            lastFetched: Date.now(),
                        });
                    }
                });

                return {
                    transactionsByUid: {
                        ...state.transactionsByUid,
                        [uid]: buildEntry(state.transactionsByUid[uid], {
                            transactions,
                            loading: false,
                            error: null,
                            lastFetched: Date.now(),
                        }),
                    },
                    transactionDetails: nextDetails,
                };
            });

            return transactions;
        } catch (error) {
            set((state) => ({
                transactionsByUid: {
                    ...state.transactionsByUid,
                    [uid]: buildEntry(state.transactionsByUid[uid], {
                        loading: false,
                        error: error instanceof Error ? error.message : 'Failed to fetch transactions',
                    }),
                },
            }));
            throw error;
        }
    },

    invalidateTransactions: async (uid: string) => {
        if (!uid) {
            return [];
        }

        set((state) => ({
            transactionsByUid: {
                ...state.transactionsByUid,
                [uid]: buildEntry(state.transactionsByUid[uid], {
                    loading: true,
                    error: null,
                    lastFetched: undefined,
                }),
            },
        }));

        return get().refreshTransactions(uid);
    },

    removeTransactions: (uid: string) => {
        set((state) => {
            const { [uid]: _removed, ...rest } = state.transactionsByUid;
            return { transactionsByUid: rest };
        });
    },

    ensureTransactionDetail: (transactionId: string) => {
        if (!transactionId) {
            return;
        }

        const existing = get().transactionDetails[transactionId];
        if (existing?.loading) {
            return;
        }

        if (existing?.lastFetched) {
            return;
        }

        void get().refreshTransactionDetail(transactionId).catch((error) => {
            set((state) => ({
                transactionDetails: {
                    ...state.transactionDetails,
                    [transactionId]: buildDetailEntry(state.transactionDetails[transactionId], {
                        loading: false,
                        error: error instanceof Error ? error.message : 'Failed to fetch transaction',
                    }),
                },
            }));
        });
    },

    refreshTransactionDetail: async (transactionId: string) => {
        if (!transactionId) {
            return null;
        }

        set((state) => ({
            transactionDetails: {
                ...state.transactionDetails,
                [transactionId]: buildDetailEntry(state.transactionDetails[transactionId], {
                    loading: true,
                    error: null,
                }),
            },
        }));

        try {
            const docRef = doc('transactions', transactionId);
            const docSnap = await docRef.get();
            const transaction = mapDocumentSnapshotToTransaction(transactionId, docSnap);

            set((state) => ({
                transactionDetails: {
                    ...state.transactionDetails,
                    [transactionId]: buildDetailEntry(state.transactionDetails[transactionId], {
                        transaction,
                        loading: false,
                        error: transaction ? null : 'Transaction not found',
                        lastFetched: Date.now(),
                    }),
                },
            }));

            return transaction;
        } catch (error) {
            set((state) => ({
                transactionDetails: {
                    ...state.transactionDetails,
                    [transactionId]: buildDetailEntry(state.transactionDetails[transactionId], {
                        loading: false,
                        error: error instanceof Error ? error.message : 'Failed to fetch transaction',
                    }),
                },
            }));
            throw error;
        }
    },

    invalidateTransactionDetail: async (transactionId: string) => {
        if (!transactionId) {
            return null;
        }

        set((state) => ({
            transactionDetails: {
                ...state.transactionDetails,
                [transactionId]: buildDetailEntry(state.transactionDetails[transactionId], {
                    loading: true,
                    error: null,
                    lastFetched: undefined,
                }),
            },
        }));

        return get().refreshTransactionDetail(transactionId);
    },

    removeTransactionDetail: (transactionId: string) => {
        set((state) => {
            const { [transactionId]: _removed, ...rest } = state.transactionDetails;
            return { transactionDetails: rest };
        });
    },

    clearAll: () => {
        set({ transactionsByUid: {}, transactionDetails: {} });
    },
}));

export const getTransactionsFromCache = (uid: string): Transaction[] => {
    if (!uid) {
        return [];
    }
    return useTransactionStore.getState().transactionsByUid[uid]?.transactions ?? [];
};

export const getTransactionDetailFromCache = (transactionId: string): Transaction | null => {
    if (!transactionId) {
        return null;
    }
    return useTransactionStore.getState().transactionDetails[transactionId]?.transaction ?? null;
};
