import { useSignal } from '@preact/signals';
import { startRegistration } from '@simplewebauthn/browser';

import { MultiFactorAuthMethodType } from '/lib/types.ts';
import {
  RequestBody as PasskeySetupBeginRequestBody,
  ResponseBody as PasskeySetupBeginResponseBody,
} from '/routes/api/auth/multi-factor/passkey/setup-begin.ts';
import {
  RequestBody as PasskeySetupCompleteRequestBody,
  ResponseBody as PasskeySetupCompleteResponseBody,
} from '/routes/api/auth/multi-factor/passkey/setup-complete.ts';
import {
  RequestBody as TOTPSetupRequestBody,
  ResponseBody as TOTPSetupResponseBody,
} from '/routes/api/auth/multi-factor/totp/setup.ts';
import {
  RequestBody as MultiFactorAuthEnableRequestBody,
  ResponseBody as MultiFactorAuthEnableResponseBody,
} from '/routes/api/auth/multi-factor/enable.ts';
import {
  RequestBody as MultiFactorAuthDisableRequestBody,
  ResponseBody as MultiFactorAuthDisableResponseBody,
} from '/routes/api/auth/multi-factor/disable.ts';

interface MultiFactorAuthMethod {
  type: MultiFactorAuthMethodType;
  id: string;
  name: string;
  enabled: boolean;
  backupCodesCount?: number;
}

interface MultiFactorAuthSettingsProps {
  methods: MultiFactorAuthMethod[];
}

interface TOTPSetupData {
  type: 'totp';
  secret: string;
  qrCodeUrl: string;
  backupCodes: string[];
  methodId: string;
}

interface PasskeySetupData {
  methodId: string;
  type: 'passkey';
}

const methodTypeLabels: Record<MultiFactorAuthMethodType, string> = {
  totp: 'Authenticator App',
  passkey: 'Passkey',
};

const methodTypeDescriptions: Record<MultiFactorAuthMethodType, string> = {
  totp: 'Use an authenticator app like Aegis Authenticator or Google Authenticator to generate codes',
  passkey: 'Use biometric authentication or security keys',
};

const availableMethodTypes = ['totp', 'passkey'] as MultiFactorAuthMethodType[];

export default function MultiFactorAuthSettings({ methods }: MultiFactorAuthSettingsProps) {
  const setupData = useSignal<TOTPSetupData | PasskeySetupData | null>(null);
  const isLoading = useSignal(false);
  const error = useSignal<string | null>(null);
  const success = useSignal<string | null>(null);
  const showDisableForm = useSignal<'all' | string | null>(null);
  const verificationToken = useSignal('');
  const disablePassword = useSignal('');

  const enabledMethods = methods.filter((method) => method.enabled);
  const hasMultiFactorAuthEnabled = enabledMethods.length > 0;

  const setupPasskey = async () => {
    const beginRequestBody: PasskeySetupBeginRequestBody = {};

    const beginResponse = await fetch('/api/auth/multi-factor/passkey/setup-begin', {
      method: 'POST',
      body: JSON.stringify(beginRequestBody),
    });

    if (!beginResponse.ok) {
      throw new Error(
        `Failed to begin passkey registration! ${beginResponse.statusText} ${await beginResponse.text()}`,
      );
    }

    const beginData = await beginResponse.json() as PasskeySetupBeginResponseBody;

    if (!beginData.success) {
      throw new Error(beginData.error || 'Failed to begin passkey registration');
    }

    const registrationResponse = await startRegistration({ optionsJSON: beginData.options! });

    const completeRequestBody: PasskeySetupCompleteRequestBody = {
      methodId: beginData.sessionData!.methodId,
      challenge: beginData.sessionData!.challenge,
      registrationResponse,
    };

    const completeResponse = await fetch('/api/auth/multi-factor/passkey/setup-complete', {
      method: 'POST',
      body: JSON.stringify(completeRequestBody),
    });

    if (!completeResponse.ok) {
      throw new Error(
        `Failed to complete passkey registration! ${completeResponse.statusText} ${await completeResponse.text()}`,
      );
    }

    const completeData = await completeResponse.json() as PasskeySetupCompleteResponseBody;

    if (!completeData.success) {
      throw new Error(completeData.error || 'Failed to complete passkey registration');
    }

    setupData.value = {
      methodId: beginData.sessionData!.methodId,
      type: 'passkey',
    };
  };

  const setupTOTP = async () => {
    const requestBody: TOTPSetupRequestBody = {};

    const response = await fetch('/api/auth/multi-factor/totp/setup', {
      method: 'POST',
      body: JSON.stringify(requestBody),
    });

    const data = await response.json() as TOTPSetupResponseBody;

    if (!data.success || !data.data) {
      throw new Error(data.error || 'Failed to setup TOTP multi-factor authentication');
    }

    setupData.value = {
      type: 'totp',
      secret: data.data.secret!,
      qrCodeUrl: data.data.qrCodeUrl!,
      backupCodes: data.data.backupCodes!,
      methodId: data.data.methodId!,
    };
  };

  const setupMultiFactorAuth = async (type: MultiFactorAuthMethodType) => {
    isLoading.value = true;
    error.value = null;

    try {
      if (type === 'totp') {
        await setupTOTP();
      } else if (type === 'passkey') {
        await setupPasskey();
      }
    } catch (setupError) {
      error.value = (setupError as Error).message;
    } finally {
      isLoading.value = false;
    }
  };

  const enableMultiFactorAuth = async () => {
    if (!setupData.value) {
      error.value = 'No setup data available';
      return;
    }

    if (setupData.value.type !== 'passkey' && !verificationToken.value) {
      error.value = 'Please enter a verification token';
      return;
    }

    isLoading.value = true;
    error.value = null;

    try {
      const requestBody: MultiFactorAuthEnableRequestBody = {
        methodId: setupData.value.methodId,
        code: setupData.value.type === 'passkey' ? 'passkey-verified' : verificationToken.value,
      };

      const response = await fetch('/api/auth/multi-factor/enable', {
        method: 'POST',
        body: JSON.stringify(requestBody),
      });

      const data = await response.json() as MultiFactorAuthEnableResponseBody;

      if (!data.success) {
        throw new Error(data.error || 'Failed to enable multi-factor authentication');
      }

      success.value = 'Multi-factor authentication has been enabled successfully! Reloading...';
      setupData.value = null;
      verificationToken.value = '';

      setTimeout(() => {
        window.location.reload();
      }, 2000);
    } catch (enableError) {
      error.value = (enableError as Error).message;
    } finally {
      isLoading.value = false;
    }
  };

  const disableMultiFactorAuth = async (methodId?: string, disableAll = false) => {
    if (!disablePassword.value) {
      error.value = 'Please enter your password';
      return;
    }

    isLoading.value = true;
    error.value = null;

    try {
      const requestBody: MultiFactorAuthDisableRequestBody = {
        methodId,
        password: disablePassword.value,
        disableAll,
      };

      const response = await fetch('/api/auth/multi-factor/disable', {
        method: 'POST',
        body: JSON.stringify(requestBody),
      });

      const data = await response.json() as MultiFactorAuthDisableResponseBody;

      if (!data.success) {
        throw new Error(data.error || 'Failed to disable multi-factor authentication');
      }

      success.value = 'Multi-factor authentication has been disabled successfully! Reloading...';
      showDisableForm.value = null;
      disablePassword.value = '';

      setTimeout(() => {
        window.location.reload();
      }, 2000);
    } catch (disableError) {
      error.value = (disableError as Error).message;
    } finally {
      isLoading.value = false;
    }
  };

  const cancelSetup = () => {
    setupData.value = null;
    verificationToken.value = '';
    error.value = null;
  };

  const cancelDisable = () => {
    showDisableForm.value = null;
    disablePassword.value = '';
    error.value = null;
  };

  return (
    <section class='mb-16'>
      <h2 class='text-2xl mb-4 text-left px-4 max-w-screen-md mx-auto lg:min-w-96'>
        Multi-Factor Authentication (MFA)
      </h2>

      <section class='px-4 max-w-screen-md mx-auto lg:min-w-96'>
        {error.value
          ? (
            <section class='notification-error mb-4'>
              <p>{error.value}</p>
            </section>
          )
          : null}

        {success.value
          ? (
            <section class='notification-success mb-4'>
              <p>{success.value}</p>
            </section>
          )
          : null}

        <p class='mb-6'>
          Multi-factor authentication adds an extra layer of security to your account by requiring additional
          verification beyond your password.
        </p>

        {availableMethodTypes
            .filter((type) => !enabledMethods.some((method) => method.type === type)).length > 0
          ? (
            <section class='mb-6 mt-4'>
              <h3 class='text-lg font-semibold mb-4'>
                Available Authentication Methods
              </h3>
              <section class='space-y-4'>
                {availableMethodTypes
                  .filter((type) =>
                    !enabledMethods.some((method) => method.type === type) && setupData.value?.type !== type
                  )
                  .map((type) => (
                    <section key={type} class='border rounded-lg p-4'>
                      <section class='flex items-center justify-between'>
                        <section>
                          <h4 class='font-medium'>{methodTypeLabels[type]}</h4>
                          <p class='text-sm text-gray-400'>{methodTypeDescriptions[type]}</p>
                        </section>
                        <button
                          type='button'
                          onClick={() => setupMultiFactorAuth(type)}
                          disabled={isLoading.value}
                          class='button-secondary'
                        >
                          {isLoading.value ? '...' : 'Add'}
                        </button>
                      </section>
                    </section>
                  ))}
              </section>
            </section>
          )
          : null}

        {setupData.value && setupData.value.type !== 'passkey'
          ? (
            <section class='mb-6'>
              <h3 class='text-lg font-semibold mb-4'>Setup Authenticator App</h3>

              <section class='mb-6'>
                <p class='mb-4'>
                  1. Scan this QR code with your authenticator app (Aegis Authenticator, Google Authenticator, etc.):
                </p>
                <section class='flex justify-center mb-4'>
                  <img src={setupData.value.qrCodeUrl} alt='TOTP QR Code' class='border' />
                </section>
                <p class='text-sm text-gray-400 mb-4'>
                  Or manually enter this secret:{' '}
                  <code class='bg-gray-200 px-2 py-1 rounded text-gray-900'>{setupData.value.secret}</code>
                </p>
              </section>

              <section class='mb-6'>
                <p class='mb-4'>
                  2. Save these backup codes <strong class='font-bold text-sky-500'>NOW</strong> in a safe place:
                </p>
                <section class='bg-gray-200 border rounded p-4 font-mono text-sm text-gray-900'>
                  {setupData.value.backupCodes.map((code, index) => <section key={index} class='mb-1'>{code}</section>)}
                </section>
                <p class='text-sm text-gray-400 mt-2'>
                  These codes can be used to access your account if you lose your authenticator device.{' '}
                  <strong class='font-bold text-sky-500'>They won't be visible again</strong>.
                </p>
              </section>

              <fieldset class='block mb-6'>
                <label class='text-slate-300 block pb-1'>
                  3. Enter the 6-digit code from your authenticator app:
                </label>
                <input
                  type='text'
                  value={verificationToken.value}
                  onInput={(event) => verificationToken.value = (event.target as HTMLInputElement).value}
                  placeholder='123456'
                  class='mt-1 input-field'
                  maxLength={6}
                />
              </fieldset>

              <section class='flex justify-end gap-2 mt-8 mb-4'>
                <button
                  type='button'
                  onClick={cancelSetup}
                  disabled={isLoading.value}
                  class='button-secondary'
                >
                  Cancel
                </button>
                <button
                  type='button'
                  onClick={enableMultiFactorAuth}
                  disabled={isLoading.value || !verificationToken.value}
                  class='button'
                >
                  {isLoading.value ? 'Enabling...' : 'Enable TOTP MFA'}
                </button>
              </section>
            </section>
          )
          : null}

        {setupData.value && setupData.value.type === 'passkey'
          ? (
            <section class='mb-6'>
              <h3 class='text-lg font-semibold mb-4'>Passkey Setup Complete</h3>
              <p class='mb-4'>
                Your passkey has been successfully registered! You can now enable it for multi-factor authentication.
              </p>

              <section class='flex justify-end gap-2 mt-8 mb-4'>
                <button
                  type='button'
                  onClick={cancelSetup}
                  disabled={isLoading.value}
                  class='button-secondary'
                >
                  Cancel
                </button>
                <button
                  type='button'
                  onClick={enableMultiFactorAuth}
                  disabled={isLoading.value}
                  class='button'
                >
                  {isLoading.value ? 'Enabling...' : 'Enable Passkey MFA'}
                </button>
              </section>
            </section>
          )
          : null}

        {hasMultiFactorAuthEnabled && !showDisableForm.value
          ? (
            <section>
              <section class='mb-6'>
                <h3 class='text-lg font-semibold mb-4'>Active Authentication Methods</h3>

                {enabledMethods.map((method) => (
                  <section key={method.id} class='border rounded-lg p-4 mb-4'>
                    <section class='flex items-center justify-between'>
                      <section>
                        <section
                          class={`flex items-center ${
                            method.type === 'totp' && typeof method.backupCodesCount !== 'undefined' ? 'mb-2' : ''
                          }`}
                        >
                          <span class='inline-block w-3 h-3 bg-green-500 rounded-full mr-2'></span>
                          <span class='font-medium'>{method.name}</span>
                        </section>
                        {method.type === 'totp' && typeof method.backupCodesCount !== 'undefined'
                          ? (
                            <p class='text-sm text-gray-600'>
                              {method.backupCodesCount > 0
                                ? `${method.backupCodesCount} backup codes remaining`
                                : 'No backup codes remaining'}
                            </p>
                          )
                          : null}
                      </section>
                      <button
                        type='button'
                        onClick={() => showDisableForm.value = method.id}
                        class='button-secondary'
                      >
                        Disable
                      </button>
                    </section>
                  </section>
                ))}
              </section>

              <section class='flex justify-end mt-8 mb-4'>
                <button
                  type='button'
                  onClick={() => showDisableForm.value = 'all'}
                  class='button-danger'
                >
                  Disable All MFA
                </button>
              </section>
            </section>
          )
          : null}

        {showDisableForm.value
          ? (
            <section class='mb-6'>
              <h3 class='text-lg font-semibold mb-4'>
                {showDisableForm.value === 'all'
                  ? 'Disable All Multi-Factor Authentication'
                  : 'Disable Authentication Method'}
              </h3>
              <p class='mb-4'>
                {showDisableForm.value === 'all'
                  ? 'This will disable all multi-factor authentication methods and make your account less secure.'
                  : 'This will disable this authentication method.'} Please enter your password to confirm.
              </p>

              <fieldset class='block mb-4'>
                <label class='text-slate-300 block pb-1'>Password</label>
                <input
                  type='password'
                  value={disablePassword.value}
                  onInput={(event) => disablePassword.value = (event.target as HTMLInputElement).value}
                  placeholder='Enter your password'
                  class='mt-1 input-field'
                />
              </fieldset>

              <section class='flex justify-end gap-2 mt-8 mb-4'>
                <button
                  type='button'
                  onClick={cancelDisable}
                  disabled={isLoading.value}
                  class='button-secondary'
                >
                  Cancel
                </button>
                <button
                  type='button'
                  onClick={() =>
                    disableMultiFactorAuth(
                      showDisableForm.value === 'all' ? undefined : showDisableForm.value || undefined,
                      showDisableForm.value === 'all',
                    )}
                  disabled={isLoading.value || !disablePassword.value}
                  class='button-danger'
                >
                  {isLoading.value ? 'Disabling...' : 'Disable'}
                </button>
              </section>
            </section>
          )
          : null}
      </section>
    </section>
  );
}
