import {
  mdiArrowLeft,
  mdiCreditCardCheckOutline,
} from '@mdi/js'
import axios from 'axios'
import Head from 'next/head'
import { useRouter } from 'next/router'
import React, { ReactElement, useEffect, useMemo, useRef, useState } from 'react'
import BaseButton from '../../components/BaseButton'
import CardBox from '../../components/CardBox'
import SectionMain from '../../components/SectionMain'
import SectionTitleLineWithButton from '../../components/SectionTitleLineWithButton'
import { getPageTitle } from '../../config'
import LayoutAuthenticated from '../../layouts/Authenticated'

type StripeSession = {
  id: string
  status?: string
  payment_status?: string
  amount_total?: number
  currency?: string
  customer_email?: string
  metadata?: Record<string, string | undefined>
}

type PaymentRecord = {
  id: string
  stripe_charge_reference?: string
}

type PaymentsListResponse = {
  rows?: PaymentRecord[]
  count?: number
}

type DemoReservation = {
  id: string
  totalAmount: number
  paidAmount: number
}

type FrontDeskDemoState = {
  rooms?: unknown[]
  reservations?: DemoReservation[]
}

const storageKey = 'hotelpilot-front-desk-demo'
const uuidPattern = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i

const moneyFromCents = (amountInCents?: number, currency = 'usd') =>
  new Intl.NumberFormat('en-US', {
    style: 'currency',
    currency: currency.toUpperCase(),
    maximumFractionDigits: 0,
  }).format((amountInCents || 0) / 100)

const apiErrorMessage = (error: unknown) => {
  if (axios.isAxiosError(error)) {
    const data = error.response?.data

    if (typeof data === 'string') return data

    if (data && typeof data === 'object' && 'message' in data) {
      const message = (data as { message?: unknown }).message
      if (typeof message === 'string') return message
    }

    return error.message
  }

  if (error instanceof Error) return error.message

  return 'Unable to verify the Stripe payment.'
}

const isUuid = (value: string) => uuidPattern.test(value)

const markDemoReservationPaid = (reservationId: string, amountPaid: number) => {
  if (typeof window === 'undefined') return false

  const saved = window.localStorage.getItem(storageKey)
  if (!saved) return false

  try {
    const parsed = JSON.parse(saved) as FrontDeskDemoState
    let updated = false

    if (!Array.isArray(parsed.reservations)) return false

    const reservations = parsed.reservations.map((reservation) => {
      if (reservation.id !== reservationId) return reservation

      updated = true
      return {
        ...reservation,
        paidAmount: Math.min(reservation.totalAmount, reservation.paidAmount + amountPaid),
      }
    })

    if (!updated) return false

    window.localStorage.setItem(storageKey, JSON.stringify({ ...parsed, reservations }))
    return true
  } catch (error) {
    console.error('Failed to update HotelPilot demo reservation after Stripe success', error)
    return false
  }
}

const syncPmsPaymentRecord = async (session: StripeSession, reservationId: string, amountPaid: number) => {
  const existingPayment = await axios.get<PaymentsListResponse>('payments', {
    params: {
      limit: 1,
      page: 0,
      stripe_charge_reference: session.id,
    },
  })

  if (existingPayment.data.rows?.length) {
    return 'Payment verified. A PMS payment record already exists for this Stripe session.'
  }

  await axios.post('payments', {
    data: {
      method: 'stripe',
      amount: amountPaid,
      status: 'captured',
      paid_at: new Date().toISOString(),
      stripe_payment_intent_reference: session.id,
      stripe_charge_reference: session.id,
      receipt_number: session.metadata?.reservationNumber || session.id,
      notes: `Stripe Checkout verified for reservation ${session.metadata?.reservationNumber || reservationId}.`,
      reservation: reservationId,
    },
  })

  return 'Payment verified and saved as a captured PMS payment record.'
}

const StripeSuccessPage = () => {
  const router = useRouter()
  const [session, setSession] = useState<StripeSession | null>(null)
  const [loading, setLoading] = useState(true)
  const [errorMessage, setErrorMessage] = useState('')
  const [syncMessage, setSyncMessage] = useState('')
  const [syncErrorMessage, setSyncErrorMessage] = useState('')
  const syncedSessionRef = useRef('')

  const sessionId = useMemo(() => {
    const value = router.query.session_id
    return typeof value === 'string' ? value : ''
  }, [router.query.session_id])

  const reservationIdFromQuery = useMemo(() => {
    const value = router.query.reservationId
    return typeof value === 'string' ? value : ''
  }, [router.query.reservationId])

  useEffect(() => {
    if (!router.isReady) return

    if (!sessionId) {
      setErrorMessage('Stripe did not provide a Checkout session id.')
      setLoading(false)
      return
    }

    const verifySession = async () => {
      setLoading(true)
      setErrorMessage('')
      setSyncErrorMessage('')

      try {
        const response = await axios.get<StripeSession>(`/stripe/checkout-session/${sessionId}`)
        setSession(response.data)

        const reservationId = response.data.metadata?.reservationId || reservationIdFromQuery
        const amountPaid = (response.data.amount_total || 0) / 100

        if (response.data.payment_status === 'paid' && reservationId && amountPaid > 0) {
          if (syncedSessionRef.current === response.data.id) return

          syncedSessionRef.current = response.data.id

          if (isUuid(reservationId)) {
            try {
              setSyncMessage(await syncPmsPaymentRecord(response.data, reservationId, amountPaid))
            } catch (syncError) {
              console.error('Stripe payment verified, but PMS payment sync failed', syncError)
              setSyncErrorMessage(`Payment verified by Stripe, but PMS payment sync failed: ${apiErrorMessage(syncError)}`)
            }
          } else {
            const updated = markDemoReservationPaid(reservationId, amountPaid)
            setSyncMessage(
              updated
                ? 'Payment verified. The Front Desk demo reservation was marked as paid.'
                : 'Payment verified. Return to Front Desk to continue the stay workflow.',
            )
          }
        } else if (response.data.payment_status === 'paid') {
          setSyncMessage('Payment verified by Stripe.')
        } else {
          setSyncMessage('Stripe returned the session, but the payment is not marked paid yet.')
        }
      } catch (error) {
        if (axios.isAxiosError(error)) {
          console.warn('Stripe payment verification failed', {
            status: error.response?.status,
            data: error.response?.data,
          })
        } else {
          console.error('Stripe payment verification failed', error)
        }

        setErrorMessage(apiErrorMessage(error))
      } finally {
        setLoading(false)
      }
    }

    verifySession()
  }, [reservationIdFromQuery, router.isReady, sessionId])

  return (
    <>
      <Head>
        <title>{getPageTitle('Stripe Payment Success')}</title>
      </Head>
      <SectionMain>
        <SectionTitleLineWithButton icon={mdiCreditCardCheckOutline} title="Stripe payment success" main>
          <BaseButton href="/front-desk" icon={mdiArrowLeft} label="Back to Front Desk" color="info" />
        </SectionTitleLineWithButton>

        <CardBox className="border-0 shadow-sm ring-1 ring-emerald-100">
          <div className="rounded-3xl bg-emerald-50 p-6 text-emerald-900">
            <p className="text-sm font-semibold uppercase tracking-[0.2em] text-emerald-600">HotelPilot PMS</p>
            <h2 className="mt-2 text-3xl font-black">Payment return received</h2>
            <p className="mt-3 max-w-2xl text-sm leading-6 text-emerald-700">
              HotelPilot is verifying the Stripe Checkout session before updating the front desk payment state.
            </p>
          </div>

          <div className="mt-6 space-y-4">
            {loading && <p className="rounded-2xl bg-slate-50 p-4 text-sm text-slate-600">Verifying Stripe payment...</p>}

            {!loading && errorMessage && (
              <p className="rounded-2xl border border-rose-200 bg-rose-50 p-4 text-sm font-medium text-rose-800">{errorMessage}</p>
            )}

            {!loading && !errorMessage && session && (
              <div className="grid gap-4 md:grid-cols-2">
                <div className="rounded-2xl border border-slate-100 p-4">
                  <p className="text-xs uppercase tracking-wide text-slate-400">Session</p>
                  <p className="mt-1 break-all font-bold text-slate-900">{session.id}</p>
                </div>
                <div className="rounded-2xl border border-slate-100 p-4">
                  <p className="text-xs uppercase tracking-wide text-slate-400">Payment status</p>
                  <p className="mt-1 font-bold text-slate-900">{session.payment_status || 'Unknown'}</p>
                </div>
                <div className="rounded-2xl border border-slate-100 p-4">
                  <p className="text-xs uppercase tracking-wide text-slate-400">Amount</p>
                  <p className="mt-1 font-bold text-slate-900">{moneyFromCents(session.amount_total, session.currency)}</p>
                </div>
                <div className="rounded-2xl border border-slate-100 p-4">
                  <p className="text-xs uppercase tracking-wide text-slate-400">Guest email</p>
                  <p className="mt-1 font-bold text-slate-900">{session.customer_email || 'Not provided'}</p>
                </div>
              </div>
            )}

            {syncMessage && <p className="rounded-2xl border border-emerald-200 bg-emerald-50 p-4 text-sm font-medium text-emerald-800">{syncMessage}</p>}
            {syncErrorMessage && <p className="rounded-2xl border border-rose-200 bg-rose-50 p-4 text-sm font-medium text-rose-800">{syncErrorMessage}</p>}

            <div className="flex flex-wrap gap-3">
              <BaseButton href="/front-desk" color="info" label="Return to Front Desk" />
              <BaseButton href="/payments/payments-list" color="whiteDark" label="Open Payments CRUD" />
            </div>
          </div>
        </CardBox>
      </SectionMain>
    </>
  )
}

StripeSuccessPage.getLayout = function getLayout(page: ReactElement) {
  return <LayoutAuthenticated>{page}</LayoutAuthenticated>
}

export default StripeSuccessPage
