import { useCallback, useContext, useEffect, useMemo, useState, type ReactElement } from 'react';
import { format } from 'date-fns';
import { Loader2 } from 'lucide-react';
import { useQuery } from 'urql';
import type { RowSelectionState } from '@tanstack/react-table';
import {
  ChargeFilterType,
  VatMonthlyReportDocument,
  type VatReportFilter,
} from '../../../gql/graphql.js';
import { dedupeFragments, type TimelessDateString } from '../../../helpers/index.js';
import { useUrlQuery } from '../../../hooks/use-url-query.js';
import { FiltersContext } from '../../../providers/filters-context.js';
import { UserContext } from '../../../providers/user-provider.js';
import { MergeChargesButton } from '../../common/index.js';
import { PageLayout } from '../../layout/page-layout.js';
import { BusinessTripsTable } from './business-trips-table.js';
import { ExpensesTable } from './expenses-section/expenses-table.js';
import { IncomeTable } from './income-section/income-table.js';
import { MiscTable } from './misc-table.js';
import { MissingInfoTable } from './missing-info-table.js';
import { PCNGenerator } from './pcn-generator.js';
import { ReportSummary } from './report-summary.js';
import { VatMonthlyReportFilter } from './vat-monthly-report-filters.js';

// eslint-disable-next-line @typescript-eslint/no-unused-expressions -- used by codegen
/* GraphQL */ `
  query VatMonthlyReport($filters: VatReportFilter) {
    vatReport(filters: $filters) {
      ...VatReportSummaryFields
      ...VatReportIncomeFields
      ...VatReportExpensesFields
      ...VatReportMissingInfoFields
      ...VatReportMiscTableFields
      ...VatReportBusinessTripsFields
    }
  }
`;

// Dedupe the fragments once at module load: the document never depends on render
// state, so hoisting it keeps a stable query reference across renders (a fresh
// object each render would re-key the urql operation) without an empty-deps hook.
const vatMonthlyReportQuery = dedupeFragments(VatMonthlyReportDocument);

export const VatMonthlyReport = (): ReactElement => {
  const { get } = useUrlQuery();
  const { setFiltersContext } = useContext(FiltersContext);
  const { userContext } = useContext(UserContext);
  const [filter, setFilter] = useState<VatReportFilter>(
    get('vatMonthlyReportFilters')
      ? (JSON.parse(
          decodeURIComponent(get('vatMonthlyReportFilters') as string),
        ) as VatReportFilter)
      : {
          financialEntityId: userContext?.context.adminBusinessId ?? '',
          monthDate: format(new Date(), 'yyyy-MM-15') as TimelessDateString,
        },
  );
  // Single selection source of truth, shared by every sub-table so charges picked in one table
  // participate in cross-table actions (merge, and future ledger/tag actions). Keyed by charge id
  // to match `NewChargesTable`'s `getRowId`.
  const [rowSelection, setRowSelection] = useState<RowSelectionState>({});

  // auto default admin business if not set
  useEffect(() => {
    if (
      (!filter.financialEntityId || filter.financialEntityId === '') &&
      userContext?.context.adminBusinessId
    ) {
      setFilter({
        ...filter,
        financialEntityId: userContext.context.adminBusinessId,
      });
    }
  }, [filter, userContext?.context.adminBusinessId]);

  // fetch data
  const [{ data, fetching }] = useQuery({
    query: vatMonthlyReportQuery,
    variables: {
      filters: filter,
    },
    pause: filter.financialEntityId === '',
  });

  const selectedChargeIds = useMemo(
    () => Object.entries(rowSelection).flatMap(([id, selected]) => (selected ? [id] : [])),
    [rowSelection],
  );

  // Legacy `Set`-based selection API still consumed by the income/expenses tables, derived from the
  // shared `rowSelection` so all sub-tables stay in sync until those tables are migrated too.
  const mergeSelectedChargesSet = useMemo(() => new Set(selectedChargeIds), [selectedChargeIds]);

  const toggleMergeCharge = useCallback((chargeId: string) => {
    setRowSelection(old => {
      const next = { ...old };
      if (next[chargeId]) {
        delete next[chargeId];
      } else {
        next[chargeId] = true;
      }
      return next;
    });
  }, []);

  const onResetMerge = useCallback((): void => {
    setRowSelection({});
  }, []);

  useEffect(() => {
    setFiltersContext(
      <div className="flex flex-row gap-2">
        <PCNGenerator filter={filter} isLoading={fetching} />
        <VatMonthlyReportFilter filter={{ ...filter }} setFilter={setFilter} />
        <MergeChargesButton
          selected={selectedChargeIds.map(id => ({
            id,
            onChange: (): void => {
              return;
            },
          }))}
          resetMergeList={onResetMerge}
        />
      </div>,
    );
  }, [filter, fetching, setFiltersContext, setFilter, selectedChargeIds, onResetMerge]);

  return (
    <PageLayout
      title={`VAT Monthly Report${filter.monthDate ? `, ${format(new Date(filter.monthDate), 'MMMM yyyy')}` : ''}`}
    >
      {fetching ? (
        <Loader2 className="h-10 w-10 animate-spin mr-2 self-center" />
      ) : (
        <div className="min-h-screen">
          <div className="space-y-4">
            {filter.financialEntityId && filter.monthDate && (
              <ReportSummary data={data?.vatReport} />
            )}

            {filter.chargesType !== ChargeFilterType.Expense && (
              <IncomeTable
                data={data?.vatReport}
                toggleMergeCharge={toggleMergeCharge}
                mergeSelectedCharges={mergeSelectedChargesSet}
              />
            )}

            {filter.chargesType !== ChargeFilterType.Income && (
              <ExpensesTable
                data={data?.vatReport}
                toggleMergeCharge={toggleMergeCharge}
                mergeSelectedCharges={mergeSelectedChargesSet}
              />
            )}

            <MissingInfoTable
              data={data?.vatReport}
              rowSelection={rowSelection}
              onRowSelectionChange={setRowSelection}
            />

            <BusinessTripsTable
              data={data?.vatReport}
              rowSelection={rowSelection}
              onRowSelectionChange={setRowSelection}
            />

            <MiscTable
              data={data?.vatReport}
              rowSelection={rowSelection}
              onRowSelectionChange={setRowSelection}
            />
          </div>
        </div>
      )}
    </PageLayout>
  );
};
