import { useEffect, useState } from 'react';
import { Helmet } from 'react-helmet-async';
import { FormProvider, useForm } from 'react-hook-form';
import { useMutation, useQuery, type UseQueryResult } from 'react-query';
import { useLocation } from 'react-router-dom';

import { isEmpty } from 'lodash-es';
import { type AxiosResponse } from 'axios';
import Box from '@material-hu/mui/Box';
import Container from '@material-hu/mui/Container';
import Divider from '@material-hu/mui/Divider';
import Tab from '@material-hu/mui/Tab';
import Tabs from '@material-hu/mui/Tabs';
import Typography from '@material-hu/mui/Typography';

import Button, {
  type ButtonProps,
} from '@material-hu/components/design-system/Buttons/Button';
import useHuSnackbar from '@material-hu/components/design-system/Snackbar';

import { TIMEZONES } from 'src/constants/timezones';
import useAuth from 'src/contexts/JWTContext';
import useFeatureFlag from 'src/hooks/useFeatureFlag';
import useGeneralError from 'src/hooks/useGeneralError';
import * as instancesService from 'src/services/instancesService';
import { FeatureFlags } from 'src/types/featureFlags';
import { type Instance, type MeInstance } from 'src/types/instance';
import { SettingsTabKeys } from 'src/types/settings';
import { insertIf } from 'src/utils/arrayUtils';
import { removeURLParams, signedUpload } from 'src/utils/filesUtils';
import { getDirtyValues } from 'src/utils/formUtils';
import { formatTitle } from 'src/utils/helmetUtils';
import { useLokaliseTranslation } from 'src/utils/i18n';
import { LogEvents, logEvent } from 'src/utils/logging';
import {
  getCorrectPasswordType,
  getPasswordTypeOptions,
} from 'src/utils/login';
import { hasAnyPermissions, UserPermissions } from 'src/utils/permissions';
import {
  instanceHasRoles,
  permissionsObjectToArray,
} from 'src/utils/userUtils';

import CircularProgress from 'src/components/CircularProgress';

import { appsKeys } from '../../Apps/queries';

import DepartmentsSettings from './components/DepartmentsSettings';
import GeneralSettings from './components/GeneralSettings';
import PermissionsSettings from './components/PermissionsSettings';
import PositionsSettings from './components/PositionsSettings';
import ProfilesSettings from './components/ProfilesSettings';
import RolesSettings from './components/RolesSettings';
import TermsAndConditions from './components/TermsAndConditions';
import { type FieldValues } from './form';
import useDepartmentsPermissions from './permissions/useDepartmentsPermissions';

const toMeInstance = (instance: Instance): MeInstance => ({
  ...instance,
  passwordInDocumentsTimer: instance.passwordInDocumentsTimer ?? undefined,
});

const formatInstance = (ins: Instance) => {
  const capabilities: Record<string, boolean> = {};
  ins.capabilities?.forEach(capability => (capabilities[capability] = true));
  return {
    name: ins.name,
    color: ins.color,
    capabilities,
    defaultCurrencyCode: ins.defaultCurrencyCode || '',
    hasTermsAndConditions: ins.hasTermsAndConditions,
    terms: ins.termsAndConditions?.body || '',
    adminEmail: ins.adminEmail || '',
    logo: { url: ins.logo, file: null },
    language: ins.language,
    allowSocialNetworks: ins.allowSocialNetworks,
    allowNickname: ins.allowNickname,
    allowBirthdate: ins.allowBirthdate,
    allowPhoneCall: ins.allowPhoneCall,
    allowHiringDate: ins.allowHiringDate,
    editableName: ins.editableName,
    editableEmail: ins.editableEmail,
    editableBirthdate: ins.editableBirthdate,
    editableProfilePicture: ins.editableProfilePicture,
    editableCoverPicture: ins.editableCoverPicture,
    ssoDomains: ins.ssoDomains,
    forceSSO: ins.forceSSO,
    usersCreationBySSOEnabled: !ins.usersCreationBySSOEnabled,
    viewBossInProfile: ins.relationshipsInProfile.includes('BOSS'),
    relationshipsInProfileVisibility: ins.relationshipsInProfileVisibility,
    emailInProfileVisibility: ins.emailInProfileVisibility,
    defaultCoverPicture: ins.defaultCoverPicture,
    passwordType: getPasswordTypeOptions(ins.passwordType),
    timezone: TIMEZONES.find(tz => tz.id === ins.timezone),
  };
};

type SettingsFormProps = {
  instance: Instance;
  canViewGeneralTabs: boolean;
  canViewDepartments: boolean;
  isCerberusEnabled: boolean;
  refetchInstance: UseQueryResult<AxiosResponse<Instance>>['refetch'];
};

const SettingsForm = ({
  instance,
  canViewGeneralTabs,
  canViewDepartments,
  isCerberusEnabled,
  refetchInstance,
}: SettingsFormProps) => {
  const { t } = useLokaliseTranslation('settings');
  const { state } = useLocation() as { state: { selectedTab?: string } };
  const { enqueueSnackbar } = useHuSnackbar();
  const showGeneralError = useGeneralError();
  const { updateInstance, user: loggedUser } = useAuth();

  const form = useForm<FieldValues>({
    defaultValues: formatInstance(instance),
    mode: 'onChange',
  });
  const {
    formState: { dirtyFields, isSubmitting, isDirty },
    reset,
    handleSubmit,
    trigger,
  } = form;

  const mutation = useMutation(
    async (dirtyValues: Partial<FieldValues>) => {
      const { terms, hasTermsAndConditions, ...rest } = dirtyValues;
      const restData: any = { ...rest };
      if (rest.capabilities) {
        restData.capabilities = permissionsObjectToArray(rest.capabilities);
      }
      if (rest.timezone) {
        restData.timezone = rest.timezone.id;
      }
      await instancesService.updateInstance(instance.id, restData);
      const result = await refetchInstance();
      return result.data?.data;
    },
    {
      onSuccess: (data: Instance | undefined) => {
        if (!data) return;
        reset(formatInstance(data));
        updateInstance?.(toMeInstance(data));
        enqueueSnackbar({
          title: t('GENERAL.UPDATED_SUCCESS'),
          variant: 'success',
        });
        if (
          ['general', 'terms', 'permissions', 'profiles'].includes(
            currentTab?.section!,
          )
        ) {
          logEvent(LogEvents.SETTINGS_UPDATED, {
            userId: loggedUser?.id,
            instanceId: instance.id,
            section: currentTab?.section!,
          });
        }
      },
      onError: (err: any) => {
        if (err?.response?.data?.code === 'DOMAIN_ALREADY_USED') {
          enqueueSnackbar({
            title: t('GENERAL.DOMAIN_SSO_ERROR'),
            variant: 'error',
          });
        } else {
          showGeneralError(err);
        }
      },
    },
  );

  const handleSave = handleSubmit(async values => {
    const dirtyValues = getDirtyValues(dirtyFields, values);
    const getLogoUrl = async (logo: {
      url: string | null;
      file: File | null;
    }) => {
      if (logo?.url) {
        return logo.url;
      }
      if (logo?.file) {
        const signedURL = await signedUpload(logo.file, true);
        return removeURLParams(signedURL);
      }
      return undefined;
    };

    dirtyValues.logo = await getLogoUrl(dirtyValues.logo);

    if (dirtyFields.allowSocialNetworks) {
      dirtyValues.allowSocialNetworks = values.allowSocialNetworks;
    }

    if (dirtyFields.viewBossInProfile) {
      dirtyValues.relationshipsInProfile = dirtyValues.viewBossInProfile
        ? ['BOSS']
        : [];
    }

    if (dirtyValues.passwordType) {
      dirtyValues.passwordType = getCorrectPasswordType(
        dirtyValues.passwordType,
      );
    }

    if (!isEmpty(dirtyValues)) {
      await mutation.mutateAsync(dirtyValues);
    }
  });

  const tabs = [
    ...insertIf(canViewGeneralTabs, {
      key: SettingsTabKeys.GENERAL,
      label: t('GENERAL.GENERAL'),
      Component: GeneralSettings,
      section: 'general',
    }),
    ...insertIf(canViewGeneralTabs, {
      key: SettingsTabKeys.TERMS_CONDITIONS,
      label: t('GENERAL.TERMS_CONDITIONS'),
      Component: TermsAndConditions,
      section: 'terms',
    }),
    ...insertIf(!isCerberusEnabled && !instanceHasRoles(instance.id), {
      key: SettingsTabKeys.PERMISSIONS,
      label: t('GENERAL.PERMISSIONS'),
      Component: PermissionsSettings,
      section: 'permissions',
    }),
    ...insertIf(isCerberusEnabled, {
      key: SettingsTabKeys.PERMISSIONS,
      label: t('GENERAL.PERMISSIONS'),
      Component: RolesSettings,
      section: 'roles',
    }),
    ...insertIf(canViewGeneralTabs, {
      key: SettingsTabKeys.PROFILES,
      label: t('GENERAL.PROFILES'),
      Component: ProfilesSettings,
      section: 'profiles',
    }),
    ...insertIf(canViewDepartments, {
      key: SettingsTabKeys.DEPARTMENTS,
      label: t('GENERAL.DEPARTMENTS'),
      Component: DepartmentsSettings,
      section: 'departments',
    }),
    ...insertIf(canViewGeneralTabs, {
      key: SettingsTabKeys.POSITIONS,
      label: t('GENERAL.POSITIONS'),
      Component: PositionsSettings,
      section: 'positions',
    }),
  ];

  const [currentTab, setCurrentTab] = useState(
    state?.selectedTab
      ? tabs.find(tab => tab.key === state.selectedTab)
      : tabs[0],
  );

  useEffect(() => {
    if (
      state?.selectedTab === SettingsTabKeys.DEPARTMENTS &&
      !canViewDepartments
    ) {
      setCurrentTab(tabs[0]);
    }
  }, [state?.selectedTab, canViewDepartments]);

  useEffect(() => {
    if (!currentTab && tabs.length > 0) {
      setCurrentTab(tabs[0]);
    }
  }, [tabs.length, currentTab]);

  useEffect(() => window.history.replaceState({}, ''), []);

  const Component = currentTab?.Component;

  if (!Component) {
    return null;
  }

  const SaveButton = (props: ButtonProps) => (
    <Button
      variant="contained"
      onMouseDown={handleSave}
      loading={isSubmitting}
      disabled={!isDirty}
      {...props}
    >
      {t('GENERAL:SAVE')}
    </Button>
  );

  const handleChangeTab = async (
    event: React.SyntheticEvent,
    value: string,
  ) => {
    const formIsValid = await trigger();
    if (formIsValid) {
      const newTab = tabs.find(tab => tab.key === value);
      if (newTab) {
        setCurrentTab(newTab);
      }
    } else {
      handleSave();
    }
  };

  return (
    <FormProvider {...form}>
      <Helmet>
        <title>{formatTitle(t('GENERAL.SETTINGS'))}</title>
      </Helmet>
      <Box
        sx={{
          backgroundColor: 'background.default',
          minHeight: '100%',
          py: 3,
        }}
      >
        <Container maxWidth="xl">
          <Typography
            color="textPrimary"
            variant="h5"
          >
            {t('GENERAL.SETTINGS')}
          </Typography>
          <Tabs
            value={currentTab?.key}
            onChange={handleChangeTab}
            sx={{ mt: 3 }}
            variant="scrollable"
          >
            {tabs.map(tab => (
              <Tab
                key={tab.key}
                value={tab.key}
                label={tab.label}
              />
            ))}
          </Tabs>
          <Divider sx={{ mb: 2 }} />
          <Component SaveButton={SaveButton} />
        </Container>
      </Box>
    </FormProvider>
  );
};

const Settings = () => {
  const isCerberusEnabled = useFeatureFlag(FeatureFlags.CERBERUS_ENABLED);
  const { permissions, instance } = useAuth();
  const showGeneralError = useGeneralError();

  const { canViewDepartments, isLoading: isDepartmentsLoading } =
    useDepartmentsPermissions();

  const canManageInstance = hasAnyPermissions(permissions, [
    UserPermissions.MANAGE_INSTANCE,
  ]);

  const canViewGeneralTabs = !isCerberusEnabled || canManageInstance;

  const instanceQuery = useQuery(
    appsKeys.instance(instance?.id ?? ''),
    () => instancesService.getInstance({ id: instance!.id }),
    {
      enabled: !!instance?.id,
      onError: (err: any) => showGeneralError(err),
    },
  );

  if (instanceQuery.isLoading || isDepartmentsLoading) {
    return <CircularProgress centered />;
  }

  const fullInstance = instanceQuery.data?.data;
  if (!fullInstance) {
    return null;
  }

  return (
    <SettingsForm
      instance={fullInstance}
      canViewGeneralTabs={canViewGeneralTabs}
      canViewDepartments={canViewDepartments}
      isCerberusEnabled={isCerberusEnabled}
      refetchInstance={instanceQuery.refetch}
    />
  );
};

export default Settings;
