"use client"; import { useEffect, useMemo, useState } from "react"; import { useRouter, useSearchParams } from "next/navigation"; import { Server, CheckCircle, Clock, TriangleAlert, MapPin } from "lucide-react"; import { useAccountInternetCatalog } from "@/features/services/hooks"; import { useActiveSubscriptions } from "@/features/subscriptions/hooks/useSubscriptions"; import type { InternetPlanCatalogItem, InternetInstallationCatalogItem, } from "@customer-portal/domain/services"; import { Skeleton } from "@/components/atoms/loading-skeleton"; import { AsyncBlock } from "@/components/molecules/AsyncBlock/AsyncBlock"; import { AlertBanner } from "@/components/molecules/AlertBanner/AlertBanner"; import { Button } from "@/components/atoms/button"; import { ServicesBackLink } from "@/features/services/components/base/ServicesBackLink"; import { useServicesBasePath } from "@/features/services/hooks/useServicesBasePath"; import { InternetImportantNotes } from "@/features/services/components/internet/InternetImportantNotes"; import { InternetOfferingCard, type TierInfo, } from "@/features/services/components/internet/InternetOfferingCard"; import { PublicInternetPlansContent } from "@/features/services/views/PublicInternetPlans"; import { PlanComparisonGuide } from "@/features/services/components/internet/PlanComparisonGuide"; import { useInternetEligibility, useRequestInternetEligibilityCheck, } from "@/features/services/hooks"; import { useAuthSession } from "@/features/auth/services/auth.store"; import { cn } from "@/lib/utils"; type AutoRequestStatus = "idle" | "submitting" | "submitted" | "failed" | "missing_address"; // Offering configuration for display interface OfferingConfig { offeringType: string; title: string; speedBadge: string; description: string; iconType: "home" | "apartment"; isPremium: boolean; displayOrder: number; isAlternative?: boolean; alternativeNote?: string; } const OFFERING_CONFIGS: Record> = { "Home 10G": { title: "Home 10Gbps", speedBadge: "10 Gbps", description: "Ultra-fast fiber with the highest speeds available in Japan.", iconType: "home", isPremium: true, displayOrder: 1, }, "Home 1G": { title: "Home 1Gbps", speedBadge: "1 Gbps", description: "High-speed fiber. The most popular choice for home internet.", iconType: "home", isPremium: false, displayOrder: 2, }, "Apartment 1G": { title: "Apartment 1Gbps", speedBadge: "1 Gbps", description: "High-speed fiber-to-the-unit for mansions and apartment buildings.", iconType: "apartment", isPremium: false, displayOrder: 1, }, "Apartment 100M": { title: "Apartment 100Mbps", speedBadge: "100 Mbps", description: "Standard speed via VDSL or LAN for apartment buildings.", iconType: "apartment", isPremium: false, displayOrder: 2, }, }; function getTierInfo(plans: InternetPlanCatalogItem[], offeringType: string): TierInfo[] { const filtered = plans.filter(p => p.internetOfferingType === offeringType); const tierOrder: ("Silver" | "Gold" | "Platinum")[] = ["Silver", "Gold", "Platinum"]; const tierDescriptions: Record< string, { description: string; features: string[]; pricingNote?: string } > = { Silver: { description: "Essential setup—bring your own router", features: ["NTT modem + ISP connection", "IPoE or PPPoE protocols", "Self-configuration"], }, Gold: { description: "All-inclusive with router rental", features: [ "Everything in Silver", "WiFi router included", "Auto-configured", "Range extender option", ], }, Platinum: { description: "Tailored setup for larger homes", features: [ "Netgear INSIGHT mesh routers", "Cloud-managed WiFi", "Remote support", "Custom setup", ], pricingNote: "+ equipment fees", }, }; const result: TierInfo[] = []; for (const tier of tierOrder) { const plan = filtered.find(p => p.internetPlanTier?.toLowerCase() === tier.toLowerCase()); if (!plan) continue; const config = tierDescriptions[tier]; result.push({ tier, planSku: plan.sku, monthlyPrice: plan.monthlyPrice ?? 0, description: config.description, features: config.features, recommended: tier === "Gold", pricingNote: config.pricingNote, }); } return result; } function getSetupFee(installations: InternetInstallationCatalogItem[]): number { const basic = installations.find(i => i.sku?.toLowerCase().includes("basic")); return basic?.oneTimePrice ?? 22800; } function getAvailableOfferings( eligibility: string | null, plans: InternetPlanCatalogItem[] ): OfferingConfig[] { if (!eligibility) return []; const results: OfferingConfig[] = []; const eligibilityLower = eligibility.toLowerCase(); if (eligibilityLower.includes("home 10g")) { const config10g = OFFERING_CONFIGS["Home 10G"]; const config1g = OFFERING_CONFIGS["Home 1G"]; if (config10g && plans.some(p => p.internetOfferingType === "Home 10G")) { results.push({ offeringType: "Home 10G", ...config10g }); } if (config1g && plans.some(p => p.internetOfferingType === "Home 1G")) { results.push({ offeringType: "Home 1G", ...config1g, isAlternative: true, alternativeNote: "Lower monthly cost option", }); } } else if (eligibilityLower.includes("home 1g")) { const config = OFFERING_CONFIGS["Home 1G"]; if (config && plans.some(p => p.internetOfferingType === "Home 1G")) { results.push({ offeringType: "Home 1G", ...config }); } } else if (eligibilityLower.includes("apartment 1g")) { const config = OFFERING_CONFIGS["Apartment 1G"]; if (config && plans.some(p => p.internetOfferingType === "Apartment 1G")) { results.push({ offeringType: "Apartment 1G", ...config }); } } else if (eligibilityLower.includes("apartment 100m")) { const config = OFFERING_CONFIGS["Apartment 100M"]; if (config && plans.some(p => p.internetOfferingType === "Apartment 100M")) { results.push({ offeringType: "Apartment 100M", ...config }); } } return results.sort((a, b) => a.displayOrder - b.displayOrder); } function formatEligibilityDisplay(eligibility: string): { residenceType: "home" | "apartment"; speed: string; label: string; description: string; } { const lower = eligibility.toLowerCase(); if (lower.includes("home 10g")) { return { residenceType: "home", speed: "10 Gbps", label: "Standalone House (10Gbps available)", description: "Your address supports our fastest 10Gbps service. You can also choose 1Gbps for lower monthly cost.", }; } if (lower.includes("home 1g")) { return { residenceType: "home", speed: "1 Gbps", label: "Standalone House (1Gbps)", description: "Your address supports high-speed 1Gbps fiber connection.", }; } if (lower.includes("apartment 1g")) { return { residenceType: "apartment", speed: "1 Gbps", label: "Apartment/Mansion (1Gbps FTTH)", description: "Your building has fiber-to-the-unit infrastructure supporting 1Gbps speeds.", }; } if (lower.includes("apartment 100m")) { return { residenceType: "apartment", speed: "100 Mbps", label: "Apartment/Mansion (100Mbps)", description: "Your building uses VDSL or LAN infrastructure with up to 100Mbps speeds.", }; } return { residenceType: "home", speed: eligibility, label: eligibility, description: "Service is available at your address.", }; } // Status badge component function EligibilityStatusBadge({ status, speed, }: { status: "eligible" | "pending" | "not_requested" | "ineligible"; speed?: string; }) { const configs = { eligible: { icon: CheckCircle, bg: "bg-success-soft", border: "border-success/30", text: "text-success", label: "Service Available", }, pending: { icon: Clock, bg: "bg-info-soft", border: "border-info/30", text: "text-info", label: "Review in Progress", }, not_requested: { icon: MapPin, bg: "bg-muted", border: "border-border", text: "text-muted-foreground", label: "Verification Required", }, ineligible: { icon: TriangleAlert, bg: "bg-warning/10", border: "border-warning/30", text: "text-warning", label: "Not Available", }, }; const config = configs[status]; const Icon = config.icon; return (
{config.label} {status === "eligible" && speed && ( <> · Up to {speed} )}
); } export function InternetPlansContainer() { const router = useRouter(); const servicesBasePath = useServicesBasePath(); const searchParams = useSearchParams(); const { user } = useAuthSession(); const { data, isLoading, error } = useAccountInternetCatalog(); const eligibilityQuery = useInternetEligibility(); const eligibilityLoading = eligibilityQuery.isLoading; const refetchEligibility = eligibilityQuery.refetch; const eligibilityRequest = useRequestInternetEligibilityCheck(); const submitEligibilityRequest = eligibilityRequest.mutateAsync; const plans: InternetPlanCatalogItem[] = useMemo(() => data?.plans ?? [], [data?.plans]); const installations: InternetInstallationCatalogItem[] = useMemo( () => data?.installations ?? [], [data?.installations] ); const { data: activeSubs } = useActiveSubscriptions(); const hasActiveInternet = useMemo( () => Array.isArray(activeSubs) ? activeSubs.some( s => String(s.productName || "") .toLowerCase() .includes("sonixnet via ntt optical fiber") && String(s.status || "").toLowerCase() === "active" ) : false, [activeSubs] ); const eligibilityValue = eligibilityQuery.data?.eligibility; const eligibilityStatus = eligibilityQuery.data?.status; const requestedAt = eligibilityQuery.data?.requestedAt; const rejectionNotes = eligibilityQuery.data?.notes; const isEligible = eligibilityStatus === "eligible" && typeof eligibilityValue === "string" && eligibilityValue.trim().length > 0; const isPending = eligibilityStatus === "pending"; const isNotRequested = eligibilityStatus === "not_requested"; const isIneligible = eligibilityStatus === "ineligible"; const hasServiceAddress = Boolean( user?.address?.address1 && user?.address?.city && user?.address?.postcode && (user?.address?.country || user?.address?.countryCode) ); const autoEligibilityRequest = searchParams?.get("autoEligibilityRequest") === "1"; const autoPlanSku = searchParams?.get("planSku"); const [autoRequestStatus, setAutoRequestStatus] = useState("idle"); const [autoRequestId, setAutoRequestId] = useState(null); const addressLabel = useMemo(() => { const a = user?.address; if (!a) return ""; return [a.address1, a.address2, a.city, a.state, a.postcode, a.country || a.countryCode] .filter(Boolean) .map(part => String(part).trim()) .filter(part => part.length > 0) .join(", "); }, [user?.address]); const eligibility = useMemo(() => { if (!isEligible) return null; return eligibilityValue?.trim() ?? null; }, [eligibilityValue, isEligible]); const setupFee = useMemo(() => getSetupFee(installations), [installations]); const availableOfferings = useMemo(() => { if (!eligibility) return []; return getAvailableOfferings(eligibility, plans); }, [eligibility, plans]); const eligibilityDisplay = useMemo(() => { if (!eligibility) return null; return formatEligibilityDisplay(eligibility); }, [eligibility]); const offeringCards = useMemo(() => { return availableOfferings .map(config => { const tiers = getTierInfo(plans, config.offeringType); const startingPrice = tiers.length > 0 ? Math.min(...tiers.map(t => t.monthlyPrice)) : 0; return { ...config, tiers, startingPrice, setupFee, ctaPath: `${servicesBasePath}/internet/configure`, }; }) .filter(card => card.tiers.length > 0); }, [availableOfferings, plans, setupFee, servicesBasePath]); // Logic to handle check availability click const handleCheckAvailability = async (e?: React.MouseEvent) => { if (e) e.preventDefault(); if (!hasServiceAddress) { // Should redirect to address page if not handled by parent UI router.push("/account/settings"); return; } // Trigger eligibility check const confirmed = typeof window === "undefined" || window.confirm(`Request availability check for:\n\n${addressLabel}`); if (!confirmed) return; setAutoRequestId(null); setAutoRequestStatus("submitting"); try { const result = await submitEligibilityRequest({ address: user?.address ?? undefined }); setAutoRequestId(result.requestId ?? null); setAutoRequestStatus("submitted"); await refetchEligibility(); const query = result.requestId ? `?requestId=${encodeURIComponent(result.requestId)}` : ""; router.push(`${servicesBasePath}/internet/request-submitted${query}`); } catch { setAutoRequestStatus("failed"); } }; // Auto eligibility request effect useEffect(() => { if (!autoEligibilityRequest) return; if (autoRequestStatus !== "idle") return; if (eligibilityLoading) return; if (!isNotRequested) { router.replace(`${servicesBasePath}/internet`); return; } if (!hasServiceAddress) { setAutoRequestStatus("missing_address"); router.replace(`${servicesBasePath}/internet`); return; } const submit = async () => { setAutoRequestStatus("submitting"); try { const notes = autoPlanSku ? `Requested after signup. Selected plan SKU: ${autoPlanSku}` : "Requested after signup."; const result = await submitEligibilityRequest({ address: user?.address ?? undefined, notes, }); setAutoRequestId(result.requestId ?? null); setAutoRequestStatus("submitted"); await refetchEligibility(); const query = result.requestId ? `?requestId=${encodeURIComponent(result.requestId)}` : ""; router.replace(`${servicesBasePath}/internet/request-submitted${query}`); return; } catch { setAutoRequestStatus("failed"); } router.replace(`${servicesBasePath}/internet`); }; void submit(); }, [ autoEligibilityRequest, autoPlanSku, autoRequestStatus, eligibilityLoading, refetchEligibility, submitEligibilityRequest, hasServiceAddress, isNotRequested, servicesBasePath, user?.address, router, ]); // Loading state if (isLoading || error) { return (
{[1, 2].map(i => (
))}
); } // Determine current status for the badge const currentStatus = isEligible ? "eligible" : isPending ? "pending" : isIneligible ? "ineligible" : "not_requested"; // Case 1: Unverified / Not Requested - Show Public Content exactly if (isNotRequested && autoRequestStatus !== "submitting" && autoRequestStatus !== "submitted") { return (
{/* Already has internet warning */} {hasActiveInternet && ( You already have an internet subscription. For additional residences, please{" "} contact support . )} {/* Auto-request status alerts - only show for errors/success */} {autoRequestStatus === "failed" && ( Please try again below or contact support. )} {autoRequestStatus === "missing_address" && (
Add your service address to request availability verification.
)}
); } // Case 2: Standard Portal View (Pending, Eligible, Ineligible, Loading) return (
{/* Hero section - compact (for portal view) */}

Your Internet Options

Plans tailored to your residence and available infrastructure

{/* Status badge */} {!eligibilityLoading && autoRequestStatus !== "submitting" && ( )} {/* Loading states */} {(eligibilityLoading || autoRequestStatus === "submitting") && (
{autoRequestStatus === "submitting" ? "Submitting request..." : "Checking status..."}
)}
{/* Already has internet warning */} {hasActiveInternet && ( You already have an internet subscription. For additional residences, please{" "} contact support . )} {/* Auto-request status alerts - only show for errors/success */} {autoRequestStatus === "submitted" && ( We'll verify your address and notify you when complete. {autoRequestId && ( ID: {autoRequestId} )} )} {autoRequestStatus === "failed" && ( Please try again below or contact support. )} {autoRequestStatus === "missing_address" && (
Add your service address to request availability verification.
)} {/* ELIGIBLE STATE - Clean & Personalized */} {isEligible && eligibilityDisplay && offeringCards.length > 0 && ( <> {/* Plan comparison guide */}
{/* Speed options header (only if multiple) */} {offeringCards.length > 1 && (

Choose your speed

Your address supports multiple options

)} {/* Offering cards */}
{offeringCards.map(card => (
{card.isAlternative && (
Alternative option
)}
))}
{/* Important notes - collapsed by default */} )} {/* PENDING STATE - Clean Status View */} {isPending && ( <>

Verification in Progress

We're currently verifying NTT service availability at your registered address.
This manual check ensures we offer you the correct fiber connection type.

Estimated time 1-2 business days
{requestedAt && (

Request submitted: {new Date(requestedAt).toLocaleDateString()}

)}
)} {/* INELIGIBLE STATE */} {isIneligible && (

Service not available

{rejectionNotes || "Our review determined that NTT fiber service isn't available at your address."}

)} {/* No plans available */} {plans.length === 0 && !isLoading && (

No Plans Available

We couldn't find any internet plans at this time.

)}
); } export default InternetPlansContainer;