import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { Navigate, useLocation } from 'react-router-dom';
import LoginView from '../login/LoginView';
import useApiError from '../hooks/useApiError';
import {
  isWebAuthnAvailable,
  preparePublicKeyRequestOptions,
  preparePublicKeyCreationOptions,
  serializeAuthenticationCredential,
  serializeRegistrationCredential,
} from '../utils/webauthn';
import { api, useAppDispatch, useAppState } from './appState';

type StatusVariant = 'info' | 'success' | 'error';

interface StatusMessage {
  message: string;
  variant: StatusVariant;
}

interface TenantOption {
  id?: string | number | null;
  name?: string | null;
}

interface TenantSelectionState {
  selectionToken?: string | null;
  tenants?: TenantOption[];
}


const LoginRoute: React.FC = () => {
  const appState = useAppState();
  const { status: appStatus } = appState;
  const tenantSelection = (appState.tenantSelection ?? null) as TenantSelectionState | null;
  const appDispatch = useAppDispatch();
  const location = useLocation();
  const [status, setStatus] = useState<StatusMessage | null>(null);
  const [selectingTenantId, setSelectingTenantId] = useState(null);
  const passkeySupported = isWebAuthnAvailable();
  const [passkeyLoading, setPasskeyLoading] = useState(false);
  const signupSupported = passkeySupported;
  const [signupLoading, setSignupLoading] = useState(false);
  const [magicLoginPending, setMagicLoginPending] = useState(false);
  const magicLoginParams = useMemo(() => {
    const extract = (searchString) => {
      const params = new URLSearchParams(searchString || '');
      const token = (params.get('magic_token') || '').trim();
      const usernameHint = (params.get('username') || '').trim();
      const preferredTenantId = (params.get('preferred_tenant_id') || '').trim();
      return {
        token: token || null,
        username: usernameHint || null,
        preferredTenantId: preferredTenantId || null,
      };
    };

    let combined = extract(location.search);

    const hash = window.location.hash || '';
    const queryIndex = hash.indexOf('?');
    if (queryIndex !== -1) {
      const hashQuery = hash.slice(queryIndex + 1);
      const hashParams = extract(`?${hashQuery}`);
      combined = {
        token: combined.token || hashParams.token,
        username: combined.username || hashParams.username,
        preferredTenantId: combined.preferredTenantId || hashParams.preferredTenantId,
      };
    }
    if (!combined.token) {
      const searchParams = extract(window.location.search);
      combined = {
        token: combined.token || searchParams.token,
        username: combined.username || searchParams.username,
        preferredTenantId: combined.preferredTenantId || searchParams.preferredTenantId,
      };
    }

    return combined;
  }, [location.search]);
  const preferredTenantRef = useRef(null);
  const attemptedMagicTokenRef = useRef(null);
  const magicLoginPendingRef = useRef(false);
  const appStatusRef = useRef(appStatus);

  const {
    token: magicToken,
    username: magicUsername,
    preferredTenantId: magicPreferredTenantId,
  } = magicLoginParams;

  useEffect(() => {
    appStatusRef.current = appStatus;
  }, [appStatus]);

  const setStatusMessage = useCallback((message, variant = 'info') => {
    setStatus(message ? { message, variant } : null);
  }, []);

  const handleLoginApiReport = useCallback(
    ({ message, variant }) => setStatusMessage(message, variant),
    [setStatusMessage],
  );

  const reportLoginError = useApiError({
    onReport: handleLoginApiReport,
  });

  const notifyLoginError = useCallback(
    (error, fallbackMessage, variant = 'error') =>
      reportLoginError(error, { message: fallbackMessage, variant }),
    [reportLoginError],
  );

  const clearMagicParamsFromUrl = useCallback(() => {
    const removableKeys = ['magic_token', 'username', 'preferred_tenant_id'];
    const currentSearch = new URLSearchParams(window.location.search);
    let searchChanged = false;
    removableKeys.forEach((key) => {
      if (currentSearch.has(key)) {
        currentSearch.delete(key);
        searchChanged = true;
      }
    });

    const hash = window.location.hash || '';
    let nextHash = hash;
    const hashQuestionIndex = hash.indexOf('?');
    if (hashQuestionIndex !== -1) {
      const hashPath = hash.slice(0, hashQuestionIndex);
      const hashQuery = hash.slice(hashQuestionIndex + 1);
      const hashParams = new URLSearchParams(hashQuery);
      let hashChanged = false;
      removableKeys.forEach((key) => {
        if (hashParams.has(key)) {
          hashParams.delete(key);
          hashChanged = true;
        }
      });
      if (hashChanged) {
        const nextQuery = hashParams.toString();
        nextHash = nextQuery ? `${hashPath}?${nextQuery}` : hashPath;
      }
    }

    if (!searchChanged && nextHash === hash) {
      return;
    }

    const nextSearch = currentSearch.toString();
    const nextUrl = `${window.location.pathname}${nextSearch ? `?${nextSearch}` : ''}${nextHash}`;
    window.history.replaceState(window.history.state, document.title, nextUrl);
  }, []);

  const handleTenantSelect = useCallback(
    async (tenant) => {
      if (!tenantSelection?.selectionToken || !tenant?.id) {
        return;
      }

      try {
        setSelectingTenantId(tenant.id);
        const { data } = await api.post(
          '/auth/select-tenant',
          { tenant_id: tenant.id },
          {
            headers: {
              Authorization: `Bearer ${tenantSelection.selectionToken}`,
            },
          },
        );

        if (!data?.access_token) {
          throw new Error('Invalid tenant selection response.');
        }

        appDispatch({
          type: 'LOGIN_SUCCESS',
          token: data.access_token,
          tenant: data.tenant || null,
        });
        setStatusMessage('Login successful.', 'success');
      } catch (error) {
        const message = error?.response?.data?.error || 'Failed to finalize login.';
        notifyLoginError(error, message, 'error');
      } finally {
        setSelectingTenantId(null);
      }
    },
    [appDispatch, notifyLoginError, setStatusMessage, tenantSelection],
  );

  const handleCancelSelection = useCallback(() => {
    appDispatch({ type: 'CLEAR_TENANT_SELECTION' });
    setStatusMessage(null);
  }, [appDispatch, setStatusMessage]);

  const handlePasskeyLogin = useCallback(
    async (rawUsername) => {
      const username = rawUsername?.trim?.() || '';
      if (!username) {
        setStatusMessage('Enter your username before using a passkey.', 'error');
        return;
      }
      if (!passkeySupported) {
        setStatusMessage('Passkeys are not supported in this browser.', 'error');
        return;
      }

      setPasskeyLoading(true);
      appDispatch({ type: 'LOGIN_REQUEST' });
      try {
        const { data: startData } = await api.post('/auth/passkeys/login/start', { username });
        const challengeId = startData.challengeId;
        const publicKeyOptions = startData.publicKey;

        if (!challengeId || !publicKeyOptions) {
          throw new Error('Invalid passkey challenge response.');
        }

        const publicKey = preparePublicKeyRequestOptions({ publicKey: publicKeyOptions });
        setStatusMessage('Confirm the passkey prompt to continue.', 'info');
        const assertion = await navigator.credentials.get({ publicKey }) as PublicKeyCredential | null;

        if (!assertion) {
          setStatusMessage('Passkey login cancelled.', 'info');
          return;
        }

        if (!(assertion instanceof PublicKeyCredential)) {
          setStatusMessage('Unexpected credential response.', 'error');
          return;
        }

        const serialized = serializeAuthenticationCredential(assertion);
        const finishPayload = {
          challengeId,
          credential: serialized,
        };

        const { data: finishData } = await api.post('/auth/passkeys/login/finish', finishPayload);

        if (finishData?.access_token && Array.isArray(finishData?.tenants)) {
          appDispatch({
            type: 'TENANT_SELECTION_REQUIRED',
            selectionToken: finishData.access_token,
            tenants: finishData.tenants,
          });
          setStatusMessage('Select a tenant to continue.', 'info');
          return;
        }

        if (!finishData?.access_token) {
          throw new Error('Invalid login response.');
        }

        appDispatch({
          type: 'LOGIN_SUCCESS',
          token: finishData.access_token,
          tenant: finishData.tenant || null,
        });
        setStatusMessage('Login successful.', 'success');
      } catch (error) {
        if (error?.name === 'NotAllowedError') {
          setStatusMessage('Passkey login cancelled.', 'info');
        } else if (error?.response?.status === 404) {
          setStatusMessage('No passkey registered for this username. Create an account first.', 'error');
          appDispatch({ type: 'LOGIN_FAILURE', error: 'passkey not registered' });
        } else if (error?.response?.status === 400 && error?.response?.data?.error) {
          setStatusMessage(error.response.data.error, 'error');
          appDispatch({ type: 'LOGIN_FAILURE', error: error.response.data.error });
        } else {
          const message = error?.response?.data?.error || error.message || 'Passkey login failed.';
          notifyLoginError(error, message);
          appDispatch({ type: 'LOGIN_FAILURE', error: message });
        }
      } finally {
        setPasskeyLoading(false);
      }
    },
    [appDispatch, notifyLoginError, passkeySupported, setStatusMessage],
  );

  useEffect(() => {
    if (!magicToken) {
      return;
    }
    if (magicLoginPendingRef.current) {
      return;
    }
    if (attemptedMagicTokenRef.current === magicToken) {
      return;
    }
    if (appStatusRef.current === 'authenticated') {
      clearMagicParamsFromUrl();
      return;
    }

    let cancelled = false;
    attemptedMagicTokenRef.current = magicToken;

    const attemptMagicLogin = async () => {
      magicLoginPendingRef.current = true;
      setMagicLoginPending(true);
      preferredTenantRef.current = magicPreferredTenantId;
      appDispatch({ type: 'LOGIN_REQUEST' });
      setStatusMessage('Signing you in…', 'info');

      try {
        const payload: {
          magic_token: string;
          username?: string;
          preferred_tenant_id?: string | number;
        } = {
          magic_token: magicToken,
        };
        if (magicUsername) {
          payload.username = magicUsername;
        }
        if (magicPreferredTenantId) {
          payload.preferred_tenant_id = magicPreferredTenantId;
        }

        const { data } = await api.post('/auth/login', payload);
        if (cancelled) {
          return;
        }

        if (data?.access_token && Array.isArray(data?.tenants)) {
          appDispatch({
            type: 'TENANT_SELECTION_REQUIRED',
            selectionToken: data.access_token,
            tenants: data.tenants,
          });
          setStatusMessage('Select a tenant to continue.', 'info');
          return;
        }

        if (!data?.access_token) {
          throw new Error('Invalid login response.');
        }

        appDispatch({
          type: 'LOGIN_SUCCESS',
          token: data.access_token,
          tenant: data.tenant || null,
        });
        setStatusMessage('Login successful.', 'success');
        preferredTenantRef.current = null;
      } catch (error) {
        if (cancelled) {
          return;
        }
        const message = error?.response?.data?.error || 'Magic link login failed.';
        notifyLoginError(error, message);
        appDispatch({ type: 'LOGIN_FAILURE', error: message });
        preferredTenantRef.current = null;
      } finally {
        if (!cancelled) {
          setMagicLoginPending(false);
          magicLoginPendingRef.current = false;
          clearMagicParamsFromUrl();
        }
      }
    };

    attemptMagicLogin();

    return () => {
      cancelled = true;
      magicLoginPendingRef.current = false;
      setMagicLoginPending(false);
    };
  }, [
    appDispatch,
    clearMagicParamsFromUrl,
    magicToken,
    magicUsername,
    magicPreferredTenantId,
    notifyLoginError,
    setStatusMessage,
  ]);

  useEffect(() => {
    const preferredTenantId = preferredTenantRef.current;
    if (!preferredTenantId) {
      return;
    }
    if (!tenantSelection?.tenants?.length) {
      return;
    }
    if (selectingTenantId) {
      return;
    }
    const match = tenantSelection.tenants.find((tenant) => tenant.id === preferredTenantId);
    if (!match) {
      preferredTenantRef.current = null;
      return;
    }
    handleTenantSelect(match);
    preferredTenantRef.current = null;
  }, [handleTenantSelect, selectingTenantId, tenantSelection]);

  const handleSignup = useCallback(
    async (rawUsername) => {
      const username = rawUsername?.trim?.() || '';
      if (!username) {
        setStatusMessage('Choose a username to create your account.', 'error');
        return;
      }
      if (!passkeySupported) {
        setStatusMessage('Passkeys are not supported in this browser.', 'error');
        return;
      }

      setSignupLoading(true);
      try {
        const { data: startData } = await api.post('/auth/signup/start', { username });
        const signupToken = startData.signup_token;
        const challengePayload = startData.challenge;
        const challengeId = challengePayload?.challengeId;
        const publicKeyOptions = challengePayload?.publicKey;

        if (!signupToken || !challengeId || !publicKeyOptions) {
          throw new Error('Invalid signup challenge response.');
        }

        const publicKey = preparePublicKeyCreationOptions({ publicKey: publicKeyOptions });
        setStatusMessage('Confirm the passkey prompt to finish creating your account.', 'info');
        const credential = await navigator.credentials.create({ publicKey }) as PublicKeyCredential | null;

        if (!credential) {
          setStatusMessage('Signup cancelled.', 'info');
          return;
        }

        if (!(credential instanceof PublicKeyCredential)) {
          setStatusMessage('Unexpected credential response.', 'error');
          return;
        }

        const serialized = serializeRegistrationCredential(credential);
        const finishPayload = {
          signup_token: signupToken,
          credential: serialized,
        };

        const { data: finishData } = await api.post('/auth/signup/finish', finishPayload);

        if (finishData?.access_token && Array.isArray(finishData?.tenants)) {
          appDispatch({
            type: 'TENANT_SELECTION_REQUIRED',
            selectionToken: finishData.access_token,
            tenants: finishData.tenants,
          });
          setStatusMessage('Select a tenant to continue.', 'info');
          return;
        }

        if (!finishData?.access_token) {
          throw new Error('Invalid signup response.');
        }

        appDispatch({
          type: 'LOGIN_SUCCESS',
          token: finishData.access_token,
          tenant: finishData.tenant || null,
        });
        setStatusMessage('Account created. Welcome!', 'success');
      } catch (error) {
        if (error?.name === 'NotAllowedError') {
          setStatusMessage('Signup cancelled.', 'info');
        } else if (error?.response?.status === 409) {
          setStatusMessage('This passkey is already registered. Try signing in instead.', 'error');
        } else if (error?.response?.data?.error) {
          setStatusMessage(error.response.data.error, 'error');
        } else {
          const message = error?.message || 'Failed to create account.';
          setStatusMessage(message, 'error');
        }
      } finally {
        setSignupLoading(false);
      }
    },
    [appDispatch, passkeySupported, setStatusMessage],
  );

  const redirectTarget = useMemo(() => {
    const target = String(location.state?.from ?? '');
    if (target.startsWith('/')) {
      return target;
    }
    return '/documents';
  }, [location.state]);

  if (!['logged-out', 'authenticating', 'selecting-tenant'].includes(appStatus)) {
    return <Navigate to={redirectTarget} replace />;
  }

  return (
    <div className="app-shell">
      <LoginView
        status={status}
        tenantSelection={tenantSelection}
        onSelectTenant={handleTenantSelect}
        onCancelSelection={handleCancelSelection}
        selectingTenantId={selectingTenantId}
        onPasskeyLogin={handlePasskeyLogin}
      passkeySupported={passkeySupported}
      passkeyLoading={passkeyLoading}
      onSignup={handleSignup}
      signupSupported={signupSupported}
      signupLoading={signupLoading}
      magicLoginPending={magicLoginPending}
      initialUsername={magicLoginParams.username || ''}
    />
    </div>
  );
};

export default LoginRoute;
