import React, { useCallback, useEffect, useLayoutEffect, useRef, useState } from 'react';
import { Box, Button, Input, Spinner, Text, color } from 'folds';
import {
  Outlet,
  generatePath,
  matchPath,
  useLocation,
  useNavigate,
  useParams,
} from 'react-router-dom';
import { useTranslation } from 'react-i18next';

import { AuthFooter } from './AuthFooter';
import * as css from './styles.css';
import {
  clientAllowedServer,
  clientDefaultServer,
  useClientConfig,
} from '../../hooks/useClientConfig';
import { AsyncStatus, useAsyncCallback } from '../../hooks/useAsyncCallback';
import { LOGIN_PATH, REGISTER_PATH, RESET_PASSWORD_PATH } from '../paths';
import { AutoDiscoveryAction, autoDiscovery } from '../../cs-api';
import { SpecVersionsLoader } from '../../components/SpecVersionsLoader';
import { SpecVersionsProvider } from '../../hooks/useSpecVersions';
import { AutoDiscoveryInfoProvider } from '../../hooks/useAutoDiscoveryInfo';
import { AuthFlowsLoader } from '../../components/AuthFlowsLoader';
import { AuthFlowsProvider } from '../../hooks/useAuthFlows';
import { AuthServerProvider } from '../../hooks/useAuthServer';
import { tryDecodeURIComponent } from '../../utils/dom';
import mascotPoster from '../../../../public/res/img/mascot.png';
import mascotWebm from '../../../../public/res/img/mascot.webm';

const currentAuthPath = (pathname: string): string => {
  if (matchPath(LOGIN_PATH, pathname)) return LOGIN_PATH;
  if (matchPath(RESET_PASSWORD_PATH, pathname)) return RESET_PASSWORD_PATH;
  if (matchPath(REGISTER_PATH, pathname)) return REGISTER_PATH;
  return LOGIN_PATH;
};

function AuthLayoutLoading({ message }: { message: string }) {
  return (
    <Box justifyContent="Center" alignItems="Center" gap="200">
      <Spinner size="100" variant="Secondary" />
      <Text align="Center" size="T300">
        {message}
      </Text>
    </Box>
  );
}

function AuthLayoutError({ message }: { message: string }) {
  return (
    <Box justifyContent="Center" alignItems="Center" gap="200">
      <Text align="Center" style={{ color: color.Critical.Main }} size="T300">
        {message}
      </Text>
    </Box>
  );
}

/* ── Server edit dialog ── */
function ServerEditDialog({
  currentServer,
  onConfirm,
  onCancel,
}: {
  currentServer: string;
  onConfirm: (server: string) => void;
  onCancel: () => void;
}) {
  const { t } = useTranslation();
  const [value, setValue] = useState(currentServer);
  const inputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    inputRef.current?.focus();
    inputRef.current?.select();
  }, []);

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    const trimmed = value.trim();
    if (trimmed) onConfirm(trimmed);
  };

  return (
    <div
      className={css.ServerDialog}
      role="dialog"
      aria-modal="true"
      onKeyDown={(e) => { if (e.key === 'Escape') onCancel(); }}
    >
      <div className={css.ServerDialogBackdrop} onClick={onCancel} aria-hidden="true" />
      <div className={css.ServerDialogCard}>
        <Text size="H3" style={{ color: '#e8e4df', fontWeight: 600 }}>
          {t('Auth.homeserver_dialog_title')}
        </Text>
        <Text size="T300" style={{ color: 'rgba(232, 228, 223, 0.65)' }}>
          {t('Auth.homeserver_dialog_desc')}
        </Text>
        <form onSubmit={handleSubmit}>
          <Box direction="Column" gap="400">
            <Input
              ref={inputRef}
              variant="Background"
              size="500"
              outlined
              value={value}
              onChange={(e: React.ChangeEvent<HTMLInputElement>) => setValue(e.target.value)}
              placeholder={t('Auth.homeserver_dialog_placeholder')}
            />
            <Box gap="300" justifyContent="End">
              <Button
                type="button"
                variant="Secondary"
                fill="Soft"
                size="400"
                onClick={onCancel}
              >
                <Text size="B400">{t('Auth.homeserver_dialog_cancel')}</Text>
              </Button>
              <Button type="submit" variant="Primary" size="400">
                <Text size="B400">{t('Auth.homeserver_dialog_confirm')}</Text>
              </Button>
            </Box>
          </Box>
        </form>
      </div>
    </div>
  );
}

/* ── Layout helpers (from element-web) ── */

function readPx(s: CSSStyleDeclaration, name: string, fallback = 0): number {
  const v = Number.parseFloat(s.getPropertyValue(name));
  return Number.isFinite(v) ? v : fallback;
}

function readNum(s: CSSStyleDeclaration, name: string, fallback: number): number {
  const v = Number.parseFloat(s.getPropertyValue(name));
  return Number.isFinite(v) ? v : fallback;
}

function calculateModalLayout(input: {
  pageHeight: number;
  mascotTopOffset: number;
  mascotHeight: number;
  modalHeight: number;
  footerHeight: number;
  anchorRatio: number;
  minTop: number;
  bottomGap: number;
}) {
  const desiredTop = input.mascotTopOffset + input.mascotHeight * input.anchorRatio;
  const maxTop = input.pageHeight - input.footerHeight - input.bottomGap - input.modalHeight;
  const top = Math.max(input.minTop, Math.min(desiredTop, maxTop));
  const maxHeight = Math.max(0, input.pageHeight - input.footerHeight - input.bottomGap - top);
  return {
    top: Math.round(top),
    maxHeight: Math.floor(maxHeight),
    constrained: input.modalHeight > maxHeight,
  };
}

const rootVars: React.CSSProperties = {
  '--vojo-mascot-size': 'clamp(28rem, 57dvh, 48rem)',
  '--vojo-mascot-top': 'clamp(1.5rem, 4dvh, 3rem)',
  '--vojo-stack-pad': '1.5rem',
  '--vojo-anchor-ratio': '0.71',
  '--vojo-modal-min-top': 'clamp(12rem, 26dvh, 18rem)',
  '--vojo-modal-gap': 'clamp(0.75rem, 2dvh, 1.5rem)',
  '--vojo-footer-space': 'clamp(4.5rem, 9dvh, 6.5rem)',
} as React.CSSProperties;

export function AuthLayout() {
  const { t } = useTranslation();
  const navigate = useNavigate();
  const location = useLocation();
  const { server: urlEncodedServer } = useParams();

  const clientConfig = useClientConfig();
  const defaultServer = clientDefaultServer(clientConfig);
  let server: string = urlEncodedServer ? tryDecodeURIComponent(urlEncodedServer) : defaultServer;

  if (!clientAllowedServer(clientConfig, server)) {
    server = defaultServer;
  }

  const [showServerDialog, setShowServerDialog] = useState(false);

  const [discoveryState, discoverServer] = useAsyncCallback(
    useCallback(async (serverName: string) => {
      const response = await autoDiscovery(fetch, serverName);
      return { serverName, response };
    }, [])
  );

  useEffect(() => {
    if (server) discoverServer(server);
  }, [discoverServer, server]);

  useEffect(() => {
    if (!urlEncodedServer || tryDecodeURIComponent(urlEncodedServer) !== server) {
      navigate(
        generatePath(currentAuthPath(location.pathname), {
          server: encodeURIComponent(server),
        }),
        { replace: true }
      );
    }
  }, [urlEncodedServer, navigate, location, server]);

  const selectServer = useCallback(
    (newServer: string) => {
      if (newServer === server) {
        if (discoveryState.status === AsyncStatus.Loading) return;
        discoverServer(server);
        return;
      }
      navigate(
        generatePath(currentAuthPath(location.pathname), {
          server: encodeURIComponent(newServer),
        })
      );
    },
    [navigate, location, discoveryState, server, discoverServer]
  );

  const [autoDiscoveryError, autoDiscoveryInfo] =
    discoveryState.status === AsyncStatus.Success ? discoveryState.data.response : [];

  /* ── Page title based on route ── */
  const getPageTitle = () => {
    if (matchPath(LOGIN_PATH, location.pathname)) return t('Auth.title_login');
    if (matchPath(REGISTER_PATH, location.pathname)) return t('Auth.title_register');
    return t('Auth.title_reset_password');
  };
  const pageTitle = getPageTitle();

  /* ── Refs for JS-driven layout ── */
  const pageRef = useRef<HTMLDivElement>(null);
  const mascotRef = useRef<HTMLDivElement>(null);
  const modalRef = useRef<HTMLDivElement>(null);
  const footerRef = useRef<HTMLElement>(null);
  const cardContentRef = useRef<HTMLDivElement>(null);

  useLayoutEffect(() => {
    const page = pageRef.current;
    const mascot = mascotRef.current;
    const modal = modalRef.current;
    const footer = footerRef.current;
    if (!page || !mascot || !modal || !footer) return undefined;

    let frameId = 0;
    const update = (): void => {
      cancelAnimationFrame(frameId);
      frameId = requestAnimationFrame(() => {
        const s = getComputedStyle(page);
        const layout = calculateModalLayout({
          pageHeight: page.clientHeight,
          mascotTopOffset: readPx(s, '--vojo-mascot-top'),
          mascotHeight: mascot.offsetHeight,
          modalHeight: modal.offsetHeight,
          footerHeight: footer.offsetHeight || readPx(s, '--vojo-footer-space', footer.offsetHeight),
          anchorRatio: readNum(s, '--vojo-anchor-ratio', 0.58),
          minTop: readPx(s, '--vojo-modal-min-top'),
          bottomGap: readPx(s, '--vojo-modal-gap'),
        });
        page.style.setProperty('--vojo-modal-top', `${layout.top}px`);
        page.style.setProperty('--vojo-modal-max-h', `${layout.maxHeight}px`);
        if (cardContentRef.current) {
          cardContentRef.current.classList.toggle(css.AuthCardContentConstrained, layout.constrained);
        }
      });
    };

    update();
    const ro = new ResizeObserver(update);
    ro.observe(page);
    ro.observe(mascot);
    ro.observe(modal);
    ro.observe(footer);
    window.addEventListener('resize', update);

    return () => {
      cancelAnimationFrame(frameId);
      ro.disconnect();
      window.removeEventListener('resize', update);
    };
  }, []);

  return (
    <div className={css.AuthLayout} style={rootVars} ref={pageRef}>
      <div className={css.AuthStack}>
        <div className={css.AuthMascot} aria-hidden="true" ref={mascotRef}>
          <video
            className={css.AuthMascotVideo}
            autoPlay
            loop
            muted
            playsInline
            preload="auto"
            poster={mascotPoster}
          >
            <source src={mascotWebm} type="video/webm" />
          </video>
        </div>

        <div className={css.AuthModalZone}>
          <div className={css.AuthCard} ref={modalRef}>
            <div className={css.AuthCardContent} ref={cardContentRef}>
              {/* Title */}
              <Text size="H2" style={{ color: '#e8e4df', fontWeight: 700 }}>
                {pageTitle}
              </Text>

              {/* Server info row with separator */}
              <div className={css.ServerRow}>
                <span className={css.ServerLabel}>{t('Auth.homeserver')}</span>
                <span className={css.ServerName}>{server}</span>
                {clientConfig.allowCustomHomeservers && (
                  <button
                    type="button"
                    className={css.ServerEdit}
                    onClick={() => setShowServerDialog(true)}
                  >
                    {t('Auth.homeserver_edit')}
                  </button>
                )}
              </div>

              {/* Loading / error / form */}
              {discoveryState.status === AsyncStatus.Loading && (
                <AuthLayoutLoading message={t('Auth.loading_server')} />
              )}
              {discoveryState.status === AsyncStatus.Error && (
                <AuthLayoutError message={t('Auth.error_server_not_found')} />
              )}
              {autoDiscoveryError?.action === AutoDiscoveryAction.FAIL_PROMPT && (
                <AuthLayoutError
                  message={t('Auth.error_server_config_invalid', { host: autoDiscoveryError.host })}
                />
              )}
              {autoDiscoveryError?.action === AutoDiscoveryAction.FAIL_ERROR && (
                <AuthLayoutError message={t('Auth.error_server_base_url_invalid')} />
              )}
              {discoveryState.status === AsyncStatus.Success && autoDiscoveryInfo && (
                <AuthServerProvider value={discoveryState.data.serverName}>
                  <AutoDiscoveryInfoProvider value={autoDiscoveryInfo}>
                    <SpecVersionsLoader
                      baseUrl={autoDiscoveryInfo['m.homeserver'].base_url}
                      fallback={() => (
                        <AuthLayoutLoading
                          message={t('Auth.loading_connecting', { url: autoDiscoveryInfo['m.homeserver'].base_url })}
                        />
                      )}
                      error={() => (
                        <AuthLayoutError message={t('Auth.error_server_unavailable')} />
                      )}
                    >
                      {(specVersions) => (
                        <SpecVersionsProvider value={specVersions}>
                          <AuthFlowsLoader
                            fallback={() => (
                              <AuthLayoutLoading message={t('Auth.loading_auth_flows')} />
                            )}
                            error={() => (
                              <AuthLayoutError message={t('Auth.error_auth_flows')} />
                            )}
                          >
                            {(authFlows) => (
                              <AuthFlowsProvider value={authFlows}>
                                <Outlet />
                              </AuthFlowsProvider>
                            )}
                          </AuthFlowsLoader>
                        </SpecVersionsProvider>
                      )}
                    </SpecVersionsLoader>
                  </AutoDiscoveryInfoProvider>
                </AuthServerProvider>
              )}
            </div>
          </div>
        </div>
      </div>
      <AuthFooter footerRef={footerRef} />

      {/* Server edit dialog */}
      {showServerDialog && (
        <ServerEditDialog
          currentServer={server}
          onConfirm={(newServer) => {
            setShowServerDialog(false);
            selectServer(newServer);
          }}
          onCancel={() => setShowServerDialog(false)}
        />
      )}
    </div>
  );
}
