import { useContext, useMemo, useState, type ReactElement } from 'react';
import { Loader2 } from 'lucide-react';
import { useQuery } from 'urql';
import {
  flexRender,
  getCoreRowModel,
  getSortedRowModel,
  useReactTable,
  type ColumnDef,
  type SortingState,
} from '@tanstack/react-table';
import { getFragmentData } from '@/gql/fragment-masking.js';
import { UserContext } from '@/providers/user-provider.js';
import {
  Currency,
  DepositTransactionFieldsFragmentDoc,
  SharedDepositTransactionsDocument,
} from '../../gql/graphql.js';
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../ui/table.js';
import { columns, type DepositTransactionRowType } from './columns.js';
import { DepositReassignDialog } from './deposit-reassign-dialog.js';
import { identifyInterestTransactionIds } from './utils.js';

// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
/* GraphQL */ `
  query SharedDepositTransactions($depositId: UUID!) {
    deposit(id: $depositId) {
      id
      currency
      metadata {
        id
        transactions {
          id
          ...DepositTransactionFields
        }
      }
    }
  }
`;

const currencyMap: Partial<
  Record<Currency, 'eur' | 'gbp' | 'cad' | 'jpy' | 'aud' | 'sek' | 'usd'>
> = {
  [Currency.Eur]: 'eur',
  [Currency.Gbp]: 'gbp',
  [Currency.Cad]: 'cad',
  [Currency.Jpy]: 'jpy',
  [Currency.Aud]: 'aud',
  [Currency.Sek]: 'sek',
  [Currency.Usd]: 'usd',
};

type Props = {
  depositId: string;
  enableReassign?: boolean;
  refetch?: () => void;
};

export function DepositsTransactionsTable({
  depositId,
  enableReassign = false,
  refetch,
}: Props): ReactElement {
  const { userContext } = useContext(UserContext);
  const [sorting, setSorting] = useState<SortingState>([{ id: 'date', desc: false }]);

  const [{ data, fetching }] = useQuery({
    query: SharedDepositTransactionsDocument,
    variables: { depositId },
  });

  const tableData: DepositTransactionRowType[] = useMemo(() => {
    if (!data?.deposit?.metadata.transactions) {
      return [];
    }

    const transactions = data.deposit.metadata.transactions.map(rawTx =>
      getFragmentData(DepositTransactionFieldsFragmentDoc, rawTx),
    );

    // Identify interest transactions using shared client helper
    const interestTransactionIds = identifyInterestTransactionIds(transactions, {
      getId: tx => tx.id,
      getChargeId: tx => tx.chargeId,
      getAmount: tx => Number(tx.amount.raw ?? 0),
    });

    // Sort by date ascending for cumulative calculation
    const sortedTransactions = [...transactions].sort((a, b) => {
      const dateA = a.eventDate ? new Date(a.eventDate).getTime() : 0;
      const dateB = b.eventDate ? new Date(b.eventDate).getTime() : 0;
      return dateA - dateB;
    });

    let cumulativeBalance = 0;
    let totalInterest = 0;
    let localCumulativeBalance = 0;

    return sortedTransactions.map(tx => {
      let rate: number | null = null;
      const currencyKey = currencyMap[tx.amount.currency];
      if (currencyKey) {
        rate = tx.debitExchangeRates?.[currencyKey] ?? tx.eventExchangeRates?.[currencyKey] ?? null;
      }
      const amount = Number(tx.amount.raw);
      const localAmount = amount * (rate ?? 1);
      const localCurrency = (userContext?.context.defaultLocalCurrency as Currency) ?? Currency.Ils;
      const isInterest = interestTransactionIds.has(tx.id);

      // Only include non-interest transactions in balance
      if (isInterest) {
        totalInterest += amount;
      } else {
        cumulativeBalance += amount;
        localCumulativeBalance += localAmount;
      }

      return {
        ...tx,
        cumulativeBalance,
        localCumulativeBalance,
        localAmount,
        localCurrency,
        totalInterest,
        isInterest,
      };
    });
  }, [data?.deposit, userContext]);

  const columnsWithActions: ColumnDef<DepositTransactionRowType>[] = useMemo(() => {
    const defaultLocalCurrency =
      (userContext?.context.defaultLocalCurrency as Currency) ?? Currency.Ils;

    const originEqualsLocal =
      !!data?.deposit?.currency && data.deposit.currency === defaultLocalCurrency;

    const baseColumns: ColumnDef<DepositTransactionRowType>[] = originEqualsLocal
      ? columns.filter(c => c.id !== 'amount' && c.id !== 'cumulativeBalance')
      : columns;

    const actionColumns: ColumnDef<DepositTransactionRowType>[] = enableReassign
      ? [
          {
            id: 'actions',
            header: 'Actions',
            cell: ({ row }) => (
              <DepositReassignDialog
                depositId={depositId}
                chargeId={row.original.chargeId}
                refetch={refetch}
              />
            ),
          },
        ]
      : [];

    return [...baseColumns, ...actionColumns];
  }, [
    enableReassign,
    depositId,
    refetch,
    data?.deposit?.currency,
    userContext?.context.defaultLocalCurrency,
  ]);

  const table = useReactTable({
    data: tableData,
    columns: columnsWithActions,
    getCoreRowModel: getCoreRowModel(),
    onSortingChange: setSorting,
    getSortedRowModel: getSortedRowModel(),
    state: { sorting },
  });

  if (fetching) {
    return (
      <div className="flex h-64 w-full items-center justify-center">
        <Loader2 className="size-10 animate-spin" />
      </div>
    );
  }

  return (
    <Table>
      <TableHeader>
        {table.getHeaderGroups().map(headerGroup => (
          <TableRow key={headerGroup.id}>
            {headerGroup.headers.map(header => (
              <TableHead key={header.id} colSpan={header.colSpan}>
                {header.isPlaceholder
                  ? null
                  : flexRender(header.column.columnDef.header, header.getContext())}
              </TableHead>
            ))}
          </TableRow>
        ))}
      </TableHeader>
      <TableBody>
        {table.getRowModel().rows?.length ? (
          table.getRowModel().rows.map(row => (
            <TableRow key={row.id}>
              {row.getVisibleCells().map(cell => (
                <TableCell key={cell.id}>
                  {flexRender(cell.column.columnDef.cell, cell.getContext())}
                </TableCell>
              ))}
            </TableRow>
          ))
        ) : (
          <TableRow>
            <TableCell colSpan={columnsWithActions.length} className="h-24 text-center">
              No transactions in this deposit.
            </TableCell>
          </TableRow>
        )}
      </TableBody>
    </Table>
  );
}
