/**
 * Signup Form - Multi-step signup for Japanese customers
 * Uses domain schemas from @customer-portal/domain
 */

"use client";

import { useState, useCallback, useEffect, useRef } from "react";
import { flushSync } from "react-dom";
import Link from "next/link";
import { useSearchParams } from "next/navigation";
import { ErrorMessage } from "@/components/atoms";
import { useSignupWithRedirect } from "../../hooks/use-auth";
import { signupInputSchema, buildSignupRequest } from "@customer-portal/domain/auth";
import { genderEnum } from "@customer-portal/domain/common";
import { addressFormSchema } from "@customer-portal/domain/customer";
import { useZodForm } from "@/shared/hooks";
import { z } from "zod";
import { getSafeRedirect } from "@/features/auth/utils/route-protection";
import { formatJapanesePostalCode } from "@/shared/constants";

import { MultiStepForm } from "./MultiStepForm";
import { AccountStep } from "./steps/AccountStep";
import { AddressStepJapan } from "@/features/address";
import { PasswordStep } from "./steps/PasswordStep";

/**
 * Frontend signup form schema
 * Extends the domain signupInputSchema with:
 * - confirmPassword: UI-only field for password confirmation
 * - phoneCountryCode: Separate field for country code input
 * - address: Required addressFormSchema (domain schema makes it optional)
 * - dateOfBirth: Required for signup (domain schema makes it optional)
 * - gender: Required for signup (domain schema makes it optional)
 */
const genderSchema = genderEnum;

const signupAddressSchema = addressFormSchema.extend({
  address2: z
    .string()
    .min(1, "Address line 2 is required")
    .max(200, "Address line 2 is too long")
    .trim(),
});

const signupFormBaseSchema = signupInputSchema.omit({ sfNumber: true }).extend({
  confirmPassword: z.string().min(1, "Please confirm your password"),
  phoneCountryCode: z.string().regex(/^\+\d{1,4}$/, "Enter a valid country code (e.g., +81)"),
  address: signupAddressSchema,
  dateOfBirth: z.string().min(1, "Date of birth is required"),
  gender: genderSchema,
});

const signupFormSchema = signupFormBaseSchema
  .refine(data => data.acceptTerms === true, {
    message: "You must accept the terms and conditions",
    path: ["acceptTerms"],
  })
  .refine(data => data.password === data.confirmPassword, {
    message: "Passwords do not match",
    path: ["confirmPassword"],
  });

type SignupFormData = z.infer<typeof signupFormSchema>;
type SignupAddress = SignupFormData["address"];

interface SignupFormProps {
  onSuccess?: (() => void) | undefined;
  onError?: ((error: string) => void) | undefined;
  className?: string | undefined;
  redirectTo?: string | undefined;
  initialEmail?: string | undefined;
  showFooterLinks?: boolean | undefined;
}

const STEPS = [
  {
    key: "account",
    title: "Account Details",
    description: "Your contact information",
  },
  {
    key: "address",
    title: "Address",
    description: "Used for service eligibility and delivery",
  },
  {
    key: "security",
    title: "Security & Terms",
    description: "Create a password and confirm agreements",
  },
] as const;

const STEP_FIELD_KEYS: Record<(typeof STEPS)[number]["key"], Array<keyof SignupFormData>> = {
  account: ["firstName", "lastName", "email", "phone", "phoneCountryCode", "dateOfBirth", "gender"],
  address: ["address"],
  security: ["password", "confirmPassword", "acceptTerms", "marketingConsent"],
};

const STEP_VALIDATION_SCHEMAS: Record<(typeof STEPS)[number]["key"], z.ZodTypeAny | undefined> = {
  account: signupFormBaseSchema.pick({
    firstName: true,
    lastName: true,
    email: true,
    phone: true,
    phoneCountryCode: true,
    dateOfBirth: true,
    gender: true,
  }),
  address: signupFormBaseSchema.pick({
    address: true,
  }),
  security: signupFormBaseSchema
    .pick({
      password: true,
      confirmPassword: true,
      acceptTerms: true,
    })
    .refine(data => data.password === data.confirmPassword, {
      message: "Passwords do not match",
      path: ["confirmPassword"],
    })
    .refine(data => data.acceptTerms === true, {
      message: "You must accept the terms and conditions",
      path: ["acceptTerms"],
    }),
};

export function SignupForm({
  onSuccess,
  onError,
  className = "",
  redirectTo,
  initialEmail,
  showFooterLinks = true,
}: SignupFormProps) {
  const formRef = useRef<HTMLFormElement | null>(null);
  const searchParams = useSearchParams();
  const [step, setStep] = useState(0);
  const redirectFromQuery = searchParams?.get("next") || searchParams?.get("redirect");
  const redirect = getSafeRedirect(redirectTo || redirectFromQuery, "");
  const redirectQuery = redirect ? `?redirect=${encodeURIComponent(redirect)}` : "";
  const { signup, loading, error, clearError } = useSignupWithRedirect(
    redirect ? { redirectTo: redirect } : undefined
  );

  const form = useZodForm<SignupFormData>({
    schema: signupFormSchema,
    initialValues: {
      firstName: "",
      lastName: "",
      email: initialEmail ?? "",
      phone: "",
      phoneCountryCode: "+81",
      company: "",
      dateOfBirth: "",
      gender: "" as unknown as "male" | "female" | "other", // Will be validated on submit
      address: {
        address1: "",
        address2: "",
        city: "",
        state: "",
        postcode: "",
        country: "JP",
        countryCode: "JP",
      },
      password: "",
      confirmPassword: "",
      acceptTerms: false,
      marketingConsent: false,
    },
    onSubmit: async data => {
      clearError();
      try {
        // Combine country code + phone for WHMCS format: +CC.NNNNNNNN
        const countryDigits = data.phoneCountryCode.replace(/\D/g, "");
        const phoneDigits = data.phone.replace(/\D/g, "");
        const formattedPhone = `+${countryDigits}.${phoneDigits}`;

        // Build request with normalized address and phone
        // Exclude UI-only fields (confirmPassword) from the request
        const { confirmPassword: _confirmPassword, ...requestData } = data;
        const request = buildSignupRequest({
          ...requestData,
          phone: formattedPhone,
          dateOfBirth: data.dateOfBirth || undefined,
          gender: data.gender || undefined,
          address: {
            ...data.address,
            country: "JP",
            countryCode: "JP",
          },
        });
        await signup(request);
        onSuccess?.();
      } catch (err) {
        onError?.(err instanceof Error ? err.message : "Signup failed");
        throw err;
      }
    },
  });

  const {
    values,
    errors,
    touched,
    setValue: setFormValue,
    setTouchedField: setFormTouchedField,
    handleSubmit,
    isSubmitting,
  } = form;

  const normalizeAutofillValue = useCallback((field: string, value: string) => {
    switch (field) {
      case "phoneCountryCode": {
        let normalized = value.replace(/[^\d+]/g, "");
        if (!normalized.startsWith("+")) normalized = "+" + normalized.replace(/\+/g, "");
        return normalized.slice(0, 5);
      }
      case "phone":
        return value.replace(/\D/g, "");
      case "address.postcode":
        return formatJapanesePostalCode(value);
      default:
        return value;
    }
  }, []);

  const syncStepValues = useCallback(
    (shouldFlush = true) => {
      const formNode = formRef.current;
      if (!formNode) {
        return values;
      }

      const nextValues: SignupFormData = {
        ...values,
        address: { ...values.address },
      };

      const fields = formNode.querySelectorAll<HTMLInputElement | HTMLSelectElement>(
        "[data-field]"
      );
      for (const field of fields) {
        const key = field.dataset["field"];
        if (!key) {
          continue;
        }
        const normalized = normalizeAutofillValue(key, field.value);
        if (key.startsWith("address.")) {
          const addressKey = key.replace("address.", "") as keyof SignupAddress;
          nextValues.address[addressKey] = normalized;
        } else if (key === "acceptTerms" || key === "marketingConsent") {
          // Handle boolean fields separately
          const boolValue =
            field.type === "checkbox" ? (field as HTMLInputElement).checked : normalized === "true";
          (nextValues as Record<string, unknown>)[key] = boolValue;
        } else {
          // Only assign to string fields
          const stringKey = key as keyof Pick<
            SignupFormData,
            Exclude<keyof SignupFormData, "address" | "acceptTerms" | "marketingConsent">
          >;
          (nextValues as Record<string, unknown>)[stringKey] = normalized;
        }
      }

      const applySyncedValues = () => {
        for (const key of Object.keys(nextValues) as Array<keyof SignupFormData>) {
          if (key === "address") {
            continue;
          }
          if (nextValues[key] !== values[key]) {
            setFormValue(key, nextValues[key]);
          }
        }

        const addressChanged = (Object.keys(nextValues.address) as Array<keyof SignupAddress>).some(
          key => nextValues.address[key] !== values.address[key]
        );
        if (addressChanged) {
          setFormValue("address", nextValues.address);
        }
      };

      if (shouldFlush) {
        flushSync(() => {
          applySyncedValues();
        });
      } else {
        applySyncedValues();
      }

      return nextValues;
    },
    [normalizeAutofillValue, setFormValue, values]
  );

  useEffect(() => {
    const syncTimer = window.setTimeout(() => {
      syncStepValues(false);
    }, 0);

    return () => {
      window.clearTimeout(syncTimer);
    };
  }, [step, syncStepValues]);

  const isLastStep = step === STEPS.length - 1;

  const markStepTouched = useCallback(
    (stepIndex: number) => {
      const stepKey = STEPS[stepIndex]?.key;
      if (!stepKey) {
        return;
      }
      const fields = STEP_FIELD_KEYS[stepKey] ?? [];
      for (const field of fields) setFormTouchedField(field);
    },
    [setFormTouchedField]
  );

  const isStepValid = useCallback(
    (stepIndex: number, data: SignupFormData = values) => {
      const stepKey = STEPS[stepIndex]?.key;
      if (!stepKey) {
        return true;
      }
      const schema = STEP_VALIDATION_SCHEMAS[stepKey];
      if (!schema) {
        return true;
      }
      return schema.safeParse(data).success;
    },
    [values]
  );

  const handleNext = useCallback(() => {
    const syncedValues = syncStepValues();
    markStepTouched(step);

    if (isLastStep) {
      void handleSubmit();
      return;
    }

    if (!isStepValid(step, syncedValues)) {
      return;
    }

    setStep(s => Math.min(s + 1, STEPS.length - 1));
  }, [handleSubmit, isLastStep, isStepValid, markStepTouched, step, syncStepValues]);

  const handlePrevious = useCallback(() => {
    setStep(s => Math.max(0, s - 1));
  }, []);

  // Wrap form methods to have generic types for step components
  const formProps = {
    values,
    errors,
    touched,
    setValue: (field: string, value: unknown) =>
      setFormValue(field as keyof SignupFormData, value as never),
    setTouchedField: (field: string) => setFormTouchedField(field as keyof SignupFormData),
  };

  const stepContent = [
    <AccountStep key="account" form={formProps} />,
    <AddressStepJapan key="address" form={formProps} />,
    <PasswordStep key="security" form={formProps} />,
  ];

  const steps = STEPS.map((s, i) => ({
    ...s,
    content: stepContent[i],
  }));

  return (
    <div className={`w-full ${className}`}>
      <form
        ref={formRef}
        autoComplete="on"
        onSubmit={event => {
          event.preventDefault();
          handleNext();
        }}
      >
        <MultiStepForm
          steps={steps}
          currentStep={step}
          onNext={handleNext}
          onPrevious={handlePrevious}
          isLastStep={isLastStep}
          isSubmitting={isSubmitting || loading}
          canProceed={isLastStep || isStepValid(step)}
        />
      </form>

      {error && (
        <ErrorMessage className="mt-4 text-center p-3 bg-danger-soft rounded-lg">
          {error}
        </ErrorMessage>
      )}

      {showFooterLinks && (
        <div className="mt-6 text-center border-t border-border pt-6 space-y-3">
          <p className="text-sm text-muted-foreground">
            Already have an account?{" "}
            <Link
              href={`/auth/login${redirectQuery}`}
              className="font-medium text-primary hover:underline transition-colors"
            >
              Sign in
            </Link>
          </p>
          <p className="text-sm text-muted-foreground">
            Existing customer?{" "}
            <Link
              href="/auth/migrate"
              className="font-medium text-primary hover:underline transition-colors"
            >
              Migrate your account
            </Link>
          </p>
        </div>
      )}
    </div>
  );
}
