import { useCallback, useState } from 'react';
import ContentLoader from 'react-content-loader';
import { nl2br } from '@ablogcms/react-utils';
import { parseQuery } from '@ablogcms/utils';
import { fetchClient, isFetchError } from '@ablogcms/fetch-client';
import useEffectOnce from '@ablogcms/react-hooks/use-effect-once';
import useClipboard from '@ablogcms/react-hooks/use-clipboard';
import SyntaxHighlight from '@ablogcms/components/syntax-highlight';
import Modal, { ModalBody, ModalHeader } from '@ablogcms/components/modal';
import { Table, TableBody, TableRow, TableHead, TableCell } from '@ablogcms/components/table';
import Alert from '@ablogcms/components/alert';
import { Tooltip } from '@ablogcms/components/tooltip';
import { VStack } from '@ablogcms/components/stack';

interface Log {
  id: number;
  success: boolean;
  datetime: string;
  level: string;
  levelName: string;
  suid: number;
  sessionUserName: string;
  sessionUserMail: string;
  bid: number;
  blogName: string;
  message: string;
  url: string;
  isManagedDomain: boolean;
  ua: string;
  referer: string;
  ipAddress: string;
  method: string;
  httpStatus: string;
  responseTime: number;
  eid: null | number;
  cid: null | number;
  uid: null | number;
  rid: null | number;
  ruleName: string;
  extra: string;
  context: string;
  switchUserName: string;
  switchUserMail: string;
  reqHeader: string;
  reqBody: string;
  acmsPost: string;
}

function TablePlaceholder() {
  return (
    <ContentLoader viewBox="0 0 400 190" speed={0.9}>
      <rect x="0" y="0" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="0" rx="1" ry="1" width="345" height="8" />
      <rect x="0" y="16" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="16" rx="1" ry="1" width="270" height="8" />
      <rect x="0" y="32" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="32" rx="1" ry="1" width="230" height="8" />
      <rect x="0" y="48" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="48" rx="1" ry="1" width="345" height="8" />
      <rect x="0" y="64" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="64" rx="1" ry="1" width="140" height="8" />
      <rect x="0" y="80" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="80" rx="1" ry="1" width="200" height="8" />
      <rect x="0" y="96" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="96" rx="1" ry="1" width="345" height="8" />
      <rect x="0" y="112" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="112" rx="1" ry="1" width="270" height="8" />
      <rect x="0" y="128" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="128" rx="1" ry="1" width="230" height="8" />
      <rect x="0" y="144" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="144" rx="1" ry="1" width="345" height="8" />
      <rect x="0" y="160" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="160" rx="1" ry="1" width="140" height="8" />
      <rect x="0" y="176" rx="1" ry="1" width="50" height="8" />
      <rect x="55" y="176" rx="1" ry="1" width="200" height="8" />
    </ContentLoader>
  );
}

interface AuditLogDetailModalProps {
  buttons: NodeListOf<HTMLButtonElement>;
}

function AuditLogDetailModal({ buttons }: AuditLogDetailModalProps) {
  const [isOpen, setIsOpen] = useState<boolean>(false);
  const [isReady, setIsReady] = useState<boolean>(false);
  const [logInfo, setLogInfo] = useState<Log>({} as Log);
  const [errorMessage, setErrorMessage] = useState<string>('');
  const { copy, copied, reset } = useClipboard();

  const fetchLogInfo = async (id: string) => {
    setLogInfo({} as Log);
    setIsReady(false);
    setIsOpen(true);

    const params = new URLSearchParams();
    params.append('ACMS_POST_Logger_Info', 'post');
    params.append('id', id);
    params.append('formToken', window.csrfToken);

    try {
      const response = await fetchClient.post<Log>(window.location.href, params);
      const json = response.data;
      if (response.status === 200) {
        if (json && json.success === true) {
          setLogInfo(json);
          setIsReady(true);

          if ('history' in window) {
            history.replaceState(null, '', `#id=${json.id}`);
          }
        }
      } else {
        setErrorMessage('ログの取得に失敗しました');
      }
    } catch (e) {
      if (isFetchError(e) && e.status === 400) {
        setErrorMessage(`ログの取得に失敗しました。${e.message}`);
      }
    }
  };

  useEffectOnce(() => {
    const handleClick = (e: MouseEvent) => {
      e.preventDefault();
      if (!(e.currentTarget instanceof HTMLButtonElement)) {
        return;
      }
      const id = e.currentTarget.getAttribute('data-id');
      if (id) {
        fetchLogInfo(id);
      }
    };

    [].forEach.call(buttons, (button: HTMLButtonElement) => {
      button.addEventListener('click', handleClick);
    });

    if (window.location.hash) {
      const { id } = parseQuery(window.location.hash.slice(1));
      if (id) {
        fetchLogInfo(id);
      }
    }

    return () => {
      [].forEach.call(buttons, (button: HTMLButtonElement) => {
        button.removeEventListener('click', handleClick);
      });
    };
  });

  const handleCopy = useCallback(() => {
    copy(JSON.stringify(logInfo, null, 2));
    setTimeout(() => {
      reset();
    }, 2000);
  }, [copy, logInfo, reset]);

  const ModalContent = (
    <VStack align="stretch" spacing="1rem">
      <div
        style={{
          display: 'flex',
          justifyContent: 'flex-end',
        }}
      >
        <button
          type="button"
          className="acms-admin-btn-admin"
          onClick={handleCopy}
          data-tooltip-id="log-detail-copied-tooltip"
          data-tooltip-content={ACMS.i18n('logger.modal.clipboardMessage')}
          data-data-tooltip-place="left"
          data-tooltip-variant="dark"
        >
          {ACMS.i18n('logger.modal.clipboardButton')}
        </button>
        <Tooltip id="log-detail-copied-tooltip" isOpen={copied} openEvents={{ mouseover: false, focus: false }} />
      </div>
      {errorMessage && (
        <div>
          <Alert variant="danger">{errorMessage}</Alert>
        </div>
      )}
      <div className="acms-admin-log-table" style={{ overflowX: 'scroll' }}>
        {!isReady && <TablePlaceholder />}
        {isReady && (
          <Table striped>
            <TableBody>
              <TableRow>
                <TableHead nowrap>{ACMS.i18n('logger.modal.datetime')}</TableHead>
                <TableCell>{logInfo.datetime}</TableCell>
              </TableRow>
              <TableRow>
                <TableHead nowrap>{ACMS.i18n('logger.modal.level')}</TableHead>
                <TableCell>{logInfo.levelName}</TableCell>
              </TableRow>
              <TableRow>
                <TableHead nowrap>{ACMS.i18n('logger.modal.message')}</TableHead>
                <TableCell>{nl2br(logInfo.message)}</TableCell>
              </TableRow>
              <TableRow>
                <TableHead nowrap>{ACMS.i18n('logger.modal.blog')}</TableHead>
                <TableCell>
                  {logInfo.blogName && (
                    <>
                      {logInfo.blogName}
                      &nbsp; &#40;id=
                      {logInfo.bid}
                      &#41;
                    </>
                  )}
                </TableCell>
              </TableRow>
              <TableRow>
                <TableHead nowrap>{ACMS.i18n('logger.modal.loginUserName')}</TableHead>
                <TableCell>
                  {logInfo.sessionUserName && (
                    <>
                      {logInfo.sessionUserName}
                      &nbsp; &#40;
                      {logInfo.sessionUserMail}
                      &#41;
                    </>
                  )}
                </TableCell>
              </TableRow>
              {logInfo.switchUserName && (
                <TableRow>
                  <TableHead nowrap>{ACMS.i18n('logger.modal.switchUserName')}</TableHead>
                  <TableCell>
                    {logInfo.switchUserName}
                    &nbsp; &#40;
                    {logInfo.switchUserMail}
                    &#41;
                  </TableCell>
                </TableRow>
              )}
              <TableRow>
                <TableHead nowrap>URL</TableHead>
                <TableCell>
                  {logInfo.isManagedDomain ? (
                    <a href={logInfo.url} target="_blank" rel="noreferrer">
                      {logInfo.url}
                    </a>
                  ) : (
                    logInfo.url
                  )}
                </TableCell>
              </TableRow>
              <TableRow>
                <TableHead nowrap>UA</TableHead>
                <TableCell>{logInfo.ua}</TableCell>
              </TableRow>
              <TableRow>
                <TableHead nowrap>{ACMS.i18n('logger.modal.ipAddress')}</TableHead>
                <TableCell>{logInfo.ipAddress}</TableCell>
              </TableRow>
              <TableRow>
                <TableHead nowrap>{ACMS.i18n('logger.modal.referer')}</TableHead>
                <TableCell>{logInfo.referer}</TableCell>
              </TableRow>
              <TableRow>
                <TableHead nowrap>{ACMS.i18n('logger.modal.method')}</TableHead>
                <TableCell>{logInfo.method}</TableCell>
              </TableRow>
              <TableRow>
                <TableHead nowrap>{ACMS.i18n('logger.modal.httpStatus')}</TableHead>
                <TableCell>{logInfo.httpStatus}</TableCell>
              </TableRow>
              <TableRow>
                <TableHead nowrap>{ACMS.i18n('logger.modal.responseTime')}</TableHead>
                <TableCell>
                  {logInfo.responseTime}
                  &nbsp; ms
                </TableCell>
              </TableRow>
              <TableRow>
                <TableHead nowrap>{ACMS.i18n('logger.modal.ruleName')}</TableHead>
                <TableCell>
                  {logInfo.rid && (
                    <>
                      {logInfo.ruleName}
                      &nbsp; &#40;id=
                      {logInfo.rid}
                      &#41;
                    </>
                  )}
                </TableCell>
              </TableRow>
              {logInfo.acmsPost && (
                <TableRow>
                  <TableHead nowrap>{ACMS.i18n('logger.modal.postModule')}</TableHead>
                  <TableCell>{logInfo.acmsPost}</TableCell>
                </TableRow>
              )}
              {logInfo.reqHeader && (
                <TableRow>
                  <TableHead nowrap>{ACMS.i18n('logger.modal.httpRequestHeader')}</TableHead>
                  <TableCell>
                    <SyntaxHighlight language="json">{JSON.stringify(logInfo.reqHeader, null, 2)}</SyntaxHighlight>
                  </TableCell>
                </TableRow>
              )}
              {logInfo.reqBody && (
                <TableRow>
                  <TableHead nowrap>{ACMS.i18n('logger.modal.httpRequestBody')}</TableHead>
                  <TableCell>
                    <SyntaxHighlight language="json">{JSON.stringify(logInfo.reqBody, null, 2)}</SyntaxHighlight>
                  </TableCell>
                </TableRow>
              )}
              {logInfo.context && (
                <TableRow>
                  <TableHead nowrap>{ACMS.i18n('logger.modal.extraInfo')}</TableHead>
                  <TableCell>
                    <SyntaxHighlight language="json">{JSON.stringify(logInfo.context, null, 2)}</SyntaxHighlight>
                  </TableCell>
                </TableRow>
              )}
            </TableBody>
          </Table>
        )}
      </div>
    </VStack>
  );

  return (
    <Modal
      isOpen={isOpen}
      onClose={() => {
        setIsOpen(false);
        if ('history' in window) {
          window.history.replaceState(null, '', location.pathname + location.search);
        }
      }}
      className="acms-admin-logger-modal"
      size="large"
      aria-labelledby="acms-admin-logger-modal-title"
    >
      <ModalHeader>{ACMS.i18n('logger.modal.title')}</ModalHeader>
      <ModalBody>{ModalContent}</ModalBody>
    </Modal>
  );
}

export default AuditLogDetailModal;
