/**
 * Store Zustand pour le module Banking - COOP-CA AMIFOND
 *
 * Gestion centralisée de l'état Banking avec Zustand
 */

import { create } from "zustand";
import { devtools, subscribeWithSelector } from "zustand/middleware";

import { BankingService } from "../../services/banking.service";
import {
  Account,
  Transaction,
  Transfer,
  BankingStats,
  BalanceDetails,
  AccountFilters,
  TransactionFilters,
  TransferFilters,
  AccountCreateData,
  AccountUpdateData,
  TransactionCreateData,
  TransferCreateData,
  DepositData,
  WithdrawalData,
  BulkAccountAction,
  BulkTransactionAction,
  BankingError,
} from "../../types/banking";

// Types pour l'état du store
interface BankingState {
  // États de chargement
  isLoading: boolean;
  isLoadingAccounts: boolean;
  isLoadingTransactions: boolean;
  isLoadingTransfers: boolean;
  isLoadingStats: boolean;

  // Données
  accounts: Account[];
  transactions: Transaction[];
  transfers: Transfer[];
  bankingStats: BankingStats | null;
  selectedAccount: Account | null;
  accountBalance: BalanceDetails | null;

  // Pagination
  accountsPagination: {
    count: number;
    next: string | null;
    previous: string | null;
    currentPage: number;
    totalPages: number;
  };
  transactionsPagination: {
    count: number;
    next: string | null;
    previous: string | null;
    currentPage: number;
    totalPages: number;
  };
  transfersPagination: {
    count: number;
    next: string | null;
    previous: string | null;
    currentPage: number;
    totalPages: number;
  };

  // Filtres actifs
  accountFilters: AccountFilters;
  transactionFilters: TransactionFilters;
  transferFilters: TransferFilters;

  // Erreurs
  error: BankingError | null;
  accountsError: BankingError | null;
  transactionsError: BankingError | null;
  transfersError: BankingError | null;

  // Cache et métadonnées
  lastUpdated: {
    accounts: string | null;
    transactions: string | null;
    transfers: string | null;
    stats: string | null;
  };
}

// Types pour les actions du store
interface BankingActions {
  // Actions générales
  clearError: () => void;
  clearAllErrors: () => void;
  resetStore: () => void;

  // Actions comptes
  fetchAccounts: (filters?: AccountFilters) => Promise<void>;
  fetchAccount: (id: number) => Promise<Account>;
  createAccount: (data: AccountCreateData) => Promise<Account>;
  createAccountForClient: (data: AccountCreateData) => Promise<Account>;
  updateAccount: (id: number, data: AccountUpdateData) => Promise<Account>;
  deleteAccount: (id: number) => Promise<void>;
  freezeAccount: (id: number, reason?: string) => Promise<Account>;
  unfreezeAccount: (id: number, reason?: string) => Promise<Account>;
  fetchAccountBalance: (id: number) => Promise<void>;
  bulkAccountActions: (action: BulkAccountAction) => Promise<void>;
  setSelectedAccount: (account: Account | null) => void;
  setAccountFilters: (filters: AccountFilters) => void;

  // Actions transactions
  fetchTransactions: (filters?: TransactionFilters) => Promise<void>;
  fetchTransaction: (id: number) => Promise<Transaction>;
  createTransaction: (data: TransactionCreateData) => Promise<Transaction>;
  createTransactionForClient: (data: TransactionCreateData) => Promise<Transaction>;
  deposit: (data: DepositData) => Promise<Transaction>;
  withdrawal: (data: WithdrawalData) => Promise<Transaction>;
  cancelTransaction: (id: number, reason?: string) => Promise<Transaction>;
  reverseTransaction: (id: number, reason?: string) => Promise<Transaction>;
  bulkTransactionActions: (action: BulkTransactionAction) => Promise<void>;
  setTransactionFilters: (filters: TransactionFilters) => void;

  // Actions transferts
  fetchTransfers: (filters?: TransferFilters) => Promise<void>;
  fetchTransfer: (id: number) => Promise<Transfer>;
  createTransfer: (data: TransferCreateData) => Promise<Transfer>;
  cancelTransfer: (id: number, reason?: string) => Promise<Transfer>;
  processTransfer: (id: number) => Promise<Transfer>;
  setTransferFilters: (filters: TransferFilters) => void;

  // Actions statistiques
  fetchBankingStats: () => Promise<void>;

  // Actions utilitaires
  refreshData: () => Promise<void>;
  invalidateCache: () => void;
}

// Store type complet
type BankingStore = BankingState & BankingActions;

// État initial
const initialState: BankingState = {
  // États de chargement
  isLoading: false,
  isLoadingAccounts: false,
  isLoadingTransactions: false,
  isLoadingTransfers: false,
  isLoadingStats: false,

  // Données
  accounts: [],
  transactions: [],
  transfers: [],
  bankingStats: null,
  selectedAccount: null,
  accountBalance: null,

  // Pagination
  accountsPagination: {
    count: 0,
    next: null,
    previous: null,
    currentPage: 1,
    totalPages: 1,
  },
  transactionsPagination: {
    count: 0,
    next: null,
    previous: null,
    currentPage: 1,
    totalPages: 1,
  },
  transfersPagination: {
    count: 0,
    next: null,
    previous: null,
    currentPage: 1,
    totalPages: 1,
  },

  // Filtres actifs
  accountFilters: {},
  transactionFilters: {},
  transferFilters: {},

  // Erreurs
  error: null,
  accountsError: null,
  transactionsError: null,
  transfersError: null,

  // Cache et métadonnées
  lastUpdated: {
    accounts: null,
    transactions: null,
    transfers: null,
    stats: null,
  },
};

// Fonction utilitaire pour transformer les erreurs
const transformError = (error: unknown): BankingError => {
  if (error && typeof error === "object" && "status" in error) {
    const httpError = error as { status: number; message: string; data?: unknown };
    return {
      code: `HTTP_${httpError.status}`,
      message: httpError.message,
      details: httpError.data as Record<string, string | number | boolean>,
    };
  }

  if (error instanceof Error) {
    return {
      code: "UNKNOWN_ERROR",
      message: error.message,
    };
  }

  return {
    code: "UNKNOWN_ERROR",
    message: "Une erreur inattendue s'est produite",
  };
};

// Fonction utilitaire pour calculer la pagination
const calculatePagination = (response: { count: number; next?: string | null; previous?: string | null }) => {
  const itemsPerPage = 20; // Valeur par défaut Django REST Framework
  const totalPages = Math.ceil(response.count / itemsPerPage);

  // Extraire le numéro de page depuis l'URL next/previous
  let currentPage = 1;
  const nextUrl = response.next ?? null;
  const previousUrl = response.previous ?? null;

  if (nextUrl) {
    const nextUrlObj = new URL(nextUrl);
    const nextPageParam = nextUrlObj.searchParams.get("page");
    if (nextPageParam) {
      currentPage = parseInt(nextPageParam) - 1;
    }
  } else if (previousUrl) {
    const prevUrlObj = new URL(previousUrl);
    const prevPageParam = prevUrlObj.searchParams.get("page");
    if (prevPageParam) {
      currentPage = parseInt(prevPageParam) + 1;
    }
  }

  return {
    count: response.count,
    next: nextUrl,
    previous: previousUrl,
    currentPage,
    totalPages,
  };
};

// Création du store Banking
export const useBankingStore = create<BankingStore>()(
  devtools(
    subscribeWithSelector((set, get) => ({
      ...initialState,

      // Actions générales
      clearError: () => set({ error: null }),

      clearAllErrors: () =>
        set({
          error: null,
          accountsError: null,
          transactionsError: null,
          transfersError: null,
        }),

      resetStore: () => set(initialState),

      // Actions comptes
      fetchAccounts: async (filters = {}) => {
        set({ isLoadingAccounts: true, accountsError: null });

        try {
          const response = await BankingService.getAccounts(filters);

          set({
            accounts: response.results,
            accountsPagination: calculatePagination(response),
            accountFilters: filters,
            isLoadingAccounts: false,
            lastUpdated: {
              ...get().lastUpdated,
              accounts: new Date().toISOString(),
            },
          });
        } catch (error) {
          set({
            isLoadingAccounts: false,
            accountsError: transformError(error),
          });
          throw error;
        }
      },

      fetchAccount: async (id) => {
        set({ isLoading: true, error: null });

        try {
          const account = await BankingService.getAccount(id);

          // Mettre à jour le compte dans la liste si il existe
          const accounts = get().accounts.map((acc) => (acc.id === id ? account : acc));

          set({
            accounts,
            isLoading: false,
          });

          return account;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      createAccount: async (data) => {
        set({ isLoading: true, error: null });

        try {
          const account = await BankingService.createAccount(data);

          set({
            accounts: [account, ...get().accounts],
            isLoading: false,
          });

          return account;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      createAccountForClient: async (data) => {
        set({ isLoading: true, error: null });

        try {
          const account = await BankingService.createAccountForClient(data);

          set({
            accounts: [account, ...get().accounts],
            isLoading: false,
          });

          return account;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      updateAccount: async (id, data) => {
        set({ isLoading: true, error: null });

        try {
          const account = await BankingService.updateAccount(id, data);

          const accounts = get().accounts.map((acc) => (acc.id === id ? account : acc));

          set({
            accounts,
            selectedAccount: get().selectedAccount?.id === id ? account : get().selectedAccount,
            isLoading: false,
          });

          return account;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      deleteAccount: async (id) => {
        set({ isLoading: true, error: null });

        try {
          await BankingService.deleteAccount(id);

          const accounts = get().accounts.filter((acc) => acc.id !== id);

          set({
            accounts,
            selectedAccount: get().selectedAccount?.id === id ? null : get().selectedAccount,
            isLoading: false,
          });
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      freezeAccount: async (id, reason) => {
        set({ isLoading: true, error: null });

        try {
          const account = await BankingService.freezeAccount(id, reason);

          const accounts = get().accounts.map((acc) => (acc.id === id ? account : acc));

          set({
            accounts,
            selectedAccount: get().selectedAccount?.id === id ? account : get().selectedAccount,
            isLoading: false,
          });

          return account;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      unfreezeAccount: async (id, reason) => {
        set({ isLoading: true, error: null });

        try {
          const account = await BankingService.unfreezeAccount(id, reason);

          const accounts = get().accounts.map((acc) => (acc.id === id ? account : acc));

          set({
            accounts,
            selectedAccount: get().selectedAccount?.id === id ? account : get().selectedAccount,
            isLoading: false,
          });

          return account;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      fetchAccountBalance: async (id) => {
        set({ isLoading: true, error: null });

        try {
          const balance = await BankingService.getAccountBalance(id);

          set({
            accountBalance: balance,
            isLoading: false,
          });
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      bulkAccountActions: async (action) => {
        set({ isLoading: true, error: null });

        try {
          await BankingService.bulkAccountActions(action);

          // Recharger les comptes après action en lot
          await get().fetchAccounts(get().accountFilters);

          set({ isLoading: false });
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      setSelectedAccount: (account) => set({ selectedAccount: account }),

      setAccountFilters: (filters) => set({ accountFilters: filters }),

      // Actions transactions
      fetchTransactions: async (filters = {}) => {
        set({ isLoadingTransactions: true, transactionsError: null });

        try {
          const response = await BankingService.getTransactions(filters);

          set({
            transactions: response.results,
            transactionsPagination: calculatePagination(response),
            transactionFilters: filters,
            isLoadingTransactions: false,
            lastUpdated: {
              ...get().lastUpdated,
              transactions: new Date().toISOString(),
            },
          });
        } catch (error) {
          set({
            isLoadingTransactions: false,
            transactionsError: transformError(error),
          });
          throw error;
        }
      },

      fetchTransaction: async (id) => {
        set({ isLoading: true, error: null });

        try {
          const transaction = await BankingService.getTransaction(id);

          // Mettre à jour la transaction dans la liste si elle existe
          const transactions = get().transactions.map((txn) => (txn.id === id ? transaction : txn));

          set({
            transactions,
            isLoading: false,
          });

          return transaction;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      createTransaction: async (data) => {
        set({ isLoading: true, error: null });

        try {
          const transaction = await BankingService.createTransaction(data);

          set({
            transactions: [transaction, ...get().transactions],
            isLoading: false,
          });

          return transaction;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      createTransactionForClient: async (data) => {
        set({ isLoading: true, error: null });

        try {
          const transaction = await BankingService.createTransactionForClient(data);

          set({
            transactions: [transaction, ...get().transactions],
            isLoading: false,
          });

          return transaction;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      deposit: async (data) => {
        set({ isLoading: true, error: null });

        try {
          const transaction = await BankingService.deposit(data);

          set({
            transactions: [transaction, ...get().transactions],
            isLoading: false,
          });

          // Invalider le cache du solde pour forcer le rechargement
          if (get().selectedAccount?.id === data.account_id) {
            await get().fetchAccountBalance(data.account_id);
          }

          return transaction;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      withdrawal: async (data) => {
        set({ isLoading: true, error: null });

        try {
          const transaction = await BankingService.withdrawal(data);

          set({
            transactions: [transaction, ...get().transactions],
            isLoading: false,
          });

          // Invalider le cache du solde pour forcer le rechargement
          if (get().selectedAccount?.id === data.account_id) {
            await get().fetchAccountBalance(data.account_id);
          }

          return transaction;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      cancelTransaction: async (id, reason) => {
        set({ isLoading: true, error: null });

        try {
          const transaction = await BankingService.cancelTransaction(id, reason);

          const transactions = get().transactions.map((txn) => (txn.id === id ? transaction : txn));

          set({
            transactions,
            isLoading: false,
          });

          return transaction;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      reverseTransaction: async (id, reason) => {
        set({ isLoading: true, error: null });

        try {
          const transaction = await BankingService.reverseTransaction(id, reason);

          const transactions = get().transactions.map((txn) => (txn.id === id ? transaction : txn));

          set({
            transactions,
            isLoading: false,
          });

          return transaction;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      bulkTransactionActions: async (action) => {
        set({ isLoading: true, error: null });

        try {
          await BankingService.bulkTransactionActions(action);

          // Recharger les transactions après action en lot
          await get().fetchTransactions(get().transactionFilters);

          set({ isLoading: false });
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      setTransactionFilters: (filters) => set({ transactionFilters: filters }),

      // Actions transferts
      fetchTransfers: async (filters = {}) => {
        set({ isLoadingTransfers: true, transfersError: null });

        try {
          const response = await BankingService.getTransfers(filters);

          set({
            transfers: response.results,
            transfersPagination: calculatePagination(response),
            transferFilters: filters,
            isLoadingTransfers: false,
            lastUpdated: {
              ...get().lastUpdated,
              transfers: new Date().toISOString(),
            },
          });
        } catch (error) {
          set({
            isLoadingTransfers: false,
            transfersError: transformError(error),
          });
          throw error;
        }
      },

      fetchTransfer: async (id) => {
        set({ isLoading: true, error: null });

        try {
          const transfer = await BankingService.getTransfer(id);

          // Mettre à jour le transfert dans la liste s'il existe
          const transfers = get().transfers.map((tfr) => (tfr.id === id ? transfer : tfr));

          set({
            transfers,
            isLoading: false,
          });

          return transfer;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      createTransfer: async (data) => {
        set({ isLoading: true, error: null });

        try {
          const transfer = await BankingService.createTransfer(data);

          set({
            transfers: [transfer, ...get().transfers],
            isLoading: false,
          });

          return transfer;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      cancelTransfer: async (id, reason) => {
        set({ isLoading: true, error: null });

        try {
          const transfer = await BankingService.cancelTransfer(id, reason);

          const transfers = get().transfers.map((tfr) => (tfr.id === id ? transfer : tfr));

          set({
            transfers,
            isLoading: false,
          });

          return transfer;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      processTransfer: async (id) => {
        set({ isLoading: true, error: null });

        try {
          const transfer = await BankingService.processTransfer(id);

          const transfers = get().transfers.map((tfr) => (tfr.id === id ? transfer : tfr));

          set({
            transfers,
            isLoading: false,
          });

          return transfer;
        } catch (error) {
          set({
            isLoading: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      setTransferFilters: (filters) => set({ transferFilters: filters }),

      // Actions statistiques
      fetchBankingStats: async () => {
        set({ isLoadingStats: true, error: null });

        try {
          const stats = await BankingService.getBankingStats();

          set({
            bankingStats: stats,
            isLoadingStats: false,
            lastUpdated: {
              ...get().lastUpdated,
              stats: new Date().toISOString(),
            },
          });
        } catch (error) {
          set({
            isLoadingStats: false,
            error: transformError(error),
          });
          throw error;
        }
      },

      // Actions utilitaires
      refreshData: async () => {
        const { accountFilters, transactionFilters, transferFilters } = get();

        await Promise.allSettled([
          get().fetchAccounts(accountFilters),
          get().fetchTransactions(transactionFilters),
          get().fetchTransfers(transferFilters),
          get().fetchBankingStats(),
        ]);
      },

      invalidateCache: () => {
        set({
          lastUpdated: {
            accounts: null,
            transactions: null,
            transfers: null,
            stats: null,
          },
        });
      },
    })),
  ),
);

// Sélecteurs pour optimiser les re-renders
export const useBankingAccounts = () => useBankingStore((state) => state.accounts);
export const useBankingTransactions = () => useBankingStore((state) => state.transactions);
export const useBankingTransfers = () => useBankingStore((state) => state.transfers);
export const useBankingStats = () => useBankingStore((state) => state.bankingStats);
export const useBankingLoading = () =>
  useBankingStore((state) => ({
    isLoading: state.isLoading,
    isLoadingAccounts: state.isLoadingAccounts,
    isLoadingTransactions: state.isLoadingTransactions,
    isLoadingTransfers: state.isLoadingTransfers,
    isLoadingStats: state.isLoadingStats,
  }));
export const useBankingErrors = () =>
  useBankingStore((state) => ({
    error: state.error,
    accountsError: state.accountsError,
    transactionsError: state.transactionsError,
    transfersError: state.transfersError,
  }));
export const useSelectedAccount = () => useBankingStore((state) => state.selectedAccount);
export const useAccountBalance = () => useBankingStore((state) => state.accountBalance);

// Hook personnalisé pour les actions Banking
export const useBankingActions = () =>
  useBankingStore((state) => ({
    // Actions générales
    clearError: state.clearError,
    clearAllErrors: state.clearAllErrors,
    resetStore: state.resetStore,

    // Actions comptes
    fetchAccounts: state.fetchAccounts,
    fetchAccount: state.fetchAccount,
    createAccount: state.createAccount,
    createAccountForClient: state.createAccountForClient,
    updateAccount: state.updateAccount,
    deleteAccount: state.deleteAccount,
    freezeAccount: state.freezeAccount,
    unfreezeAccount: state.unfreezeAccount,
    fetchAccountBalance: state.fetchAccountBalance,
    bulkAccountActions: state.bulkAccountActions,
    setSelectedAccount: state.setSelectedAccount,
    setAccountFilters: state.setAccountFilters,

    // Actions transactions
    fetchTransactions: state.fetchTransactions,
    fetchTransaction: state.fetchTransaction,
    createTransaction: state.createTransaction,
    createTransactionForClient: state.createTransactionForClient,
    deposit: state.deposit,
    withdrawal: state.withdrawal,
    cancelTransaction: state.cancelTransaction,
    reverseTransaction: state.reverseTransaction,
    bulkTransactionActions: state.bulkTransactionActions,
    setTransactionFilters: state.setTransactionFilters,

    // Actions transferts
    fetchTransfers: state.fetchTransfers,
    fetchTransfer: state.fetchTransfer,
    createTransfer: state.createTransfer,
    cancelTransfer: state.cancelTransfer,
    processTransfer: state.processTransfer,
    setTransferFilters: state.setTransferFilters,

    // Actions statistiques
    fetchBankingStats: state.fetchBankingStats,

    // Actions utilitaires
    refreshData: state.refreshData,
    invalidateCache: state.invalidateCache,
  }));
