"use client";

import { PhoneNumberFormat, PhoneNumberUtil } from "google-libphonenumber";
import { useEffect, useRef, useState, useMemo, useCallback } from "react";
import type { QuestionField } from "@/data/question-data";
import type { MarriagePhoneFieldValue } from "@/hooks/marriage/types";
import { useQuestionAnswers } from "./question-answer-storage";
import QuestionTitle from "./question-title";
import { LoadingSkeleton } from "./loading-skeleton";
import { useI18n } from "@/translations/provider";

type QuestionPhoneProps = {
  question: QuestionField;
  questionIndex: number;
  countryCode?: string;
  disabled?: boolean;
};

type PhoneValueParts = {
  codeValue: string;
  phoneValue: string;
};

const phoneUtil = PhoneNumberUtil.getInstance();

function isMarriagePhoneFieldValue(
  value: unknown,
): value is MarriagePhoneFieldValue {
  if (!value || typeof value !== "object") {
    return false;
  }

  const phoneValue = value as Partial<MarriagePhoneFieldValue>;

  return (
    typeof phoneValue.countryCode === "string" &&
    typeof phoneValue.phoneNumber === "string"
  );
}

function readPhoneValue(value: unknown, fallbackCode: string): PhoneValueParts {
  if (value === null) {
    return {
      codeValue: fallbackCode,
      phoneValue: "",
    };
  }

  if (isMarriagePhoneFieldValue(value)) {
    return {
      codeValue: value.countryCode
        ? `+${value.countryCode.replace(/^\+/, "")}`
        : fallbackCode,
      phoneValue: value.phoneNumber,
    };
  }

  if (typeof value !== "string") {
    return {
      codeValue: fallbackCode,
      phoneValue: "",
    };
  }

  if (value.length === 0) {
    return {
      codeValue: fallbackCode,
      phoneValue: "",
    };
  }

  const separatorIndex = value.indexOf(" ");

  if (separatorIndex >= 0) {
    return {
      codeValue: value.slice(0, separatorIndex),
      phoneValue: value.slice(separatorIndex + 1),
    };
  }

  if (value.startsWith("+")) {
    try {
      const parsedNumber = phoneUtil.parse(value);
      const countryCode = parsedNumber.getCountryCode();
      const nationalNumber = String(parsedNumber.getNationalNumber());

      return {
        codeValue: countryCode ? `+${countryCode}` : fallbackCode,
        phoneValue: nationalNumber,
      };
    } catch {
      return {
        codeValue: fallbackCode,
        phoneValue: value,
      };
    }
  }

  return {
    codeValue: fallbackCode,
    phoneValue: value,
  };
}

function getMaxLengthForCountry(codeValue: string): number {
  try {
    const cleanCode = codeValue.replace(/[^\d]/g, "");
    if (!cleanCode) return 15;
    const countryCode = Number(cleanCode);
    const regionCode = phoneUtil.getRegionCodeForCountryCode(countryCode);
    if (!regionCode || regionCode === "ZZ") return 15;

    const exampleMobile = phoneUtil.getExampleNumberForType(
      regionCode,
      1, // MOBILE
    );
    const exampleGeneral = phoneUtil.getExampleNumber(regionCode);

    const lenMobile = exampleMobile
      ? String(exampleMobile.getNationalNumber()).length
      : 0;
    const lenGeneral = exampleGeneral
      ? String(exampleGeneral.getNationalNumber()).length
      : 0;
    const baseLen = Math.max(lenMobile, lenGeneral, 8);

    return baseLen + 1;
  } catch {
    return 15;
  }
}

function writePhoneValue(codeValue: string, phoneValue: string) {
  if (!codeValue && !phoneValue) {
    return null;
  }

  if (codeValue && phoneValue) {
    return `${codeValue} ${phoneValue}`;
  }

  if (codeValue) {
    return codeValue.startsWith("+") ? codeValue : `${codeValue} `;
  }

  return phoneValue;
}

function toStoredPhoneValue(
  codeValue: string,
  phoneValue: string,
): MarriagePhoneFieldValue | null {
  const normalizedCountryCode = sanitizeCountryCode(codeValue).replace(
    /^\+/,
    "",
  );
  const normalizedPhoneNumber = phoneValue.trim().replace(/\s+/g, "");

  if (!normalizedCountryCode && !normalizedPhoneNumber) {
    return null;
  }

  return {
    countryCode: normalizedCountryCode,
    phoneNumber: normalizedPhoneNumber,
  };
}

function sanitizeCountryCode(value: string) {
  const sanitized = value.replace(/[^\d+]/g, "");

  if (sanitized.length === 0) {
    return "";
  }

  return sanitized.startsWith("+")
    ? `+${sanitized.slice(1).replace(/\+/g, "")}`
    : `+${sanitized.replace(/\+/g, "")}`;
}

function sanitizePhoneNumber(value: string) {
  return value.replace(/[^\d\s\-().]/g, "");
}

function getNormalizedPhoneValue(codeValue: string, phoneValue: string) {
  const nextCodeValue = sanitizeCountryCode(codeValue);
  const nextPhoneValue = phoneValue.trim();

  if (nextCodeValue.length === 0 && nextPhoneValue.length === 0) {
    return {
      isValid: !nextPhoneValue.length,
      normalizedValue: null,
    };
  }

  if (nextCodeValue.length === 0 || nextPhoneValue.length === 0) {
    return {
      isValid: false,
      normalizedValue: null,
    };
  }

  try {
    const parsedNumber = phoneUtil.parse(`${nextCodeValue} ${nextPhoneValue}`);

    if (!phoneUtil.isValidNumber(parsedNumber)) {
      return {
        isValid: false,
        normalizedValue: null,
      };
    }

    return {
      isValid: true,
      normalizedValue: phoneUtil.format(parsedNumber, PhoneNumberFormat.E164),
    };
  } catch {
    return {
      isValid: false,
      normalizedValue: null,
    };
  }
}

export function QuestionPhone({
  question,
  questionIndex,
  countryCode = "+44",
  disabled,
}: QuestionPhoneProps) {
  const { locale } = useI18n();
  const { getAnswerValue, setAnswerValue, isLoading } = useQuestionAnswers();
  const value = getAnswerValue(question, questionIndex);
  const defaultCodeValue = countryCode.trim() || "+44";

  const getCachedOrSavedCode = useCallback((): string | null => {
    if (typeof window === "undefined") return null;
    return localStorage.getItem("geoIPPhoneCode");
  }, []);

  const initialCode = useMemo(() => {
    if (isMarriagePhoneFieldValue(value) && value.countryCode) {
      return value.countryCode.startsWith("+")
        ? value.countryCode
        : `+${value.countryCode}`;
    }
    if (typeof value === "string" && value.length > 0) {
      const parts = readPhoneValue(value, defaultCodeValue);
      if (parts.codeValue && parts.codeValue !== defaultCodeValue) {
        return parts.codeValue;
      }
    }
    const cached = getCachedOrSavedCode();
    if (cached) return cached;
    return defaultCodeValue;
  }, [value, defaultCodeValue, getCachedOrSavedCode]);

  const initialPhone = useMemo(() => {
    return readPhoneValue(value, defaultCodeValue).phoneValue;
  }, [value, defaultCodeValue]);

  const [codeValue, setCodeValue] = useState(initialCode);
  const [phoneValue, setPhoneValue] = useState(initialPhone);
  const lastCommittedValueRef = useRef(value);
  const hasFetchedIpRef = useRef(false);
  const userInteractedRef = useRef(false);

  const needsIpFetch = useCallback(() => {
    if (typeof window === "undefined") return false;
    if (localStorage.getItem("geoIPPhoneCode")) return false;
    if (localStorage.getItem("hasCheckedGeoIPPhone")) return false;
    return true;
  }, []);

  const [isResolvingCode, setIsResolvingCode] = useState(() => {
    if (isLoading) return true;
    const hasSavedValue =
      value &&
      ((isMarriagePhoneFieldValue(value) &&
        (value.countryCode || value.phoneNumber)) ||
        (typeof value === "string" && value.trim().length > 0));
    if (hasSavedValue) return false;
    return needsIpFetch();
  });

  const [isOpen, setIsOpen] = useState(false);
  const [searchQuery, setSearchQuery] = useState("");
  const containerRef = useRef<HTMLDivElement>(null);
  const listRef = useRef<HTMLDivElement>(null);
  const searchInputRef = useRef<HTMLInputElement>(null);

  const normalizedPhoneState = getNormalizedPhoneValue(codeValue, phoneValue);
  const showInvalidState =
    codeValue.trim().length > 0 &&
    phoneValue.trim().length > 0 &&
    !normalizedPhoneState.isValid;
  const isAnswered =
    question.required === false
      ? normalizedPhoneState.isValid || phoneValue.trim().length === 0
      : normalizedPhoneState.isValid;

  const countryList = useMemo(() => {
    const regions = phoneUtil.getSupportedRegions();
    const displayNames = new Intl.DisplayNames([locale || "en"], {
      type: "region",
    });
    const list: { name: string; code: string; flag: string }[] = [];
    const seen = new Set<string>();

    for (const region of regions) {
      try {
        const callingCode = phoneUtil.getCountryCodeForRegion(region);
        if (!callingCode) continue;

        let name = displayNames.of(region);
        if (!name) continue;

        if (region === "US")
          name = locale === "fa" ? "ایالات متحده آمریکا" : "United States";
        if (region === "GB")
          name = locale === "fa" ? "بریتانیا" : "United Kingdom";

        const key = `${name}_+${callingCode}`;
        if (seen.has(key)) continue;
        seen.add(key);

        const codePoints = region
          .toUpperCase()
          .split("")
          .map((char) => 127397 + char.charCodeAt(0));
        const flag = String.fromCodePoint(...codePoints);

        list.push({
          name,
          code: `+${callingCode}`,
          flag,
        });
      } catch (e) {}
    }

    return list.sort((a, b) => a.name.localeCompare(b.name));
  }, [locale]);

  const activeFlag = useMemo(() => {
    if (!codeValue) return "🏳️";
    const cleanActiveCode = codeValue.replace(/[^\d]/g, "");
    const match = countryList.find(
      (c) => c.code.replace(/[^\d]/g, "") === cleanActiveCode,
    );
    return match ? match.flag : "🏳️";
  }, [codeValue, countryList]);

  const filteredCountries = useMemo(() => {
    const q = searchQuery.toLowerCase().trim();
    if (!q) return countryList;
    return countryList.filter(
      (c) =>
        c.name.toLowerCase().includes(q) || c.code.toLowerCase().includes(q),
    );
  }, [countryList, searchQuery]);

  // Lock page scroll when dropdown is open
  useEffect(() => {
    if (isOpen) {
      document.body.classList.add("dropdown-open");
      document.body.style.overflow = "hidden";
      document.documentElement.style.overflow = "hidden";
    } else {
      document.body.classList.remove("dropdown-open");
      document.body.style.overflow = "";
      document.documentElement.style.overflow = "";
    }
    return () => {
      document.body.classList.remove("dropdown-open");
      document.body.style.overflow = "";
      document.documentElement.style.overflow = "";
    };
  }, [isOpen]);

  useEffect(() => {
    if (isOpen) {
      const timer = setTimeout(() => {
        searchInputRef.current?.focus();
      }, 50);
      return () => clearTimeout(timer);
    }
  }, [isOpen]);

  // Close dropdown on click outside
  useEffect(() => {
    function handleClickOutside(event: MouseEvent) {
      if (
        containerRef.current &&
        !containerRef.current.contains(event.target as Node)
      ) {
        setIsOpen(false);
      }
    }
    document.addEventListener("mousedown", handleClickOutside);
    return () => {
      document.removeEventListener("mousedown", handleClickOutside);
    };
  }, []);

  // Sync state if external value changes (e.g. backend data loaded)
  useEffect(() => {
    if (value === lastCommittedValueRef.current) {
      return;
    }

    const nextValue = readPhoneValue(value, defaultCodeValue);
    const cachedCode = getCachedOrSavedCode();

    const resolvedCode =
      (isMarriagePhoneFieldValue(value) && value.countryCode) ||
      (typeof value === "string" &&
        value.trim().length > 0 &&
        nextValue.codeValue !== defaultCodeValue)
        ? nextValue.codeValue
        : cachedCode || defaultCodeValue;

    const maxLen = getMaxLengthForCountry(resolvedCode);
    const truncatedPhone = nextValue.phoneValue.slice(0, maxLen);

    setCodeValue(resolvedCode);
    setPhoneValue(truncatedPhone);
    lastCommittedValueRef.current = value;
  }, [defaultCodeValue, value, getCachedOrSavedCode]);

  // Non-blocking background IP resolution on first visit
  useEffect(() => {
    if (isLoading) return;
    if (hasFetchedIpRef.current) return;
    if (userInteractedRef.current) return;

    // If user already has a saved value from backend, use it — no IP check needed
    const hasSavedValue =
      value &&
      ((isMarriagePhoneFieldValue(value) &&
        (value.countryCode || value.phoneNumber)) ||
        (typeof value === "string" && value.trim().length > 0));

    if (hasSavedValue) {
      hasFetchedIpRef.current = true;
      if (typeof window !== "undefined") {
        localStorage.setItem("hasCheckedGeoIPPhone", "true");
        if (isMarriagePhoneFieldValue(value) && value.countryCode) {
          const code = value.countryCode.startsWith("+")
            ? value.countryCode
            : `+${value.countryCode}`;
          localStorage.setItem("geoIPPhoneCode", code);
          setCodeValue(code);
        }
      }
      return;
    }

    // Check if we already fetched IP in a previous session or determined code
    if (typeof window !== "undefined") {
      const cachedCode = localStorage.getItem("geoIPPhoneCode");
      const alreadyChecked = localStorage.getItem("hasCheckedGeoIPPhone");

      if (cachedCode || alreadyChecked === "true") {
        hasFetchedIpRef.current = true;
        if (cachedCode) {
          setCodeValue(cachedCode);
        }
        return;
      }
    }

    // First time ever — fetch country code from IP with strict 1.5s timeout
    hasFetchedIpRef.current = true;

    const applyCode = (code: string) => {
      if (userInteractedRef.current) return;
      const ipCode = code.startsWith("+") ? code : `+${code}`;
      setCodeValue(ipCode);
      if (typeof window !== "undefined") {
        localStorage.setItem("geoIPPhoneCode", ipCode);
        localStorage.setItem("hasCheckedGeoIPPhone", "true");
      }
    };

    const applyFallback = () => {
      if (userInteractedRef.current) return;
      const fallbackCode = "+44";
      setCodeValue(fallbackCode);
      if (typeof window !== "undefined") {
        localStorage.setItem("geoIPPhoneCode", fallbackCode);
        localStorage.setItem("hasCheckedGeoIPPhone", "true");
      }
    };

    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 1500);

    fetch("https://ipapi.co/json/", { signal: controller.signal })
      .then((res) => res.json())
      .then((data) => {
        clearTimeout(timeoutId);
        if (data && data.country_calling_code) {
          applyCode(data.country_calling_code);
        } else {
          throw new Error("No calling code in response");
        }
      })
      .catch(() => {
        clearTimeout(timeoutId);
        const secondaryController = new AbortController();
        const secondaryTimeoutId = setTimeout(
          () => secondaryController.abort(),
          1500,
        );

        fetch("https://ipwho.is/", { signal: secondaryController.signal })
          .then((res) => res.json())
          .then((data) => {
            clearTimeout(secondaryTimeoutId);
            if (data && data.calling_code) {
              applyCode(data.calling_code);
            } else {
              applyFallback();
            }
          })
          .catch(() => {
            clearTimeout(secondaryTimeoutId);
            applyFallback();
          });
      });

    return () => {
      clearTimeout(timeoutId);
    };
  }, [isLoading, value]);

  const updateStoredValue = (nextCodeValue: string, nextPhoneValue: string) => {
    const draftValue = writePhoneValue(nextCodeValue, nextPhoneValue);
    const nextPhoneState = getNormalizedPhoneValue(
      nextCodeValue,
      nextPhoneValue,
    );
    const nextValue =
      draftValue === null
        ? null
        : nextPhoneState.isValid
          ? toStoredPhoneValue(nextCodeValue, nextPhoneValue)
          : null;

    lastCommittedValueRef.current = nextValue;
    setAnswerValue(question, questionIndex, nextValue);
  };

  const handleSelectCountryCode = (selectedCode: string) => {
    userInteractedRef.current = true;
    if (typeof window !== "undefined") {
      localStorage.setItem("geoIPPhoneCode", selectedCode);
      localStorage.setItem("hasCheckedGeoIPPhone", "true");
    }
    const maxLen = getMaxLengthForCountry(selectedCode);
    const truncatedPhone = phoneValue.slice(0, maxLen);

    setCodeValue(selectedCode);
    setPhoneValue(truncatedPhone);
    updateStoredValue(selectedCode, truncatedPhone);
    setIsOpen(false);
    setSearchQuery("");
  };

  return (
    <div
      ref={containerRef}
      data-question-answered={isAnswered ? "true" : "false"}
      data-question-type={question.type}
      className={[
        "relative flex w-full flex-col gap-2 transition-opacity duration-200",
        disabled ? "pointer-events-none opacity-30" : "",
      ].join(" ")}
    >
      <QuestionTitle question={question} />
      <div
        dir="ltr"
        className={[
          "flex h-[54px] w-full items-center rounded-[16px] border bg-white text-[#181818] focus-within:border-[#6F6F6F] focus-within:ring-1 focus-within:ring-[#6F6F6F] transition-all",
          showInvalidState
            ? "border-[#F2465F] ring-1 ring-[#F2465F]"
            : "border-[#D0D5DD] hover:border-[#98A2B3]",
        ].join(" ")}
      >
        <div className="flex shrink-0 items-center pl-2.5 pr-2">
          <button
            type="button"
            disabled={disabled}
            onClick={() => setIsOpen(!isOpen)}
            className="flex items-center gap-1.5 h-full px-2 rounded-lg hover:bg-gray-50 active:bg-gray-100 transition-colors cursor-pointer outline-none border-0 text-[15px] font-medium leading-none text-[#181818] tabular-nums"
          >
            <span>{activeFlag}</span>
            <span>{codeValue || defaultCodeValue}</span>
            <svg
              width="10"
              height="6"
              viewBox="0 0 10 6"
              fill="none"
              className={[
                "shrink-0 transition-transform duration-200 text-[#344054]",
                isOpen ? "rotate-180" : "",
              ].join(" ")}
            >
              <path
                d="M1 1L5 5L9 1"
                stroke="currentColor"
                strokeWidth="1.5"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
            </svg>
          </button>
          <span
            aria-hidden="true"
            className="h-5 w-px bg-[#181818]/35 ml-1"
          />
        </div>
        <span className="flex min-w-0 flex-1 items-center pr-4">
              <input
                type="tel"
                inputMode="tel"
                disabled={disabled}
                placeholder={question.extras.placeHolder?.replace(
                  /^\+\d+\s*/,
                  "",
                )}
                value={phoneValue}
                maxLength={getMaxLengthForCountry(codeValue)}
                onChange={(event) => {
                  userInteractedRef.current = true;
                  if (typeof window !== "undefined") {
                    localStorage.setItem("geoIPPhoneCode", codeValue);
                    localStorage.setItem("hasCheckedGeoIPPhone", "true");
                  }
                  const nextPhoneValue = sanitizePhoneNumber(
                    event.target.value,
                  );
                  const maxLen = getMaxLengthForCountry(codeValue);
                  const truncatedPhone = nextPhoneValue.slice(0, maxLen);

                  setPhoneValue(truncatedPhone);
                  updateStoredValue(codeValue, truncatedPhone);
                }}
                dir="ltr"
                className="h-full w-full border-0 bg-transparent p-0 text-left text-[15px] font-medium leading-none text-[#181818] tabular-nums outline-none placeholder:text-[#98A2B3]"
              />
            </span>
      </div>
      {showInvalidState ? (
        <span className="block group-10 font-semibold text-[#F2465F]">
          Enter a valid phone number with country code.
        </span>
      ) : null}

      {/* Dropdown Options Panel */}
      {isOpen && (
        <div className="absolute top-[calc(100%+8px)] left-0 z-50 flex w-full flex-col gap-3.5 rounded-[20px] bg-white p-4.5 shadow-[0_12px_36px_rgba(0,0,0,0.09)] border border-[#EAECF0] animate-in fade-in zoom-in-95 duration-150">
          {/* Search Input Bar */}
          <div className="flex h-[46px] w-full items-center gap-2.5 rounded-[12px] bg-[#EFEFEF] px-3.5 transition-colors focus-within:bg-[#E8E8E8]">
            <svg
              width="18"
              height="18"
              viewBox="0 0 18 18"
              fill="none"
              className="shrink-0 text-[#667085]"
            >
              <path
                d="M8.25 14.25C11.5637 14.25 14.25 11.5637 14.25 8.25C14.25 4.93629 11.5637 2.25 8.25 2.25C4.93629 2.25 2.25 4.93629 2.25 8.25C2.25 11.5637 4.93629 14.25 8.25 14.25Z"
                stroke="#667085"
                strokeWidth="1.75"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
              <path
                d="M15.75 15.75L12.5 12.5"
                stroke="#667085"
                strokeWidth="1.75"
                strokeLinecap="round"
                strokeLinejoin="round"
              />
            </svg>
            <input
              ref={searchInputRef}
              type="text"
              value={searchQuery}
              onChange={(e) => setSearchQuery(e.target.value)}
              placeholder={
                locale === "fa"
                  ? "جستجوی کشور یا پیش‌شماره..."
                  : "Search country or dial code..."
              }
              className="flex-1 bg-transparent text-[14px] font-medium text-[#181818] outline-none placeholder:text-[#98A2B3]"
            />
            {searchQuery ? (
              <button
                type="button"
                onClick={() => setSearchQuery("")}
                className="text-[#667085] hover:text-[#181818] text-xs font-semibold"
              >
                ✕
              </button>
            ) : null}
          </div>

          {/* Country List */}
          <div
            ref={listRef}
            className="flex max-h-[220px] flex-col gap-3.5 overflow-y-auto overscroll-contain pr-1"
          >
            {filteredCountries.length > 0 ? (
              filteredCountries.map((c) => {
                return (
                  <button
                    key={`${c.name}-${c.code}`}
                    type="button"
                    onClick={() => handleSelectCountryCode(c.code)}
                    className="flex w-full items-center justify-between text-start cursor-pointer group/opt py-0.5"
                  >
                    <div className="flex items-center gap-3">
                      <span className="text-[20px]">{c.flag}</span>
                      <span className="text-[15px] font-bold text-[#181818]">
                        {c.name}
                      </span>
                    </div>
                    <span className="text-[15px] font-semibold text-[#667085] tabular-nums">
                      {c.code}
                    </span>
                  </button>
                );
              })
            ) : (
              <span className="py-2 text-[13px] text-[#667085]">
                {locale === "fa" ? "موردی یافت نشد" : "No options found"}
              </span>
            )}
          </div>
        </div>
      )}
    </div>
  );
}

export default QuestionPhone;
