import { useEffect, useState } from 'react';

import pluralize from 'pluralize';

import WarningAmberIcon from '@mui/icons-material/WarningAmber';
import { Box, Link, type Theme, Tooltip } from '@mui/material';

import { blue } from '../../../../themes/common';
import { HostSelectionDialog } from './HostSelectionDialog';
import { entriesCount, resolveEntries, resolvedHostkeys } from '../utils/resolveEntries';
import type { EntriesFilter, EntriesMap } from '../types';

interface HostkeyIncludeExcludeProps {
  disabled?: boolean;
  entriesFilter: EntriesFilter;
  onChange: (next: EntriesFilter) => void;
  // Whether attribute/class rules are also applied, which changes the "include
  // additionally" wording.
  otherFiltersApplied: boolean;
}

const previewEntries = (entries: EntriesMap): string => {
  const list = Object.values(entries)
    .flatMap(values => values ?? [])
    .join(', ');
  return list.length > 75 ? `${list.substring(0, 75)}...` : list;
};

const NO_KEYS: string[] = [];

export const HostkeyIncludeExclude = ({ entriesFilter, otherFiltersApplied, disabled = false, onChange }: HostkeyIncludeExcludeProps) => {
  const [dialogSide, setDialogSide] = useState<'includes' | 'excludes' | null>(null);
  const [resolvedKeys, setResolvedKeys] = useState<{ excludes: string[]; includes: string[] }>({ includes: NO_KEYS, excludes: NO_KEYS });

  const { includes, excludes } = entriesFilter;

  // Resolve entries to real hosts for the "N hosts" counts. Guarded so a stale
  // response from a previous entries value can't overwrite a newer one.
  useEffect(() => {
    let active = true;
    Promise.all([resolveEntries(includes), resolveEntries(excludes)])
      .then(([resolvedIncludes, resolvedExcludes]) => {
        if (active) {
          setResolvedKeys({ includes: resolvedHostkeys(resolvedIncludes), excludes: resolvedHostkeys(resolvedExcludes) });
        }
      })
      .catch(() => {
        if (active) {
          setResolvedKeys({ includes: NO_KEYS, excludes: NO_KEYS });
        }
      });
    return () => {
      active = false;
    };
  }, [includes, excludes]);

  const hasIntersection = resolvedKeys.includes.some(key => resolvedKeys.excludes.includes(key));

  const handleSave = (entries: EntriesMap) => {
    if (dialogSide) onChange({ ...entriesFilter, [dialogSide]: entries });
    setDialogSide(null);
  };

  return (
    <Box sx={{ mt: 2 }}>
      <Section
        empty={otherFiltersApplied ? 'Include additional hosts' : 'Include hosts'}
        verb="included"
        noun="inclusion"
        entryCount={entriesCount(includes)}
        hostCount={resolvedKeys.includes.length}
        preview={previewEntries(includes)}
        warn={hasIntersection}
        disabled={disabled}
        onEdit={() => setDialogSide('includes')}
      />
      <Section
        empty="Exclude hosts"
        verb="excluded"
        noun="exclusion"
        entryCount={entriesCount(excludes)}
        hostCount={resolvedKeys.excludes.length}
        preview={previewEntries(excludes)}
        disabled={disabled}
        onEdit={() => setDialogSide('excludes')}
      />

      {/* Mounted per open so the dialog's draft state always starts from the saved entries. */}
      {dialogSide && (
        <HostSelectionDialog include={dialogSide === 'includes'} entries={entriesFilter[dialogSide]} onClose={() => setDialogSide(null)} onSave={handleSave} />
      )}
    </Box>
  );
};

interface SectionProps {
  disabled: boolean;
  // Label shown when there are no entries yet (e.g. "Include additional hosts").
  empty: string;
  entryCount: number;
  hostCount: number;
  // "inclusion" / "exclusion".
  noun: string;
  onEdit: () => void;
  preview: string;
  // "included" / "excluded".
  verb: string;
  warn?: boolean;
}

// A single blue link: "N host(s) {verb}, M {noun} entr(y/ies)" over a "(preview)"
// line, or the plain label when empty.
const Section = ({ empty, verb, noun, entryCount, hostCount, preview, warn = false, disabled, onEdit }: SectionProps) => (
  <Box sx={{ py: 0.5 }}>
    <Link
      component="button"
      type="button"
      variant="subtitle2"
      disabled={disabled}
      sx={(theme: Theme) => ({
        color: theme.palette.mode === 'dark' ? blue[300] : blue[600],
        lineHeight: '150%',
        textAlign: 'left',
        '&:disabled': { cursor: 'default', opacity: 0.5 }
      })}
      onClick={onEdit}
    >
      {entryCount === 0 ? (
        empty
      ) : (
        <>
          {pluralize('host', hostCount, true)} {verb}, {entryCount} {noun} {pluralize('entry', entryCount)}
          {warn && (
            <Tooltip title="Some hosts are both included and excluded. Exclusion will take precedence.">
              <WarningAmberIcon sx={{ color: 'error.main', fontSize: 16, ml: 1, verticalAlign: 'middle' }} />
            </Tooltip>
          )}
          <br />({preview})
        </>
      )}
    </Link>
  </Box>
);
