"use client"; import Image from "next/image"; import Link from "next/link"; import { useRouter } from "next/navigation"; import { useEffect, useMemo, useState } from "react"; import { useHabibWebReady } from "@/hooks/use-habib-web-ready"; import { FaLock } from "react-icons/fa6"; import AdvisorActionsCard from "@/components/Componentes/advisor-actions-card"; import MarriageAdvisorsOverlay, { useMarriageAdvisorsOverlay, } from "@/components/Componentes/marriage-advisors-overlay"; import MatchProfileOverlay, { useMatchProfileOverlay, } from "@/components/Componentes/match-profile-overlay"; import PageHeader from "@/components/Componentes/page-header"; import { PageBackground } from "@/components/Componentes/page-background"; import InformationSheet from "@/components/Componentes/information-sheet"; import { LoadingSkeleton } from "@/components/Componentes/loading-skeleton"; import { LoadingThreeDot } from "@/components/Componentes/loading-three-dot"; import { DiscountWidget } from "@/components/Componentes/discount-widget"; import type { CheckDiscountResult } from "@/hooks/marriage/use-validate-discount"; import { useHabcoinPaymentMutation } from "@/hooks/marriage/use-habcoin-payment"; import { useHabcoinInventoryQuery } from "@/hooks/marriage/use-habcoin-inventory"; import { useRespondToMarriageCaseMutation } from "@/hooks/marriage/use-case-respond"; import type { MarriageField, MarriageFieldValue, MarriageMatchSummary, MarriagePhoneFieldValue, } from "@/hooks/marriage/types"; import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main"; import { useViewPaddings } from "@/hooks/use-view-paddings"; import { getSubmitPath } from "@/lib/get-submit-path"; import { buyHabibCoinPackages, isInFlutterWebView, } from "@/lib/webview-actions"; import { localizePath } from "@/translations/config"; import { useI18n } from "@/translations/provider"; const advisorAvatars = [ { id: "advisor-primary", src: "/assets/images/Avatar Image.png" }, { id: "advisor-secondary", src: "/assets/images/Ellipse 370.png" }, { id: "advisor-tertiary", src: "/assets/images/Avatar Image.png" }, ]; const fieldCandidateMatchers = { name: [ "name", "full_name", "fullname", "first_name", "last_name", "display_name", "q1_full_name", ], age: ["age", "date_of_birth", "birth_date", "dob", "birth_year"], residence: [ "current_residence", "residence", "current_country_city", "location", "current_city", "residence_city", "city", "country", "birth_city", "birthplace", ], educationLevel: [ "highest_level_of_education", "highest_education_level", "education_level", "education", ], fieldOfStudy: [ "field_of_study", "study_field", "study", ], job: [ "job_title", "job_title_and_description", "job", "occupation", "profession", "career", "employment_status", "work", ], hobbies: [ "your_hobbies_and_main_interests", "hobbies_and_interests", "hobbies", "interests", "your_personality_traits", "personality_traits", ], maritalStatus: [ "current_marital_status", "marital_status", "maritalstatus", "relationship_status", ], cityPreference: [ "willingness_to_relocate", "city_preference", "citypreference", "preferred_city", "preferred_location", "future_residence", "residence_preference_after_marriage", ], } as const; type DisplayField = { id: string; label: string; value: string; }; function normalizeFieldName(value: string) { return value .toLowerCase() .replace(/^q\d+[_-]?/, "") .replace(/[^a-z0-9]/g, ""); } function matchesCandidate( field: MarriageField, candidates: readonly string[], ): boolean { const rawKey = (field.key || "").toLowerCase(); const keyParts = rawKey.split("."); const suffix = keyParts[keyParts.length - 1]; const normalizedKey = rawKey.replace(/[^a-z0-9]/g, ""); const normalizedSuffix = suffix.replace(/[^a-z0-9]/g, ""); for (const c of candidates) { const normC = c.toLowerCase().replace(/[^a-z0-9]/g, ""); if ( normalizedSuffix === normC || normalizedKey.endsWith(normC) || rawKey === c.toLowerCase() || suffix === c.toLowerCase() ) { return true; } } return false; } function calculateAgeFromDob(dobString: string): number | null { if (!dobString) return null; const match = dobString.match(/^(\d{4})-(\d{1,2})-(\d{1,2})/); if (!match) return null; const year = parseInt(match[1], 10); const month = parseInt(match[2], 10) - 1; const day = parseInt(match[3], 10); const birthDate = new Date(year, month, day); if (isNaN(birthDate.getTime())) return null; const today = new Date(); let age = today.getFullYear() - birthDate.getFullYear(); const m = today.getMonth() - birthDate.getMonth(); if (m < 0 || (m === 0 && today.getDate() < birthDate.getDate())) { age--; } return age > 0 && age < 120 ? age : null; } import { formatFieldLabel, formatFieldValue, formatOptionValue, isMarriagePhoneFieldValue, titleFromKey, } from "@/lib/marriage-field-formatter"; function toDisplayField( field: MarriageField, dictionary?: Record, ): DisplayField | null { const value = formatOptionValue(field.value, dictionary); if (!value) { return null; } return { id: field.key || field.label || value, label: formatFieldLabel(field, dictionary), value, }; } function pickField( fields: MarriageField[], candidates: readonly string[], usedIndexes: Set, dictionary?: Record, ): DisplayField | null { for (const [fieldIndex, field] of fields.entries()) { if (usedIndexes.has(fieldIndex)) { continue; } if (matchesCandidate(field, candidates)) { const displayField = toDisplayField(field, dictionary); if (displayField) { usedIndexes.add(fieldIndex); return displayField; } } } return null; } function useMatchSummaryDisplay( matchSummary: MarriageMatchSummary | null, t?: Record, ) { return useMemo(() => { const fields = matchSummary?.public_info ?? []; const usedIndexes = new Set(); // 1. Name: Combine first_name and last_name if available, or find general name let displayName: string | null = null; const firstNameIdx = fields.findIndex( (f) => f.key === "personal_identity.first_name" || f.key?.endsWith(".first_name"), ); const lastNameIdx = fields.findIndex( (f) => f.key === "personal_identity.last_name" || f.key?.endsWith(".last_name"), ); if (firstNameIdx !== -1 && fields[firstNameIdx].value) { usedIndexes.add(firstNameIdx); const firstName = formatFieldValue(fields[firstNameIdx].value); if (lastNameIdx !== -1 && fields[lastNameIdx].value) { usedIndexes.add(lastNameIdx); const lastName = formatFieldValue(fields[lastNameIdx].value); displayName = `${firstName} ${lastName}`.trim(); } else { displayName = firstName; } } else { const nameField = pickField( fields, fieldCandidateMatchers.name, usedIndexes, t, ); if (nameField) { displayName = nameField.value; } } if (!displayName && matchSummary?.id) { displayName = `Profile #${matchSummary.id}`; } // 2. Age (extract and calculate from date_of_birth if available) const dobIdx = fields.findIndex( (f) => f.key === "personal_identity.date_of_birth" || f.key?.endsWith(".date_of_birth") || f.key?.toLowerCase().includes("date_of_birth") || f.key?.toLowerCase().includes("birth_date"), ); let age: DisplayField | null = null; if (dobIdx !== -1 && fields[dobIdx].value) { usedIndexes.add(dobIdx); const calculatedAge = calculateAgeFromDob(String(fields[dobIdx].value)); if (calculatedAge) { age = { id: fields[dobIdx].key, label: t ? t["Age"] || "Age" : "Age", value: `${calculatedAge}`, }; } } if (!age) { age = pickField(fields, fieldCandidateMatchers.age, usedIndexes, t); } // 3. Country / City / Residence const residence = pickField( fields, fieldCandidateMatchers.residence, usedIndexes, t, ); // 4. Highest level of education const educationLevel = pickField( fields, fieldCandidateMatchers.educationLevel, usedIndexes, t, ); // 5. Field of study const fieldOfStudy = pickField( fields, fieldCandidateMatchers.fieldOfStudy, usedIndexes, t, ); // 6. Job / Occupation const job = pickField( fields, fieldCandidateMatchers.job, usedIndexes, t, ); // 7. Hobbies & Interests const hobbies = pickField( fields, fieldCandidateMatchers.hobbies, usedIndexes, t, ); // 8. Marital Status const maritalStatus = pickField( fields, fieldCandidateMatchers.maritalStatus, usedIndexes, t, ); // 9. City Preference / Relocation const cityPreference = pickField( fields, fieldCandidateMatchers.cityPreference, usedIndexes, t, ); const extraFields = fields .filter((_, index) => !usedIndexes.has(index)) .map((f) => toDisplayField(f, t)) .filter((field): field is DisplayField => Boolean(field)) .slice(0, 4); if (typeof window !== "undefined") { console.log( "🔍 [useMatchSummaryDisplay] computed 8 fields output:", { displayName, age, residence, educationLevel, fieldOfStudy, job, hobbies, maritalStatus, cityPreference, extraFieldsCount: extraFields.length, }, ); } return { age, residence, educationLevel, fieldOfStudy, job, hobbies, maritalStatus, cityPreference, name: displayName, extraFields, }; }, [matchSummary, t]); } function FieldLine({ field }: { field: DisplayField }) { return (

{field.label}: {field.value}

); } export default function NewMatchClient() { const router = useRouter(); const { dictionary: t, locale } = useI18n(); const { top, bottom } = useViewPaddings(); const { isAdvisorOpen, openAdvisors, closeAdvisors } = useMarriageAdvisorsOverlay(); const { isProfileOpen, openProfile, closeProfile } = useMatchProfileOverlay(); const { data: profile, isError, isLoading } = useMarriageProfileQuery(); const [isPaymentSheetOpen, setIsPaymentSheetOpen] = useState(false); const [paymentError, setPaymentError] = useState(null); const [isInsufficientCoins, setIsInsufficientCoins] = useState(false); const [appliedDiscount, setAppliedDiscount] = useState(null); const { data: inventory, isLoading: isInventoryLoading } = useHabcoinInventoryQuery({ enabled: isPaymentSheetOpen, }); const planPrice = Number(profile?.recommended_plan?.price) || 50; const finalPrice = appliedDiscount?.valid ? appliedDiscount.discountedPrice : planPrice; const coinBalance = inventory?.coin_balance ?? 0; const hasEnoughCoins = !isInsufficientCoins && (inventory === undefined ? true : coinBalance >= finalPrice); const paymentMutation = useHabcoinPaymentMutation(); const caseId = profile?.active_case?.case_id; const respondMutation = useRespondToMarriageCaseMutation(caseId ?? "", { onSuccess: () => { router.replace(localizePath("/finding-match", locale)); }, }); const handlePayment = async () => { const recommendedPlanId = profile?.recommended_plan?.id; if (!recommendedPlanId) return; try { setPaymentError(null); setIsInsufficientCoins(false); await paymentMutation.mutateAsync({ objectId: recommendedPlanId, discountCode: appliedDiscount?.valid ? appliedDiscount.code : undefined, }); setIsPaymentSheetOpen(false); openProfile(); } catch (err: any) { console.error("Payment failed", err); const msg = err?.response?.data?.error || err?.message || "Payment failed"; const modalT = (t as any).paymentModal || {}; if (msg === "Not enough coins") { setIsInsufficientCoins(true); setPaymentError( modalT.insufficientCoins || "Insufficient coin balance. Please recharge your account.", ); } else { setPaymentError(msg); } } }; const handleDecline = async () => { if (!caseId) return; try { await respondMutation.mutateAsync({ action: "reject" }); } catch (err) { console.error("Decline failed", err); } }; useEffect(() => { console.log("🔍 [NewMatchClient] Current React Query Profile State:", { isLoading, isError, hasProfile: Boolean(profile), profileId: profile?.id, status: profile?.status, active_case: profile?.active_case, hasMatchSummary: Boolean(profile?.match_summary), matchSummaryId: profile?.match_summary?.id, publicInfoCount: profile?.match_summary?.public_info?.length, fullProfileObject: profile, }); if (!profile) { return; } const targetPath = getSubmitPath(profile); if (targetPath !== "/new-match") { router.replace(localizePath(targetPath, locale)); } }, [profile, locale, router, isLoading, isError]); // Signal Flutter to lift its loading cover once the profile is available. useHabibWebReady(!!profile && !isLoading); const isRedirecting = useMemo(() => { if (!profile) return false; return getSubmitPath(profile) !== "/new-match"; }, [profile]); const matchSummary = profile?.match_summary ?? null; const matchDisplay = useMatchSummaryDisplay(matchSummary, t); if (typeof window !== "undefined") { if (!matchSummary && !isLoading && !isRedirecting) { console.warn( "⚠️ [NewMatchClient] match_summary is null on profile! 'No match summary is available yet.' will be displayed. Profile:", profile, ); } else if (matchSummary) { console.log( "✅ [NewMatchClient] Rendering match summary card with:", matchDisplay, ); } } if (isLoading || isRedirecting) { return ( <>
{/* Header Section Skeleton */}
{/* Match Card Skeleton */}
{/* Name line */} {/* Subtitle / Details lines */}
{/* Button skeleton */}
{/* Advisor Card Skeleton */}
); } const pairedEduFields = [ matchDisplay.educationLevel, matchDisplay.fieldOfStudy, ].filter((field): field is DisplayField => Boolean(field)); const isFemaleProfile = profile?.gender === "female"; const matchHeadingTitle = isFemaleProfile ? t["New Marriage Proposal"] : t["YOU HAVE A NEW MATCH!"]; const matchHeadingDescription = isFemaleProfile ? t[ "A suitable match has been found for you. If approved, your profile will be evaluated to proceed with the introduction process." ] : t[ "If you proceed, we will notify the other party, and upon their approval, you can view each other's contact information." ]; const isMale = profile?.gender === "male"; const hasActiveSub = !!profile?.active_subscription && profile.active_subscription.is_active !== false && profile.active_subscription.is_valid !== false; const isMatchAvailable = !!profile?.match_summary; return ( <>

{matchHeadingTitle}

{matchHeadingDescription}

{isLoading ? (
{/* Name line */} {/* Subtitle / Details lines */}
{/* Button skeleton */}
) : (
{isError ? (

Unable to load match summary.

) : matchSummary ? ( <>

{matchDisplay.name}

{matchDisplay.age ? ( ) : null} {matchDisplay.residence ? ( ) : null} {pairedEduFields.length ? (

{pairedEduFields.map((field, index) => ( {index > 0 ? ( | ) : null} {field.label}:{" "} {field.value} ))}

) : null} {matchDisplay.job ? ( ) : null} {matchDisplay.hobbies ? ( ) : null} {matchDisplay.maritalStatus ? ( ) : null} {matchDisplay.cityPreference ? ( ) : null}
) : (

No match summary is available yet.

)}
)}
{profile?.can_edit_profile === false && (
)} {isPaymentSheetOpen && isInventoryLoading && ( 0 ? `${14 + bottom}px` : undefined, }} onClose={() => { setIsPaymentSheetOpen(false); setPaymentError(null); setIsInsufficientCoins(false); }} /> )} {isPaymentSheetOpen && !isInventoryLoading && ( 0 ? `${14 + bottom}px` : undefined, }} icon="coin" title={ !hasEnoughCoins ? ( {t["You do not have enough Habib Coins"] || "You do not have enough Habib Coins"} ) : ( t["Verification & Subscription Activation"] || "Verification & Subscription Activation" ) } description={ !hasEnoughCoins ? (

{t[ "Please add more Habib Coins to your balance (or use discount code), so that you can use them to access this content" ] || "Please add more Habib Coins to your balance (or use discount code), so that you can use them to access this content"}

) : (

{t[ "This payment acts as a verification process for your account and activates a 3-month subscription to receive new case introductions. The cost of this subscription is 50 Habib Coins." ] || "This payment acts as a verification process for your account and activates a 3-month subscription to receive new case introductions. The cost of this subscription is 50 Habib Coins."}

{t["Valid for 3 months"] || "Valid for 3 months"} {finalPrice} {t["Habib Coins"] || "Habib Coins"}

{t[ "Please note that there is no guarantee for a specific number of cases, and the volume of incoming cases is solely subject to profile compatibility with other users." ] || "Please note that there is no guarantee for a specific number of cases, and the volume of incoming cases is solely subject to profile compatibility with other users."}

) } onClose={() => { setIsPaymentSheetOpen(false); setPaymentError(null); setIsInsufficientCoins(false); setAppliedDiscount(null); }} buttons={
{profile?.recommended_plan?.id ? ( setAppliedDiscount(res)} onDiscountCleared={() => setAppliedDiscount(null)} /> ) : null} {paymentError && (
{paymentError}
)}
{!hasEnoughCoins ? ( ) : ( )}
} /> )} ); }