"use client";
import Image from "next/image";
import { useRouter } from "next/navigation";
import { useEffect, useMemo, useState } from "react";
import { useHabibWebReady } from "@/hooks/use-habib-web-ready";
import AdvisorActionsCard from "@/components/Componentes/advisor-actions-card";
import MarriageAdvisorsOverlay, {
useMarriageAdvisorsOverlay,
} from "@/components/Componentes/marriage-advisors-overlay";
import ErrorToast from "@/components/Componentes/error-toast";
import MatchProfileOverlay, {
useMatchProfileOverlay,
} from "@/components/Componentes/match-profile-overlay";
import CallResultSheet from "@/components/Componentes/call-result-sheet";
import DismissReasonSheet from "@/components/Componentes/dismiss-reason-sheet";
import FemaleConsentSheet from "@/components/Componentes/female-consent-sheet";
import FemaleOutcomeSheet from "@/components/Componentes/female-outcome-sheet";
import { LoadingThreeDot } from "@/components/Componentes/loading-three-dot";
import OutcomeSelectionSheet from "@/components/Componentes/outcome-selection-sheet";
import { PageBackground } from "@/components/Componentes/page-background";
import PageHeader from "@/components/Componentes/page-header";
import SubscriptionRequiredSheet from "@/components/Componentes/subscription-required-sheet";
import SwipeButton from "@/components/Componentes/swipe-button";
import type {
MarriageField,
MarriagePhoneFieldValue,
} from "@/hooks/marriage/types";
import { useMarriageContactInfoQuery } from "@/hooks/marriage/use-contact-info";
import {
useSubmitMarriageContactStatusMutation,
useSubmitMarriageOutcomeMutation,
} from "@/hooks/marriage/use-contact-status";
import {
extractHabcoinPaymentUrl,
useHabcoinPaymentMutation,
} from "@/hooks/marriage/use-habcoin-payment";
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main";
import { getSubmitPath } from "@/lib/get-submit-path";
import { copyToClipboard } from "@/lib/webview-actions";
import { Ic } from "@/icons";
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/Avatar Image.png" },
{ id: "advisor-tertiary", src: "/assets/images/Avatar Image.png" },
];
type ContactInfoPhoneItem = {
key: string;
label: string;
phoneNumber: string;
};
function sanitizePhoneNumber(value: MarriageField["value"]) {
if (value === null || value === "") {
return null;
}
if (isMarriagePhoneFieldValue(value)) {
const digits = value.phoneNumber.replace(/\D/g, "");
return digits ? `+${value.countryCode}${digits}` : null;
}
const trimmedValue = String(value).trim();
if (!trimmedValue) {
return null;
}
const digits = trimmedValue.replace(/\D/g, "");
if (!digits) {
return null;
}
return trimmedValue.startsWith("+") ? `+${digits}` : digits;
}
function isMarriagePhoneFieldValue(
value: unknown,
): value is MarriagePhoneFieldValue {
if (!value || typeof value !== "object") {
return false;
}
const phoneValue = value as Partial;
return (
typeof phoneValue.countryCode === "string" &&
typeof phoneValue.phoneNumber === "string"
);
}
function getContactInfoPhoneItems(
contactInfoFields: any[] | null | undefined,
): ContactInfoPhoneItem[] {
if (!contactInfoFields || !Array.isArray(contactInfoFields)) {
return [];
}
// Look for contextual representative info if available
const repNameField = contactInfoFields.find(
(f) =>
typeof f?.key === "string" &&
f.key.toLowerCase().includes("representative_s_full_name") &&
f.value,
);
const repRelationField = contactInfoFields.find(
(f) =>
typeof f?.key === "string" &&
f.key.toLowerCase().includes("relationship_to_representative") &&
f.value,
);
const repName = repNameField?.value ? String(repNameField.value).trim() : "";
const repRelation = repRelationField?.value
? String(repRelationField.value).trim()
: "";
return contactInfoFields
.map((field) => {
if (!field) return null;
// Extract phone number from field properties
let phoneNumber: string | null = null;
if (field.phone_number && typeof field.phone_number === "string") {
phoneNumber = field.phone_number.trim();
} else if (field.raw_number && typeof field.raw_number === "string") {
const countryCode = field.country_code
? String(field.country_code).replace(/\D/g, "")
: "98";
phoneNumber = `+${countryCode} ${field.raw_number.trim()}`;
} else if (field.raw_value && typeof field.raw_value === "object") {
phoneNumber = sanitizePhoneNumber(field.raw_value);
} else if (field.value) {
phoneNumber = sanitizePhoneNumber(field.value);
}
// If this field is not a phone field, skip it
if (
!phoneNumber ||
(field.type &&
field.type !== "phone" &&
!field.phone_number &&
!field.raw_number)
) {
return null;
}
const key = String(field.key || "");
const rawLabel = String(field.label || field.key || "");
let label = rawLabel
.replace(/\s+with\s+Country\s+Code/gi, "")
.replace(/\s+با\s+کد\s+کشور/g, "")
.trim();
// Add representative context to label if applicable
if (key.includes("representative")) {
const contextParts = [repRelation, repName].filter(Boolean);
if (contextParts.length > 0 && !label.includes(contextParts[0])) {
label = `${label} (${contextParts.join(" - ")})`;
}
}
return {
key: key || phoneNumber,
label,
phoneNumber,
};
})
.filter((item): item is ContactInfoPhoneItem => item !== null);
}
function ContactInfoPhoneCard({ item }: { item: ContactInfoPhoneItem }) {
const [isCopied, setIsCopied] = useState(false);
const handleCopy = async () => {
try {
await copyToClipboard(item.phoneNumber, item.label);
setIsCopied(true);
setTimeout(() => {
setIsCopied(false);
}, 1500);
} catch (err) {
console.error("Error copying contact to clipboard", err);
}
};
return (
{item.phoneNumber}
{item.label}
);
}
export default function RequestAcceptedClient() {
const { dictionary: t, locale } = useI18n();
const router = useRouter();
const { isAdvisorOpen, openAdvisors, closeAdvisors } =
useMarriageAdvisorsOverlay();
const { isProfileOpen, openProfile, closeProfile } =
useMatchProfileOverlay();
const [isCallResultSheetOpen, setIsCallResultSheetOpen] = useState(false);
const [isDismissReasonSheetOpen, setIsDismissReasonSheetOpen] =
useState(false);
const [isContactInfoSheetOpen, setIsContactInfoSheetOpen] = useState(false);
const [isSubscriptionSheetOpen, setIsSubscriptionSheetOpen] = useState(false);
const [paymentError, setPaymentError] = useState(null);
const [isInsufficientCoins, setIsInsufficientCoins] = useState(false);
const [isOutcomeSheetOpen, setIsOutcomeSheetOpen] = useState(false);
const [isNoContactConfirmOpen, setIsNoContactConfirmOpen] = useState(false);
const [isContactReceivedConfirmOpen, setIsContactReceivedConfirmOpen] =
useState(false);
const [noContactReportedSuccess, setNoContactReportedSuccess] =
useState(false);
const [hasConfirmedFemaleContact, setHasConfirmedFemaleContact] =
useState(false);
const [showPaymentSuccessToast, setShowPaymentSuccessToast] = useState(false);
const [isOpeningProfile, setIsOpeningProfile] = useState(false);
const {
data: profile,
isLoading,
isFetched,
isFetching,
refetch: refetchProfile,
} = useMarriageProfileQuery();
const isFemaleProfile = profile?.gender === "female";
const contactSharedAtStr = profile?.active_case?.contact_shared_at;
useEffect(() => {
if (!profile || !isFetched || noContactReportedSuccess) {
return;
}
const targetPath = getSubmitPath(profile);
if (targetPath !== "/request-accepted") {
router.replace(localizePath(targetPath, locale));
}
}, [profile, isFetched, router, locale, noContactReportedSuccess]);
// Signal Flutter to lift its loading cover immediately on mount
useHabibWebReady(true);
const handleOpenProfile = async () => {
if (!isFetched || isFetching) {
setIsOpeningProfile(true);
try {
await refetchProfile();
} finally {
setIsOpeningProfile(false);
openProfile();
}
return;
}
openProfile();
};
const isRedirecting = useMemo(() => {
if (!profile) return false;
return getSubmitPath(profile) !== "/request-accepted";
}, [profile]);
const caseId = profile?.active_case?.case_id;
const caseStatus = profile?.active_case?.status;
const isFemaleContactConfirmed =
isFemaleProfile &&
(caseStatus === "contacted" || hasConfirmedFemaleContact);
const recommendedPlanId = profile?.recommended_plan?.id;
const paymentMutation = useHabcoinPaymentMutation();
const outcomeMutation = useSubmitMarriageOutcomeMutation(caseId ?? "");
const contactStatusMutation = useSubmitMarriageContactStatusMutation(
caseId ?? "",
{
onSuccess: (_data, variables) => {
if (variables?.action === "no_contact") {
setNoContactReportedSuccess(true);
} else {
if (!isFemaleProfile) {
router.push(localizePath("/finding-match", locale));
}
}
},
},
);
const [isFetchingContact, setIsFetchingContact] = useState(false);
const isMalePaymentDone =
!isFemaleProfile &&
(caseStatus === "payment_done" || caseStatus === "contacted");
const contactInfoQuery = useMarriageContactInfoQuery(caseId, {
enabled: false,
});
const titleText = isFemaleProfile
? t["Request Approved"]
: caseStatus === "payment_done" || caseStatus === "contacted"
? t["Contact info released"]
: t["Request approved!"];
const primaryActionText = isFemaleProfile
? t["No Contact"] || t["No Contact Received"] || "No Contact"
: t["View profile"];
const secondaryActionText = isFemaleProfile
? t["Contact Received"]
: caseStatus === "payment_done" || caseStatus === "contacted"
? t["Contact"]
: t["Pay and get contact"];
const contactInfoPhoneItems = getContactInfoPhoneItems(
contactInfoQuery.data?.contact_info,
);
const handlePrimaryAction = () => {
if (isFemaleProfile) {
setIsDismissReasonSheetOpen(true);
} else {
openProfile();
}
};
const handleSecondaryAction = async () => {
if (isFemaleProfile) {
setIsContactReceivedConfirmOpen(true);
return;
}
if (caseStatus === "female_accepted" || caseStatus === "payment_pending") {
setIsSubscriptionSheetOpen(true);
return;
}
if (caseStatus === "payment_done" || caseStatus === "contacted") {
if (!caseId) {
return;
}
if (!contactInfoQuery.data) {
try {
setIsFetchingContact(true);
await contactInfoQuery.refetch();
} finally {
setIsFetchingContact(false);
}
}
setIsContactInfoSheetOpen(true);
}
};
const handlePayment = async () => {
const planId = recommendedPlanId ?? 1;
if (paymentMutation.isPending) {
return;
}
try {
setPaymentError(null);
setIsInsufficientCoins(false);
const paymentResponse =
await paymentMutation.mutateAsync(planId);
const paymentUrl = extractHabcoinPaymentUrl(paymentResponse);
if (paymentUrl) {
window.location.assign(paymentUrl);
return;
}
setIsSubscriptionSheetOpen(false);
setShowPaymentSuccessToast(true);
if (caseId) {
await contactInfoQuery.refetch();
setIsContactInfoSheetOpen(true);
}
} catch (err: any) {
console.error("Habcoin payment request failed", err);
const msg =
err?.response?.data?.error ||
err?.response?.data?.detail ||
err?.response?.data?.message ||
err?.message ||
"Payment failed";
if (msg === "Not enough coins" || msg?.includes?.("Not enough coins")) {
setIsInsufficientCoins(true);
setPaymentError(
t["Insufficient coin balance. Please recharge your account."] ||
"Insufficient coin balance. Please recharge your account.",
);
} else {
setPaymentError(msg);
}
}
};
const handleNoContactReport = async () => {
if (!caseId || contactStatusMutation.isPending) return;
await contactStatusMutation.mutateAsync({
action: "no_contact",
custom_note:
"No contact reported by female candidate after decision window",
});
};
const isFinalized =
caseStatus === "finalized" || profile?.status === "matched";
return (
<>
{isCallResultSheetOpen ? (
setIsCallResultSheetOpen(false)}
onOtherReasonsClick={() => setIsDismissReasonSheetOpen(true)}
onSubmit={async (value) => {
if (caseId) {
await contactStatusMutation.mutateAsync({
action: "contacted",
custom_note: value,
});
}
}}
/>
) : null}
{isContactReceivedConfirmOpen ? (
{t["Are you sure contact has been made?"]}
}
buttons={
setIsContactReceivedConfirmOpen(false)}
onSuccess={async () => {
setIsContactReceivedConfirmOpen(false);
setHasConfirmedFemaleContact(true);
if (!caseId) {
return;
}
try {
await contactStatusMutation.mutateAsync({
action: "contacted",
custom_note:
"Contact received confirmed by female candidate",
});
} catch (error) {
// The confirmation screen must advance immediately after a swipe.
// Keep the local state visible while the profile query retries.
console.error("Unable to persist received contact", error);
}
}}
/>
}
onClose={() => setIsContactReceivedConfirmOpen(false)}
closeOnOutside={true}
/>
) : null}
{isDismissReasonSheetOpen ? (
setIsDismissReasonSheetOpen(false)}
onSubmit={async (value) => {
if (caseId) {
await outcomeMutation.mutateAsync({
status: "failure",
custom_note: value,
});
}
}}
/>
) : null}
{isOutcomeSheetOpen ? (
isFemaleProfile ? (
setIsOutcomeSheetOpen(false)}
onSubmit={async (status, reason) => {
if (status === "success") {
// If they confirm they are in the acquaintance/proposal process and nothing is finalized yet:
// No change is made to the profile, we just close the sheet.
setIsOutcomeSheetOpen(false);
} else {
// If they cancel:
if (caseId) {
await outcomeMutation.mutateAsync({
status: "failure",
custom_note: reason,
});
}
}
}}
/>
) : (
setIsOutcomeSheetOpen(false)}
onSubmit={async (status) => {
if (status === "success") {
if (caseId) {
await outcomeMutation.mutateAsync({
status: "success",
});
}
} else {
setIsDismissReasonSheetOpen(true);
}
}}
/>
)
) : null}
{isContactInfoSheetOpen ? (
{contactInfoPhoneItems.map((item) => (
))}
) : (
{t["Contact information is not available yet."]}
)
}
onClose={() => setIsContactInfoSheetOpen(false)}
/>
) : null}
{isSubscriptionSheetOpen ? (
{
setIsSubscriptionSheetOpen(false);
setPaymentError(null);
setIsInsufficientCoins(false);
}}
onPayment={handlePayment}
isPaymentPending={!recommendedPlanId || paymentMutation.isPending}
errorMessage={paymentError}
showBuyCoins={isInsufficientCoins}
/>
) : null}
{isNoContactConfirmOpen ? (
{
t[
"No contact has been made with you in any way or by any party."
]
}
}
buttons={
}
onClose={() => setIsNoContactConfirmOpen(false)}
closeOnOutside={true}
/>
) : null}
{isFinalized ? (
🎉
{t["Congratulations! 🎉"]}
{
t[
"Wishing you a lifetime of love, joy, and happiness. Your profile has been successfully closed."
]
}
) : (
<>
{/* Illustration */}
{titleText}
{caseStatus === "contacted" ||
isFemaleContactConfirmed ||
(isFemaleProfile && contactStatusMutation.isPending) ? (
{isFemaleProfile && contactStatusMutation.isPending ? (
) : (
{isFemaleProfile
? t[
"Thank you for your feedback. To complete the process, please submit the final outcome of this introduction/contact so that the final status can be determined. If the final status is not yet determined, you can stay in this state until it is finalized."
]
: t[
"Thank you for giving us feedback, we would be very happy if you also let us know the final result."
]}
)}
) : (
{noContactReportedSuccess
? t[
"Thank you for your feedback. Our support team will investigate the matter and notify you of the result. Please wait patiently during the review; our support will contact you."
]
: isFemaleProfile
? t[
"The selected candidate will contact your family shortly."
]
: t[
"You can now view their family's contact details and arrange further steps."
]}
)}
<>
{caseStatus === "contacted" ||
isFemaleContactConfirmed ||
(isFemaleProfile && contactStatusMutation.isPending) ? (
{isFemaleProfile &&
contactStatusMutation.isPending ? null : isFemaleProfile ? (
) : (
<>
>
)}
) : (
{isFemaleProfile ? (
) : (
)}
)}
{caseStatus !== "contacted" &&
!isFemaleContactConfirmed &&
!noContactReportedSuccess &&
!(isFemaleProfile && contactStatusMutation.isPending) ? (
{
t[
"Please note that after a match introduction is made, you and your family have a maximum of 48 hours to make at least an initial contact with the introduced person or their family and express your willingness to begin the acquaintance process. Failure to establish communication or provide any update within this timeframe will result in the removal of this introduction, and according to the platform’s policies, your account and subscription will be permanently suspended"
]
}
) : null}
>
>
)}
{showPaymentSuccessToast && (
setShowPaymentSuccessToast(false)}
/>
)}
>
);
}