/* eslint-disable @typescript-eslint/no-explicit-any */
import React, { useCallback, useEffect } from 'react';

import type { APIOptions } from '../../utils/types';
import type { GridlerColumn } from '../Column';

import {
  type FetchAPIOperation,
  GoAPIHeaders,
  type GoAPIOperation,
} from '../../utils/golang-restapi-v2';
import { useGridlerStore } from '../GridlerStore';

export interface GlidlerAPIAdaptorForGoLangv2Props<T = unknown> extends APIOptions {
  filter?: string;
  hotfields?: Array<string>;
  initialData?: Array<T>;
  options?: Array<GoAPIOperation>;
}

function _GlidlerAPIAdaptorForGoLangv2<T = unknown>(props: GlidlerAPIAdaptorForGoLangv2Props<T>) {
  const [setStateFN, setState, getState, addError, mounted] = useGridlerStore((s) => [
    s.setStateFN,
    s.setState,
    s.getState,
    s.addError,
    s.mounted,
  ]);

  const useAPIQuery: (index: number) => Promise<any> = useCallback(
    async (index: number) => {
      const columns = getState('columns');
      const colSort = getState('colSort');
      const pageSize = getState('pageSize');
      const colFilters = getState('colFilters');
      const searchStr = getState('searchStr');
      const searchFields = getState('searchFields');
      const _active_requests = getState('_active_requests');
      const keyField = getState('keyField');
      setState('loadingData', true);
      try {
        //console.log('APIAdaptorGoLangv2', { _active_requests, index, pageSize, props });
        if (props && props.url) {
          const head = new Headers();

          head.set('Authorization', `Token ${props.authtoken}`);
          const ops: FetchAPIOperation[] = [
            { type: 'limit', value: String(pageSize ?? 50) },
            { type: 'offset', value: String((pageSize ?? 50) * index) },
          ];

          if (colSort?.length && colSort.length > 0) {
            ops.push({
              type: 'sort',
              value: colSort
                ?.map((sort: any) => `${sort.id} ${sort.direction}`)
                .reduce((acc: any, val: any) => `${acc},${val}`),
            });
          }

          colFilters
            ?.filter((f) => f.value?.length > 0)
            ?.forEach((filter: any) => {
              if (filter.value && filter.value !== '') {
                ops.push({
                  name: `${filter.id}`,
                  op: filter.operator,
                  type: 'searchop',
                  value: filter.value,
                });
              }
            });

          if (searchStr && searchStr !== '') {
            columns
              ?.filter(
                (f) =>
                  !f.disableFilter &&
                  !f.disableSearch &&
                  !f.virtual &&
                  f.id &&
                  ((searchFields ?? []).length == 0 || searchFields?.includes(f.id))
              )
              ?.forEach((filter: any) => {
                ops.push({
                  name: `${filter.id ?? ''}`,
                  op: 'contains',
                  type: 'searchor',
                  value: searchStr,
                });
              });
          }

          if (props.filter && props.filter !== '') {
            ops.push({
              name: 'sql_filter',
              type: 'custom-sql-w',
              value: props.filter,
            });
          }

          if ((props.options ?? []).length > 0) {
            ops.push(...(props.options ?? []));
          }

          const col_ids =
            columns
              ?.filter((col) => !col.virtual)
              ?.map((col: GridlerColumn) => {
                return col.id;
              }) ?? [];

          if (props.hotfields && props.hotfields.length > 0) {
            col_ids?.push(props.hotfields.join(','));
          }

          if (keyField) {
            if (!col_ids.includes(keyField)) {
              col_ids.push(keyField);
            }
          }

          if (col_ids && col_ids.length > 0) {
            ops.push({
              type: 'select-fields',
              value: col_ids.join(','),
            });
          }

          if (ops && ops.length > 0) {
            const optionHeaders = GoAPIHeaders(ops);
            for (const oh in GoAPIHeaders(ops)) {
              head.set(oh, optionHeaders[oh]);
            }
          }

          const currentRequestIndex = _active_requests?.findIndex((f) => f.page === index) ?? -1;
          _active_requests?.forEach((r) => {
            if ((r.page >= 0 && r.page < index - 2) || (index >= 0 && r.page > index + 2)) {
              r.controller?.abort?.();
            }
          });

          if (
            _active_requests &&
            currentRequestIndex >= 0 &&
            _active_requests[currentRequestIndex]
          ) {
            //console.log(`Already queued ${index}`, index, s._active_requests);
            setState('loadingData', false);
            return undefined;
          }

          const controller = new AbortController();
          await setStateFN('_active_requests', (cv) => [
            ...(cv ?? []),
            { controller, page: index },
          ]);

          const res = await fetch(
            `${props.url}?x-limit=${String(pageSize ?? 50)}&x-offset=${String((pageSize ?? 50) * index)}`,
            {
              headers: head,
              method: 'GET',
              signal: controller?.signal,
            }
          );

          if (res.ok) {
            const cr = res.headers.get('Content-Range')?.split('/');
            if (cr?.[1] && parseInt(cr[1], 10) > 0) {
              setState('total_rows', parseInt(cr[1], 10));
            }

            const data = await res.json();
            setState('loadingData', false);
            return data ?? [];
          }
          addError(`${res.status} ${res.statusText}`, 'api', props.url);

          await setStateFN('_active_requests', (cv) => [
            ...(cv ?? []).filter((f) => f.page !== index),
          ]);
        }
        // eslint-disable-next-line @typescript-eslint/no-unused-vars
      } catch (_e) {
        //console.log('APIAdaptorGoLangv2 error', e);
        //addError(`Error: ${e}`, 'api', props.url);
      }
      setState('loadingData', false);
      return [];
    },
    [
      getState,
      props.authtoken,
      props.url,
      props.filter,
      JSON.stringify(props.options),
      setState,
      setStateFN,
      addError,
    ]
  );

  const askAPIRowNumber: (key: string) => Promise<null | number> = useCallback(
    async (key: string) => {
      const colFilters = getState('colFilters');
      if (!key || key === '' || !props.url) {
        return null;
      }
      //console.log('APIAdaptorGoLangv2', { key, props });
      if (props && props.url) {
        const head = new Headers();
        const ops: FetchAPIOperation[] = [
          { type: 'limit', value: String(10) },
          { type: 'fetch-rownumber', value: key },
        ];

        head.set('Authorization', `Token ${props.authtoken}`);

        if (colFilters?.length && colFilters.length > 0) {
          colFilters
            ?.filter((f) => f.value?.length > 0)
            ?.forEach((filter: any) => {
              if (filter.value && filter.value !== '') {
                head.set(`x-searchop-${filter.operator}-${filter.id}`, `${filter.value}`);
              }
            });
        }

        if (props.filter && props.filter !== '') {
          ops.push({
            name: 'sql_filter',
            type: 'custom-sql-w',
            value: `(${props.filter})`,
          });
        }

        if (props.options && props.options.length > 0) {
          const optionHeaders = GoAPIHeaders(props.options);
          for (const oh in optionHeaders) {
            head.set(oh, optionHeaders[oh]);
          }
        }

        if (ops && ops.length > 0) {
          const optionHeaders = GoAPIHeaders(ops);
          for (const oh in GoAPIHeaders(ops)) {
            head.set(oh, optionHeaders[oh]);
          }
        }

        const controller = new AbortController();

        const res = await fetch(`${props.url}?x-fetch-rownumber=${key}`, {
          headers: head,
          method: 'GET',
          signal: controller?.signal,
        });

        if (res.ok) {
          const data = await res.json();

          return data?.[0]?._rownumber ?? data?._rownumber ?? 0;
        }
        addError(`${res.status} ${res.statusText}`, 'api', props.url);
      }
      return [];
    },
    [props.url, props.authtoken, props.filter, JSON.stringify(props.options), getState, addError]
  );

  //Reset the function in the store.
  useEffect(() => {
    setState('useAPIQuery', useAPIQuery);
    setState('askAPIRowNumber', askAPIRowNumber);
    const isValuesInPages = getState('isValuesInPages');

    const _refresh = getState('_refresh');
    if (!isValuesInPages) {
      setState('values', []);
    }

    //Reset the loaded pages to new rules
    _refresh?.().then(() => {
      const onChange = getState('onChange');
      const getGridSelectedRows = getState('getGridSelectedRows');
      if (onChange && typeof onChange === 'function') {
        const buffers = getGridSelectedRows?.();
        onChange(buffers);
      }
    });

    getState('refreshCells')?.();
  }, [props.url, props.authtoken, props.filter, JSON.stringify(props.options), mounted, setState]);

  return <></>;
}

//The computer component does not need to be recalculated on every render, so we use React.memo to prevent unnecessary re-renders.
export const GlidlerAPIAdaptorForGoLangv2 = React.memo(_GlidlerAPIAdaptorForGoLangv2);

GlidlerAPIAdaptorForGoLangv2.displayName = 'Gridler-GlidlerAPIAdaptorForGoLangv2';
