"use client"

import { useState, useEffect } from "react"
import { useRouter } from "next/navigation"
import { useMutation, useQueryClient } from "@tanstack/react-query"
import { createSupabaseBrowserClient } from "@/lib/supabase/client"
import { useUserProfile } from "@/lib/queries/use-user-profile"
import { queryKeys } from "@/lib/queries/query-keys"
import { TIER_LIMITS } from "@asa-news/shared"
import { notify } from "@/lib/notify"
import type { Factor } from "@supabase/supabase-js"

type EnrollmentState =
  | { step: "idle" }
  | { step: "enrolling"; factorIdentifier: string; qrCodeSvg: string; otpauthUri: string }

export function AccountSettings() {
  const { data: userProfile, isLoading } = useUserProfile()
  const [isEditingName, setIsEditingName] = useState(false)
  const [editedName, setEditedName] = useState("")
  const [isRequestingData, setIsRequestingData] = useState(false)
  const [newEmailAddress, setNewEmailAddress] = useState("")
  const [emailPassword, setEmailPassword] = useState("")
  const [currentPassword, setCurrentPassword] = useState("")
  const [newPassword, setNewPassword] = useState("")
  const [confirmNewPassword, setConfirmNewPassword] = useState("")
  const [passwordMfaCode, setPasswordMfaCode] = useState("")
  const [emailMfaCode, setEmailMfaCode] = useState("")
  const [enrolledFactors, setEnrolledFactors] = useState<Factor[]>([])
  const [isTotpLoading, setIsTotpLoading] = useState(true)
  const [enrollmentState, setEnrollmentState] = useState<EnrollmentState>({ step: "idle" })
  const [factorName, setFactorName] = useState("")
  const [verificationCode, setVerificationCode] = useState("")
  const [isTotpProcessing, setIsTotpProcessing] = useState(false)
  const [unenrollConfirmIdentifier, setUnenrollConfirmIdentifier] = useState<string | null>(null)
  const supabaseClient = createSupabaseBrowserClient()
  const queryClient = useQueryClient()
  const router = useRouter()

  const updateDisplayName = useMutation({
    mutationFn: async (displayName: string | null) => {
      const { error } = await supabaseClient.auth.updateUser({
        data: { display_name: displayName },
      })

      if (error) throw error
    },
    onSuccess: () => {
      queryClient.invalidateQueries({ queryKey: queryKeys.userProfile.all })
      notify("display name updated")
    },
    onError: (error: Error) => {
      notify("failed to update display name: " + error.message)
    },
  })

  async function elevateToAal2(mfaCode: string) {
    const { data: factorsData } = await supabaseClient.auth.mfa.listFactors()
    const verifiedFactors = factorsData?.totp.filter((factor) => factor.status === "verified") ?? []

    if (verifiedFactors.length === 0) return

    if (!mfaCode || mfaCode.length !== 6) {
      throw new Error("enter your 6-digit authenticator code")
    }

    const { data: challengeData, error: challengeError } =
      await supabaseClient.auth.mfa.challenge({ factorId: verifiedFactors[0].id })

    if (challengeError) throw new Error("mfa challenge failed: " + challengeError.message)

    const { error: verifyError } = await supabaseClient.auth.mfa.verify({
      factorId: verifiedFactors[0].id,
      challengeId: challengeData.id,
      code: mfaCode,
    })

    if (verifyError) throw new Error("invalid authenticator code")
  }

  const updateEmailAddress = useMutation({
    mutationFn: async ({
      emailAddress,
      password,
      mfaCode,
    }: {
      emailAddress: string
      password: string
      mfaCode: string
    }) => {
      const {
        data: { user },
      } = await supabaseClient.auth.getUser()

      if (!user?.email) throw new Error("not authenticated")

      const { error: signInError } = await supabaseClient.auth.signInWithPassword({
        email: user.email,
        password,
      })

      if (signInError) throw new Error("incorrect password")

      await elevateToAal2(mfaCode)

      const { error } = await supabaseClient.auth.updateUser({
        email: emailAddress,
      })

      if (error) throw error
    },
    onSuccess: () => {
      setNewEmailAddress("")
      setEmailPassword("")
      setEmailMfaCode("")
      notify("confirmation email sent to your new address")
    },
    onError: (error: Error) => {
      notify("failed to update email: " + error.message)
    },
  })

  const updatePassword = useMutation({
    mutationFn: async ({
      currentPassword: current,
      newPassword: updated,
      mfaCode,
    }: {
      currentPassword: string
      newPassword: string
      mfaCode: string
    }) => {
      const {
        data: { user },
      } = await supabaseClient.auth.getUser()

      if (!user?.email) throw new Error("not authenticated")

      const { error: signInError } = await supabaseClient.auth.signInWithPassword({
        email: user.email,
        password: current,
      })

      if (signInError) throw new Error("current password is incorrect")

      await elevateToAal2(mfaCode)

      const { error } = await supabaseClient.auth.updateUser({
        password: updated,
      })

      if (error) throw error
    },
    onSuccess: async () => {
      setCurrentPassword("")
      setNewPassword("")
      setConfirmNewPassword("")
      setPasswordMfaCode("")
      notify("password updated — signing out all sessions")
      await supabaseClient.auth.signOut({ scope: "global" })
      router.push("/sign-in")
    },
    onError: (error: Error) => {
      notify("failed to update password: " + error.message)
    },
  })

  async function loadFactors() {
    const { data, error } = await supabaseClient.auth.mfa.listFactors()

    if (error) {
      notify("failed to load mfa factors")
      setIsTotpLoading(false)
      return
    }

    setEnrolledFactors(
      data.totp.filter((factor) => factor.status === "verified")
    )
    setIsTotpLoading(false)
  }

  useEffect(() => {
    loadFactors()
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [])

  async function handleBeginEnrollment() {
    setIsTotpProcessing(true)

    const enrollOptions: { factorType: "totp"; friendlyName?: string } = {
      factorType: "totp",
    }
    if (factorName.trim()) {
      enrollOptions.friendlyName = factorName.trim()
    }

    const { data, error } = await supabaseClient.auth.mfa.enroll(enrollOptions)

    setIsTotpProcessing(false)

    if (error) {
      notify("failed to start mfa enrolment: " + error.message)
      return
    }

    setEnrollmentState({
      step: "enrolling",
      factorIdentifier: data.id,
      qrCodeSvg: data.totp.qr_code,
      otpauthUri: data.totp.uri,
    })
    setVerificationCode("")
  }

  async function handleVerifyEnrollment() {
    if (enrollmentState.step !== "enrolling") return
    if (verificationCode.length !== 6) return

    setIsTotpProcessing(true)

    const { data: challengeData, error: challengeError } =
      await supabaseClient.auth.mfa.challenge({
        factorId: enrollmentState.factorIdentifier,
      })

    if (challengeError) {
      setIsTotpProcessing(false)
      notify("failed to create mfa challenge: " + challengeError.message)
      return
    }

    const { error: verifyError } = await supabaseClient.auth.mfa.verify({
      factorId: enrollmentState.factorIdentifier,
      challengeId: challengeData.id,
      code: verificationCode,
    })

    setIsTotpProcessing(false)

    if (verifyError) {
      notify("invalid code — please try again")
      setVerificationCode("")
      return
    }

    notify("two-factor authentication enabled")
    setEnrollmentState({ step: "idle" })
    setVerificationCode("")
    setFactorName("")
    await supabaseClient.auth.refreshSession()
    await loadFactors()
  }

  async function handleCancelEnrollment() {
    if (enrollmentState.step === "enrolling") {
      await supabaseClient.auth.mfa.unenroll({
        factorId: enrollmentState.factorIdentifier,
      })
    }

    setEnrollmentState({ step: "idle" })
    setVerificationCode("")
    setFactorName("")
  }

  async function handleUnenrollFactor(factorIdentifier: string) {
    setIsTotpProcessing(true)

    const { error } = await supabaseClient.auth.mfa.unenroll({
      factorId: factorIdentifier,
    })

    setIsTotpProcessing(false)

    if (error) {
      notify("failed to remove factor: " + error.message)
      return
    }

    notify("two-factor authentication removed")
    setUnenrollConfirmIdentifier(null)
    await supabaseClient.auth.refreshSession()
    await loadFactors()
  }

  if (isLoading) {
    return <p className="px-4 py-6 text-text-dim">loading account ...</p>
  }

  if (!userProfile) {
    return <p className="px-4 py-6 text-text-dim">failed to load account</p>
  }

  const tier = userProfile.tier
  const tierLimits = TIER_LIMITS[tier]

  async function handleRequestData() {
    setIsRequestingData(true)
    try {
      const response = await fetch("/api/account/data")
      if (!response.ok) throw new Error("export failed")
      const blob = await response.blob()
      const url = URL.createObjectURL(blob)
      const anchor = document.createElement("a")
      anchor.href = url
      anchor.download = `asa-news-gdpr-export-${new Date().toISOString().slice(0, 10)}.json`
      anchor.click()
      URL.revokeObjectURL(url)
      notify("data exported")
    } catch {
      notify("failed to export data")
    } finally {
      setIsRequestingData(false)
    }
  }

  function handleSaveName() {
    const trimmedName = editedName.trim()
    updateDisplayName.mutate(trimmedName || null)
    setIsEditingName(false)
  }

  function handleUpdateEmail(event: React.FormEvent) {
    event.preventDefault()
    const trimmedEmail = newEmailAddress.trim()
    if (!trimmedEmail || !emailPassword) return
    updateEmailAddress.mutate({ emailAddress: trimmedEmail, password: emailPassword, mfaCode: emailMfaCode })
  }

  function handleUpdatePassword(event: React.FormEvent) {
    event.preventDefault()
    if (!currentPassword) {
      notify("current password is required")
      return
    }
    if (!newPassword || newPassword !== confirmNewPassword) {
      notify("passwords do not match")
      return
    }
    if (newPassword.length < 8) {
      notify("password must be at least 8 characters")
      return
    }
    updatePassword.mutate({ currentPassword, newPassword, mfaCode: passwordMfaCode })
  }

  return (
    <div className="px-4 py-3">
      <div className="mb-6">
        <h3 className="mb-2 text-text-primary">display name</h3>
        {isEditingName ? (
          <div className="flex items-center gap-2">
            <input
              type="text"
              value={editedName}
              onChange={(event) => setEditedName(event.target.value)}
              placeholder="display name"
              className="min-w-0 flex-1 border border-border bg-background-primary px-3 py-2 text-text-primary outline-none placeholder:text-text-dim focus:border-text-dim"
              onKeyDown={(event) => {
                if (event.key === "Enter") handleSaveName()
                if (event.key === "Escape") setIsEditingName(false)
              }}
              autoFocus
            />
            <button
              onClick={handleSaveName}
              className="px-2 py-1 text-text-secondary transition-colors hover:text-text-primary"
            >
              save
            </button>
            <button
              onClick={() => setIsEditingName(false)}
              className="px-2 py-1 text-text-secondary transition-colors hover:text-text-primary"
            >
              cancel
            </button>
          </div>
        ) : (
          <div className="flex items-center gap-2">
            <span className="text-text-secondary">
              {userProfile.displayName ?? "not set"}
            </span>
            <button
              onClick={() => {
                setEditedName(userProfile.displayName ?? "")
                setIsEditingName(true)
              }}
              className="px-2 py-1 text-text-dim transition-colors hover:text-text-secondary"
            >
              edit
            </button>
          </div>
        )}
      </div>
      <div className="mb-6">
        <h3 className="mb-2 text-text-primary">email address</h3>
        <p className="mb-2 text-text-dim">
          {userProfile.email ?? "no email on file"}
        </p>
        <form onSubmit={handleUpdateEmail} className="space-y-2">
          <input
            type="email"
            value={newEmailAddress}
            onChange={(event) => setNewEmailAddress(event.target.value)}
            placeholder="new email address"
            className="w-full border border-border bg-background-primary px-3 py-2 text-text-primary outline-none placeholder:text-text-dim focus:border-text-dim"
          />
          <input
            type="password"
            value={emailPassword}
            onChange={(event) => setEmailPassword(event.target.value)}
            placeholder="current password"
            className="w-full border border-border bg-background-primary px-3 py-2 text-text-primary outline-none placeholder:text-text-dim focus:border-text-dim"
          />
          {enrolledFactors.length > 0 && (
            <input
              type="text"
              inputMode="numeric"
              pattern="[0-9]*"
              maxLength={6}
              value={emailMfaCode}
              onChange={(event) => setEmailMfaCode(event.target.value.replace(/\D/g, ""))}
              placeholder="authenticator code"
              className="w-full border border-border bg-background-primary px-3 py-2 text-text-primary outline-none placeholder:text-text-dim focus:border-text-dim"
            />
          )}
          <button
            type="submit"
            disabled={updateEmailAddress.isPending || !newEmailAddress.trim() || !emailPassword}
            className="border border-border bg-background-tertiary px-4 py-2 text-text-primary transition-colors hover:bg-border disabled:opacity-50"
          >
            update email
          </button>
        </form>
      </div>
      <div className="mb-6">
        <h3 className="mb-2 text-text-primary">change password</h3>
        <form onSubmit={handleUpdatePassword} className="space-y-2">
          <input
            type="password"
            value={currentPassword}
            onChange={(event) => setCurrentPassword(event.target.value)}
            placeholder="current password"
            className="w-full border border-border bg-background-primary px-3 py-2 text-text-primary outline-none placeholder:text-text-dim focus:border-text-dim"
          />
          <input
            type="password"
            value={newPassword}
            onChange={(event) => setNewPassword(event.target.value)}
            placeholder="new password"
            className="w-full border border-border bg-background-primary px-3 py-2 text-text-primary outline-none placeholder:text-text-dim focus:border-text-dim"
          />
          <input
            type="password"
            value={confirmNewPassword}
            onChange={(event) => setConfirmNewPassword(event.target.value)}
            placeholder="confirm new password"
            className="w-full border border-border bg-background-primary px-3 py-2 text-text-primary outline-none placeholder:text-text-dim focus:border-text-dim"
          />
          {enrolledFactors.length > 0 && (
            <input
              type="text"
              inputMode="numeric"
              pattern="[0-9]*"
              maxLength={6}
              value={passwordMfaCode}
              onChange={(event) => setPasswordMfaCode(event.target.value.replace(/\D/g, ""))}
              placeholder="authenticator code"
              className="w-full border border-border bg-background-primary px-3 py-2 text-text-primary outline-none placeholder:text-text-dim focus:border-text-dim"
            />
          )}
          <button
            type="submit"
            disabled={updatePassword.isPending || !currentPassword || !newPassword || newPassword !== confirmNewPassword}
            className="border border-border bg-background-tertiary px-4 py-2 text-text-primary transition-colors hover:bg-border disabled:opacity-50"
          >
            change password
          </button>
        </form>
      </div>
      <div className="mb-6">
        <h3 className="mb-2 text-text-primary">two-factor authentication</h3>
        <p className="mb-4 text-text-dim">
          add an extra layer of security to your account with a time-based one-time password (totp) authenticator app
        </p>

        {isTotpLoading ? (
          <p className="text-text-dim">loading ...</p>
        ) : (
          <>
            {enrollmentState.step === "idle" && enrolledFactors.length === 0 && (
              <div className="flex items-center gap-2">
                <input
                  type="text"
                  value={factorName}
                  onChange={(event) => setFactorName(event.target.value)}
                  placeholder="authenticator name (optional)"
                  className="min-w-0 flex-1 border border-border bg-background-primary px-3 py-2 text-text-primary outline-none placeholder:text-text-dim focus:border-text-dim"
                />
                <button
                  onClick={handleBeginEnrollment}
                  disabled={isTotpProcessing}
                  className="shrink-0 border border-border bg-background-tertiary px-4 py-2 text-text-primary transition-colors hover:bg-border disabled:opacity-50"
                >
                  {isTotpProcessing ? "setting up ..." : "set up"}
                </button>
              </div>
            )}

            {enrollmentState.step === "enrolling" && (
              <div className="space-y-4">
                <p className="text-text-secondary">
                  scan this qr code with your authenticator app, then enter the 6-digit code below
                </p>
                <div className="inline-block bg-white p-4">
                  <img
                    src={enrollmentState.qrCodeSvg}
                    alt="totp qr code"
                    className="h-48 w-48"
                  />
                </div>
                <details className="text-text-dim">
                  <summary className="cursor-pointer transition-colors hover:text-text-secondary">
                    can&apos;t scan? copy manual entry key
                  </summary>
                  <code className="mt-2 block break-all bg-background-secondary p-2 text-text-secondary">
                    {enrollmentState.otpauthUri}
                  </code>
                </details>
                <div className="flex items-center gap-2">
                  <input
                    type="text"
                    inputMode="numeric"
                    pattern="[0-9]*"
                    maxLength={6}
                    value={verificationCode}
                    onChange={(event) => {
                      const filtered = event.target.value.replace(/\D/g, "")
                      setVerificationCode(filtered)
                    }}
                    placeholder="000000"
                    className="w-32 border border-border bg-background-primary px-3 py-2 text-center font-mono text-lg tracking-widest text-text-primary outline-none placeholder:text-text-dim focus:border-text-dim"
                    autoFocus
                    onKeyDown={(event) => {
                      if (event.key === "Enter") handleVerifyEnrollment()
                      if (event.key === "Escape") handleCancelEnrollment()
                    }}
                  />
                  <button
                    onClick={handleVerifyEnrollment}
                    disabled={isTotpProcessing || verificationCode.length !== 6}
                    className="border border-border bg-background-tertiary px-4 py-2 text-text-primary transition-colors hover:bg-border disabled:opacity-50"
                  >
                    {isTotpProcessing ? "verifying ..." : "verify"}
                  </button>
                  <button
                    onClick={handleCancelEnrollment}
                    className="px-4 py-2 text-text-secondary transition-colors hover:text-text-primary"
                  >
                    cancel
                  </button>
                </div>
              </div>
            )}

            {enrolledFactors.length > 0 && enrollmentState.step === "idle" && (
              <div className="space-y-3">
                {enrolledFactors.map((factor) => (
                  <div
                    key={factor.id}
                    className="flex items-center justify-between border border-border px-4 py-3"
                  >
                    <div>
                      <span className="text-text-primary">
                        {factor.friendly_name || "totp authenticator"}
                      </span>
                      <span className="ml-2 text-text-dim">
                        added{" "}
                        {new Date(factor.created_at).toLocaleDateString("en-GB", {
                          day: "numeric",
                          month: "short",
                          year: "numeric",
                        })}
                      </span>
                    </div>
                    {unenrollConfirmIdentifier === factor.id ? (
                      <div className="flex items-center gap-2">
                        <span className="text-text-dim">remove?</span>
                        <button
                          onClick={() => handleUnenrollFactor(factor.id)}
                          disabled={isTotpProcessing}
                          className="text-status-error transition-colors hover:text-text-primary disabled:opacity-50"
                        >
                          yes
                        </button>
                        <button
                          onClick={() => setUnenrollConfirmIdentifier(null)}
                          className="text-text-secondary transition-colors hover:text-text-primary"
                        >
                          no
                        </button>
                      </div>
                    ) : (
                      <button
                        onClick={() => setUnenrollConfirmIdentifier(factor.id)}
                        className="text-text-secondary transition-colors hover:text-status-error"
                      >
                        remove
                      </button>
                    )}
                  </div>
                ))}
              </div>
            )}
          </>
        )}
      </div>
      <div className="mb-6">
        <h3 className="mb-2 text-text-primary">usage</h3>
        <div className="space-y-1">
          <UsageRow
            label="feeds"
            current={userProfile.feedCount}
            maximum={tierLimits.maximumFeeds}
          />
          <UsageRow
            label="folders"
            current={userProfile.folderCount}
            maximum={tierLimits.maximumFolders}
          />
          <UsageRow
            label="muted phrases"
            current={userProfile.mutedKeywordCount}
            maximum={tierLimits.maximumMutedKeywords}
          />
          <UsageRow
            label="custom feeds"
            current={userProfile.customFeedCount}
            maximum={tierLimits.maximumCustomFeeds}
          />
        </div>
      </div>
      <div className="mb-6">
        <h3 className="mb-2 text-text-primary">your data</h3>
        <p className="mb-3 text-text-dim">
          download all your data (profile, subscriptions, folders, highlights, saved entries)
        </p>
        <button
          onClick={handleRequestData}
          disabled={isRequestingData}
          className="border border-border bg-background-tertiary px-4 py-2 text-text-primary transition-colors hover:bg-border disabled:opacity-50"
        >
          {isRequestingData ? "exporting ..." : "request all data"}
        </button>
      </div>
      <div className="mb-6">
        <h3 className="mb-2 text-text-primary">support</h3>
        <p className="text-text-dim">
          need help or have feedback? reach out at{" "}
          <a
            href="mailto:support@asa.news"
            className="text-text-secondary transition-colors hover:text-text-primary"
          >
            support@asa.news
          </a>
        </p>
      </div>
    </div>
  )
}

function UsageRow({
  label,
  current,
  maximum,
}: {
  label: string
  current: number
  maximum: number
}) {
  const isNearLimit = current >= maximum * 0.8
  const isAtLimit = current >= maximum

  return (
    <div className="flex items-center justify-between py-1">
      <span className="text-text-secondary">{label}</span>
      <span
        className={
          isAtLimit
            ? "text-status-error"
            : isNearLimit
              ? "text-text-primary"
              : "text-text-dim"
        }
      >
        {current} / {maximum}
      </span>
    </div>
  )
}
