"use client";

import type { TwoFactorDevice } from "@domainops/shared-types";
import { App, Button, Form, Input } from "antd";
import QRCode from "qrcode";
import { useEffect, useState } from "react";
import { confirmTwoFactorDevice, setupTwoFactorDevice } from "@/lib/api/auth";
import { ApiError } from "@/lib/api/client";

interface EnrollFormValues {
  label: string;
  code: string;
}

/**
 * The QR-scan-then-confirm enrollment flow, shared between the login
 * page's forced first-device setup and the profile page's "Add device"
 * action - one flow, two entry points, per docs/ARCHITECTURE.md §9.
 * `autoStart` skips the initial button so the login flow can drop straight
 * into a QR code rather than making an already-blocked user click first.
 */
export function TwoFactorEnrollForm({
  autoStart = false,
  onEnrolled,
  onCancel,
}: {
  autoStart?: boolean;
  onEnrolled: (device: TwoFactorDevice) => void;
  onCancel?: () => void;
}) {
  const { message } = App.useApp();
  const [form] = Form.useForm<EnrollFormValues>();
  const [pendingSecret, setPendingSecret] = useState<{ secret: string; otpauth_url: string } | null>(null);
  const [qrDataUrl, setQrDataUrl] = useState<string | null>(null);
  const [isBusy, setIsBusy] = useState(false);

  useEffect(() => {
    if (!pendingSecret) {
      // eslint-disable-next-line react-hooks/set-state-in-effect
      setQrDataUrl(null);
      return;
    }
    // Rendered locally from the otpauth:// URL - it carries the raw TOTP
    // secret, so this must never go through a third-party QR API.
    let cancelled = false;
    QRCode.toDataURL(pendingSecret.otpauth_url, { width: 200, margin: 1 })
      .then((url) => {
        if (!cancelled) setQrDataUrl(url);
      })
      .catch(() => {
        if (!cancelled) setQrDataUrl(null);
      });
    return () => {
      cancelled = true;
    };
  }, [pendingSecret]);

  async function handleStartSetup() {
    setIsBusy(true);
    try {
      setPendingSecret(await setupTwoFactorDevice());
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "Couldn't start setup.");
    } finally {
      setIsBusy(false);
    }
  }

  useEffect(() => {
    if (autoStart && !pendingSecret) {
      // eslint-disable-next-line react-hooks/set-state-in-effect
      void handleStartSetup();
    }
    // Only ever fires once, when the login flow forces this step - not
    // meant to re-run if pendingSecret later clears.
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [autoStart]);

  async function handleConfirm(values: EnrollFormValues) {
    if (!pendingSecret) return;
    setIsBusy(true);
    try {
      const device = await confirmTwoFactorDevice({
        secret: pendingSecret.secret,
        code: values.code,
        label: values.label,
      });
      message.success("Device added.");
      form.resetFields();
      setPendingSecret(null);
      onEnrolled(device);
    } catch (err) {
      message.error(err instanceof ApiError ? err.message : "That code didn't work.");
    } finally {
      setIsBusy(false);
    }
  }

  if (!pendingSecret) {
    return (
      <Button type="primary" onClick={handleStartSetup} loading={isBusy} className="dms_2fa_start_setup_btn">
        Add a device
      </Button>
    );
  }

  return (
    <Form<EnrollFormValues>
      form={form}
      layout="vertical"
      onFinish={handleConfirm}
      disabled={isBusy}
      initialValues={{ label: "" }}
      className="dms_2fa_enroll_form flex flex-col gap-3"
    >
      <p className="text-sm text-secondary">
        Scan this with your authenticator app (Google Authenticator, Authy, 1Password, ...), then name the device
        and enter the code it shows to confirm.
      </p>
      {qrDataUrl && (
        // eslint-disable-next-line @next/next/no-img-element -- a locally-generated data URL, not a remote image next/image would optimize
        <img
          src={qrDataUrl}
          alt="Scan this QR code with your authenticator app"
          width={200}
          height={200}
          className="dms_2fa_qr_image"
        />
      )}
      <p className="text-xs text-secondary">
        Can&apos;t scan it? Enter this key manually instead:
      </p>
      <div className="dms_2fa_manual_secret font-mono text-xs bg-black/20 rounded px-3 py-2 break-all">
        {pendingSecret.secret}
      </div>
      <Form.Item
        name="label"
        label="Device name"
        rules={[{ required: true, message: "Give this device a name" }]}
        className="dms_2fa_device_label_field"
      >
        <Input placeholder="e.g. My Phone" autoFocus />
      </Form.Item>
      <Form.Item
        name="code"
        label="Authentication code"
        rules={[{ required: true, len: 6, message: "6-digit code" }]}
        className="dms_2fa_device_code_field"
      >
        <Input placeholder="123456" inputMode="numeric" maxLength={6} />
      </Form.Item>
      <div className="flex gap-2">
        <Form.Item className="dms_2fa_confirm_field" style={{ marginBottom: 0 }}>
          <Button type="primary" htmlType="submit" loading={isBusy} className="dms_2fa_confirm_btn">
            Confirm and enable
          </Button>
        </Form.Item>
        {onCancel && (
          <Button onClick={onCancel} disabled={isBusy} className="dms_2fa_cancel_btn">
            Cancel
          </Button>
        )}
      </div>
    </Form>
  );
}
