"use client";

import { useEffect } from "react";
import { MapPinIcon, ExclamationTriangleIcon } from "@heroicons/react/24/outline";
import { COUNTRY_OPTIONS, getCountryCodeByName } from "@/shared/constants";
import { useZodForm } from "@/shared/hooks";
import {
  addressFormSchema,
  type AddressFormData,
  type Address,
} from "@customer-portal/domain/customer";

export interface AddressFormProps {
  // Initial values
  initialAddress?: Partial<Address>;

  // Callbacks
  onChange?: (address: Address, isValid: boolean) => void;
  onValidationChange?: (errors: string[]) => void;

  // Configuration
  title?: string;
  description?: string;
  showTitle?: boolean;
  required?: boolean;

  // Field configuration
  requiredFields?: (keyof Address)[];
  hiddenFields?: (keyof Address)[];
  fieldLabels?: Partial<Record<keyof Address, string>>;
  fieldPlaceholders?: Partial<Record<keyof Address, string>>;

  // Styling
  variant?: "default" | "compact" | "inline";
  disabled?: boolean;

  // Validation
  customValidation?: (address: Partial<Address>) => string[];
}

const normalizeCountryValue = (value?: string | null) => {
  if (!value) return "";
  if (value.length === 2) return value.toUpperCase();
  return getCountryCodeByName(value) ?? value;
};

const DEFAULT_LABELS: Partial<Record<keyof Address, string>> = {
  address1: "Building / Room (Address 1)",
  address2: "Street / Block (Address 2)",
  city: "City",
  state: "State/Prefecture",
  postcode: "Postcode",
  country: "Country",
  countryCode: "Country Code",
  phoneNumber: "Phone Number",
  phoneCountryCode: "Country Dialing Code",
};

const DEFAULT_PLACEHOLDERS: Partial<Record<keyof Address, string>> = {
  address1: "Gramercy 201",
  address2: "2-20-9 Wakabayashi",
  city: "Tokyo",
  state: "Tokyo",
  postcode: "100-0001",
  country: "Select Country",
  countryCode: "JP",
};

const DEFAULT_REQUIRED_FIELDS: (keyof Address)[] = [
  "address1",
  "city",
  "state",
  "postcode",
  "country",
];

const SELECT_COUNTRY_OPTIONS = [{ code: "", name: "Select Country" }, ...COUNTRY_OPTIONS];

function AddressField({
  field,
  type = "input",
  hiddenFields,
  required,
  requiredFields,
  labels,
  placeholders,
  formValues,
  disabled,
  variant,
  getFieldError,
  onFieldChange,
  onBlur,
}: {
  field: keyof Address;
  type?: "input" | "select";
  hiddenFields: (keyof Address)[];
  required: boolean;
  requiredFields: (keyof Address)[];
  labels: Partial<Record<keyof Address, string>>;
  placeholders: Partial<Record<keyof Address, string>>;
  formValues: Record<string, string | undefined>;
  disabled: boolean;
  variant: string;
  getFieldError: (field: keyof Address) => string | undefined;
  onFieldChange: (field: keyof Address, value: string) => void;
  onBlur: (field: keyof Address) => void;
}) {
  if (hiddenFields.includes(field)) return null;

  const isRequired = required && requiredFields.includes(field);
  const fieldError = getFieldError(field);
  const hasError = !!fieldError;

  const baseInputClasses = `w-full px-3 py-2 border rounded-md focus:outline-none focus:ring-2 transition-colors ${
    hasError
      ? "border-red-300 focus:ring-red-500 focus:border-red-500"
      : "border-border focus:ring-blue-500 focus:border-blue-500"
  } ${disabled ? "bg-muted cursor-not-allowed" : "bg-card"}`;

  return (
    <div key={field} className={variant === "compact" ? "mb-3" : "mb-4"}>
      <label className="block text-sm font-medium text-foreground mb-1">
        {labels[field]}
        {isRequired && <span className="text-red-500 ml-1">*</span>}
      </label>

      {type === "select" ? (
        <select
          value={formValues[field] || ""}
          onChange={e => onFieldChange(field, e.target.value)}
          onBlur={() => onBlur(field)}
          className={baseInputClasses}
          disabled={disabled}
        >
          {SELECT_COUNTRY_OPTIONS.map(option => (
            <option key={option.code} value={option.code}>
              {option.name}
            </option>
          ))}
        </select>
      ) : (
        <input
          type="text"
          value={formValues[field] || ""}
          onChange={e => onFieldChange(field, e.target.value)}
          onBlur={() => onBlur(field)}
          placeholder={placeholders[field]}
          className={baseInputClasses}
          disabled={disabled}
        />
      )}

      {hasError && (
        <div className="mt-1 flex items-center gap-1 text-red-600">
          <ExclamationTriangleIcon className="h-4 w-4 flex-shrink-0" />
          <span className="text-sm">{fieldError}</span>
        </div>
      )}
    </div>
  );
}

function ValidationErrors({ errors, hasTouched }: { errors: string[]; hasTouched: boolean }) {
  if (errors.length === 0 || !hasTouched) return null;
  return (
    <div className="mt-4 p-3 bg-red-50 border border-red-200 rounded-lg">
      <div className="flex items-start gap-2">
        <ExclamationTriangleIcon className="h-5 w-5 text-red-500 flex-shrink-0 mt-0.5" />
        <div>
          <p className="text-sm font-medium text-red-800">Please correct the following:</p>
          <ul className="mt-1 text-sm text-red-700 list-disc list-inside">
            {errors.map((error, index) => (
              <li key={index}>{error}</li>
            ))}
          </ul>
        </div>
      </div>
    </div>
  );
}

function buildInitialValues(initialAddress: Partial<Address>): AddressFormData {
  const initialCountry = normalizeCountryValue(initialAddress.country);
  const initialCountryCode = normalizeCountryValue(initialAddress.countryCode ?? initialCountry);
  return {
    address1: initialAddress.address1 || "",
    address2: initialAddress.address2 || "",
    city: initialAddress.city || "",
    state: initialAddress.state || "",
    postcode: initialAddress.postcode || "",
    country: initialCountry,
    countryCode: initialCountryCode,
    phoneNumber: initialAddress.phoneNumber || "",
    phoneCountryCode: initialAddress.phoneCountryCode || "",
  };
}

function applyCountryFieldSync(
  field: keyof Address,
  value: string,
  setValue: (f: keyof Address, v: string) => void
) {
  if (field === "country") {
    setValue("country", value);
    setValue("countryCode", value);
  } else if (field === "countryCode") {
    setValue("countryCode", value);
    setValue("country", value);
  } else {
    setValue(field, value);
  }
}

function syncUpdatedValues(
  field: keyof Address,
  value: string,
  formValues: AddressFormData
): AddressFormData {
  const updatedValues = { ...formValues, [field]: value } as AddressFormData;
  if (field === "country") {
    updatedValues.country = value;
    updatedValues.countryCode = value;
  } else if (field === "countryCode") {
    updatedValues.countryCode = value;
    updatedValues.country = value;
  }
  return updatedValues;
}

function AddressFormHeader({
  title,
  description,
}: {
  title: string;
  description: string | undefined;
}) {
  return (
    <div className="mb-6">
      <div className="flex items-center gap-2 mb-2">
        <MapPinIcon className="h-5 w-5 text-blue-600" />
        <h3 className="text-lg font-semibold text-foreground">{title}</h3>
      </div>
      {description && <p className="text-sm text-muted-foreground">{description}</p>}
    </div>
  );
}

function syncFormWithAddress(
  initialAddress: Partial<Address>,
  formSetValue: (key: keyof Address, value: string) => void
) {
  const nextValues: Partial<Address> = {
    address1: initialAddress.address1,
    address2: initialAddress.address2,
    city: initialAddress.city,
    state: initialAddress.state,
    postcode: initialAddress.postcode,
    country: initialAddress.country,
    countryCode: initialAddress.countryCode,
    phoneNumber: initialAddress.phoneNumber,
    phoneCountryCode: initialAddress.phoneCountryCode,
  };

  for (const [key, value] of Object.entries(nextValues)) {
    if (value !== undefined) {
      const normalizedValue =
        key === "country" || key === "countryCode"
          ? normalizeCountryValue(value as string | undefined)
          : value || "";
      formSetValue(key as keyof Address, normalizedValue);
    }
  }

  const normalizedCountry = normalizeCountryValue(initialAddress.country);
  const normalizedCountryCode = normalizeCountryValue(
    initialAddress.countryCode ?? initialAddress.country
  );
  formSetValue("country", normalizedCountry);
  formSetValue("countryCode", normalizedCountryCode);
}

const CONTAINER_VARIANT_CLASSES = {
  inline: "",
  compact: "p-4 bg-muted rounded-lg border border-border",
  default: "p-6 bg-card border border-border rounded-lg",
};

export function AddressForm({
  initialAddress = {},
  onChange,
  onValidationChange,
  title = "Address Information",
  description,
  showTitle = true,
  required = true,
  requiredFields = DEFAULT_REQUIRED_FIELDS,
  hiddenFields = [],
  fieldLabels = {},
  fieldPlaceholders = {},
  variant = "default",
  disabled = false,
  customValidation,
}: AddressFormProps) {
  const labels = { ...DEFAULT_LABELS, ...fieldLabels };
  const placeholders = { ...DEFAULT_PLACEHOLDERS, ...fieldPlaceholders };

  const form = useZodForm({
    schema: addressFormSchema,
    initialValues: buildInitialValues(initialAddress),
  });

  const handleFieldChange = (field: keyof Address, value: string) => {
    if (disabled) return;

    applyCountryFieldSync(field, value, form.setValue);

    if (customValidation) {
      onValidationChange?.(customValidation({ ...form.values, [field]: value }));
    }

    const updatedValues = syncUpdatedValues(field, value, form.values);
    const isComplete = requiredFields
      .filter(f => !hiddenFields.includes(f))
      .every(f => updatedValues[f] && String(updatedValues[f]).trim());

    if (onChange && isComplete) {
      onChange(updatedValues as Address, form.isValid);
    }
  };

  const formSetValue = form.setValue;
  const {
    address1,
    address2,
    city,
    state,
    postcode,
    country,
    countryCode,
    phoneNumber,
    phoneCountryCode,
  } = initialAddress ?? {};
  const hasInitialAddress = !!initialAddress;

  useEffect(() => {
    if (!hasInitialAddress) return;
    syncFormWithAddress(
      {
        address1,
        address2,
        city,
        state,
        postcode,
        country,
        countryCode,
        phoneNumber,
        phoneCountryCode,
      },
      formSetValue
    );
  }, [
    formSetValue,
    hasInitialAddress,
    address1,
    address2,
    city,
    state,
    postcode,
    country,
    countryCode,
    phoneNumber,
    phoneCountryCode,
  ]);

  // Notify parent of validation changes
  useEffect(() => {
    const errorMessages = Object.values(form.errors).filter(Boolean) as string[];
    onValidationChange?.(errorMessages);
  }, [form.errors, onValidationChange]);

  const getFieldError = (field: keyof Address) => {
    return form.touched[field] ? form.errors[field] : undefined;
  };

  const fieldProps = {
    hiddenFields,
    required,
    requiredFields,
    labels,
    placeholders,
    formValues: form.values,
    disabled,
    variant,
    getFieldError,
    onFieldChange: handleFieldChange,
    onBlur: (field: keyof Address) => form.setTouched(field, true),
  };

  const allErrors = Object.values(form.errors).filter(Boolean) as string[];
  const hasAnyTouched = Object.values(form.touched).some(Boolean);

  return (
    <div className={CONTAINER_VARIANT_CLASSES[variant]}>
      {showTitle && <AddressFormHeader title={title} description={description} />}

      <div className="space-y-4">
        <AddressField field="address2" {...fieldProps} />
        <AddressField field="address1" {...fieldProps} />
        <div
          className={`grid gap-4 ${variant === "compact" ? "grid-cols-1 sm:grid-cols-3" : "grid-cols-1 md:grid-cols-3"}`}
        >
          <AddressField field="city" {...fieldProps} />
          <AddressField field="state" {...fieldProps} />
          <AddressField field="postcode" {...fieldProps} />
        </div>
        <AddressField field="country" type="select" {...fieldProps} />
      </div>

      <ValidationErrors errors={allErrors} hasTouched={hasAnyTouched} />
    </div>
  );
}
