import React, { useCallback, useEffect, useRef, useState } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';

import AuditLogsList from '@northern.tech/common/auditlogs/AuditLogsList';
import { AuditlogsView } from '@northern.tech/common/auditlogs/AuditlogsView';
import { ActionDescriptor, TimeWrapper, TypeDescriptor, ViewDetails } from '@northern.tech/common/auditlogs/ColumnComponents';
import EventDetailsDrawer from '@northern.tech/common/auditlogs/EventDetailsDrawer';
import EventDetailsDrawerContentMap from '@northern.tech/common/auditlogs/EventDetailsDrawerContentMap';
import { SORTING_OPTIONS, TIMEOUTS } from '@northern.tech/store/constants';
import { useLocationParams } from '@northern.tech/store/liststatehook';
import { commonProcessor, formatAuditlogs, formatPageState, parseDateParams } from '@northern.tech/store/locationutils';
import { getAuditLog, getAuditLogEntry, getAuditLogSelectionState, getGroupNames, getUserCapabilities } from '@northern.tech/store/selectors';
import { createDownload } from '@northern.tech/utils/helpers';
import dayjs from 'dayjs';

import AuditLogFilter from './AuditLogFilter.jsx';
import { ChangeDescriptor, UserDescriptor } from './ColumnComponents';
import EventDetailsFallbackComponent from './EventDetailsFallbackComponent';
import { AUDIT_LOGS_TYPES, dateFormat } from './store/constants';
import { getAuditLogDetailsOptions } from './store/organizationSlice/selectors';
import { getAuditLogNameByType, getAuditLogs, getAuditLogsCsvLink, setAuditlogsState } from './store/organizationSlice/thunks';
import { getUserList } from './store/usersSlice/thunks';

const parseAuditlogsQuery = (params, { today, tonight }) => {
  const type = AUDIT_LOGS_TYPES.find(typeObject => typeObject.value === params.get('objectType'));
  const { endDate, startDate } = parseDateParams(params, today, tonight);
  return {
    detail: params.get('objectId'),
    endDate,
    startDate,
    type,
    user: params.get('userId')
  };
};

const isUserOptionEqualToValue = ({ email, id }, value) => id === value || email === value || email === value?.email;

export const canAccess = () => {
  const element = document.querySelector('.auditLogEntryPoint');
  return element && element.getAttribute('data-has-access') === 'true';
};

const locationDefaults = { sort: { direction: SORTING_OPTIONS.desc } };

export const Auditlog = () => {
  const [csvLoading, setCsvLoading] = useState(false);
  const navigate = useNavigate();

  const isInitialized = useRef();
  const [locationParams, setLocationParams] = useLocationParams(
    'auditlogs',
    { today: null, tonight: null, defaults: locationDefaults },
    {
      auditlogs: {
        format: formatAuditlogs,
        locate: () => undefined,
        parse: parseAuditlogsQuery
      },
      common: {
        format: formatPageState,
        locate: () => undefined,
        parse: commonProcessor
      }
    }
  );

  const dispatch = useDispatch();
  const events = useSelector(getAuditLog);
  const eventItem = useSelector(getAuditLogEntry);
  const groups = useSelector(getGroupNames);
  const selectionState = useSelector(getAuditLogSelectionState);
  const userCapabilities = useSelector(getUserCapabilities);
  const auditLogDetailsOptions = useSelector(getAuditLogDetailsOptions);
  const users = useSelector(state => state.users.byId);
  const { canReadUsers } = userCapabilities;
  const [detailsReset, setDetailsReset] = useState('');
  const [dirtyField, setDirtyField] = useState('');
  const { detail, perPage, endDate, user, sort, startDate, type, total, isLoading = true } = selectionState;
  const [hasAuditlogs, setHasAuditlogs] = useState();

  useEffect(() => {
    setHasAuditlogs(canAccess());
  }, []);

  useEffect(() => {
    if (!hasAuditlogs || !isInitialized.current) {
      return;
    }
    setLocationParams({ pageState: selectionState });
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [detail, endDate, hasAuditlogs, perPage, selectionState.page, selectionState.selectedId, setLocationParams, startDate, type, user]);

  useEffect(() => {
    if (type?.value) {
      dispatch(getAuditLogNameByType(type?.value));
    }

    if (isInitialized.current) {
      setDetailsReset('detail');
      setTimeout(() => setDetailsReset(''), TIMEOUTS.debounceShort);
    }
  }, [dispatch, type?.value]);

  useEffect(() => {
    if (canReadUsers) {
      dispatch(getUserList());
    }
  }, [canReadUsers, dispatch]);

  const initAuditlogState = useCallback(
    (result, state) => {
      // check if filter is changed to show clear filter button
      ['startDate', 'endDate', 'type'].map(field => {
        if (state?.[field]) {
          setDirtyField(field);
        }
      });
      dispatch(setAuditlogsState(state));
      // the timeout here is slightly longer than the debounce in the filter component, otherwise the population of the filters with the url state would trigger a reset to page 1
      setTimeout(() => (isInitialized.current = true), TIMEOUTS.oneSecond + TIMEOUTS.debounceDefault);
    },
    [dispatch]
  );

  useEffect(() => {
    if (!hasAuditlogs || isInitialized.current !== undefined) {
      return;
    }
    isInitialized.current = false;
    const { detail, type, user } = locationParams;
    let state = { ...locationParams };

    dispatch(getAuditLogs({ page: state.page ?? 1, perPage: 50, user, type, detail }))
      .unwrap()
      .then(({ payload: result }) => initAuditlogState(result, state));
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [dispatch, hasAuditlogs, JSON.stringify(events), JSON.stringify(locationParams), initAuditlogState]);

  const createCsvDownload = () => {
    let datePart = '';
    const from = dayjs(startDate);
    if (from.isValid()) {
      datePart += `-from-${dayjs(startDate).format(dateFormat)}`;
    }

    const to = dayjs(endDate);
    if (to.isValid()) {
      datePart += `-to-${dayjs(endDate).format(dateFormat)}`;
    }

    setCsvLoading(true);
    dispatch(getAuditLogsCsvLink())
      .unwrap()
      .then(address => {
        createDownload(encodeURI(address), `CFEngine-AuditLog${datePart}.csv`);
        setCsvLoading(false);
      });
  };

  const onChangeSorting = () => {
    const currentSorting = sort.direction === SORTING_OPTIONS.desc ? SORTING_OPTIONS.asc : SORTING_OPTIONS.desc;
    dispatch(setAuditlogsState({ page: 1, sort: { direction: currentSorting } }));
  };

  const onChangePagination = (page, currentPerPage = perPage) => dispatch(setAuditlogsState({ page, perPage: currentPerPage }));

  const onIssueSelection = selectedIssue => navigate(`/${selectedIssue.id}`);

  const onFiltersChange = useCallback(
    ({ endDate, detail, startDate, user, type }) => {
      if (!isInitialized.current) {
        return;
      }
      const selectedUser = Object.values(users).find(item => isUserOptionEqualToValue(item, user));

      dispatch(setAuditlogsState({ page: 1, detail, startDate, endDate, user: selectedUser, type }));
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [dispatch, JSON.stringify(users)]
  );

  return (
    <AuditlogsView
      createCsvDownload={createCsvDownload}
      hasAuditlogs={hasAuditlogs}
      total={total}
      csvLoading={csvLoading}
      auditLogsFilter={
        <AuditLogFilter
          groups={groups}
          users={users}
          disabled={!hasAuditlogs}
          onFiltersChange={onFiltersChange}
          detailsReset={detailsReset}
          selectionState={selectionState}
          auditLogsTypes={AUDIT_LOGS_TYPES}
          dirtyField={dirtyField}
          setDirtyField={setDirtyField}
          detailOptions={auditLogDetailsOptions}
        />
      }
    >
      {!!total && (
        <AuditLogsList
          items={events}
          onChangePage={onChangePagination}
          onChangeRowsPerPage={newPerPage => onChangePagination(1, newPerPage)}
          onChangeSorting={onChangeSorting}
          selectionState={selectionState}
          userCapabilities={userCapabilities}
          auditLogColumns={[
            { title: 'Timestamp', sortable: true, render: TimeWrapper },
            { title: 'Performed by', sortable: false, render: UserDescriptor },
            { title: 'Action', sortable: false, render: ActionDescriptor },
            { title: 'Type', sortable: false, render: TypeDescriptor },
            { title: 'Affected', sortable: false, render: ChangeDescriptor },
            { title: '', sortable: false, render: (item, index) => <ViewDetails item={item} index={index} onIssueSelection={onIssueSelection} /> }
          ]}
        />
      )}
      {!(isLoading || total) && hasAuditlogs && (
        <div className="dashboard-placeholder">
          <p>No log entries were found.</p>
          <p>Try adjusting the filters.</p>
        </div>
      )}
      {hasAuditlogs === false && (
        <div className="user_notification user_notification_warning margin-top">
          You don&#39;t have permission to access the audit log. Contact an admin to gain access.
        </div>
      )}
      <EventDetailsDrawer
        mapChangeToContent={EventDetailsDrawerContentMap}
        fallbackComponent={EventDetailsFallbackComponent}
        eventItem={eventItem}
        open={Boolean(eventItem)}
        onClose={() => onIssueSelection()}
      />
    </AuditlogsView>
  );
};

export default Auditlog;
