// frontend\src\pages\settings\konto\Konto.tsx

import {
  useEffect,
  useRef,
  useState,
  type ChangeEvent,
  type FormEvent,
  type ReactNode,
} from 'react'
import Button from '../../../components/Button'
import Container from '../../../components/Container'
import type { User } from '../../../components/types'
import {
  ComputerDesktopIcon,
  ShieldCheckIcon,
} from '@heroicons/react/20/solid'
import { useNotifications } from '../../../components/Notifications'
import Avatar from '../../../components/Avatar'
import Combobox, { type ComboboxItem } from '../../../components/Combobox'

const API_URL = import.meta.env.VITE_API_URL ?? 'http://localhost:8080'

type KontoProps = {
  user?: User
  onUserUpdated?: (user: User) => void
}

type UserSession = {
  id: string
  browser: string
  operatingSystem: string
  deviceType: string
  userAgent: string
  ipAddress: string
  createdAt: string
  lastSeenAt: string
  current: boolean
}

const inputClassName =
  'block w-full rounded-md bg-white px-3 py-1.5 text-base text-gray-900 outline-1 -outline-offset-1 outline-gray-300 placeholder:text-gray-400 focus:outline-2 focus:-outline-offset-2 focus:outline-indigo-600 sm:text-sm/6 dark:bg-white/5 dark:text-white dark:outline-white/10 dark:placeholder:text-gray-500 dark:focus:outline-indigo-500'

function Section({
  title,
  description,
  children,
}: {
  title: string
  description: string
  children: ReactNode
}) {
  return (
    <Container variant="constrained" className="py-16">
      <div className="grid grid-cols-1 gap-x-8 gap-y-10 md:grid-cols-3">
        <div>
          <h2 className="text-base/7 font-semibold text-gray-900 dark:text-white">
            {title}
          </h2>

          <p className="mt-1 text-sm/6 text-gray-500 dark:text-gray-400">
            {description}
          </p>
        </div>

        <div className="md:col-span-2">
          {children}
        </div>
      </div>
    </Container>
  )
}

async function readApiError(response: Response, fallback: string) {
  const data = await response.json().catch(() => null)

  return data?.error ?? fallback
}

export default function Konto({
  user,
  onUserUpdated,
}: KontoProps) {
  const { showToast, showErrorToast } = useNotifications()

  const [currentUser, setCurrentUser] = useState<User | undefined>(user)
  const [avatarPreview, setAvatarPreview] = useState(user?.avatar ?? '')
  const avatarInputRef = useRef<HTMLInputElement | null>(null)
  const [sessions, setSessions] = useState<UserSession[]>([])

  const [isLoadingSessions, setIsLoadingSessions] = useState(false)
  const [revokingSessionId, setRevokingSessionId] = useState<string | null>(null)
  const [isSavingKonto, setIsSavingKonto] = useState(false)
  const [isSavingPassword, setIsSavingPassword] = useState(false)
  const [isLoggingOutSessions, setIsLoggingOutSessions] = useState(false)
  const [isDeletingKonto, setIsDeletingKonto] = useState(false)

  useEffect(() => {
    setCurrentUser(user)
    setAvatarPreview(user?.avatar ?? '')
  }, [user])

  useEffect(() => {
    void loadSessions()
  }, [])

  async function loadSessions() {
    setIsLoadingSessions(true)

    try {
      const response = await fetch(`${API_URL}/settings/sessions`, {
        credentials: 'include',
      })

      if (!response.ok) {
        throw new Error(
          await readApiError(response, 'Sitzungen konnten nicht geladen werden'),
        )
      }

      const data = await response.json()
      setSessions(data.sessions ?? [])
    } catch (error) {
      showToast({
        title: 'Sitzungen konnten nicht geladen werden',
        message:
          error instanceof Error
            ? error.message
            : 'Sitzungen konnten nicht geladen werden',
        variant: 'error',
      })
    } finally {
      setIsLoadingSessions(false)
    }
  }

  async function handleRevokeSession(sessionId: string) {
    setRevokingSessionId(sessionId)

    try {
      const response = await fetch(`${API_URL}/settings/sessions/${sessionId}`, {
        method: 'DELETE',
        credentials: 'include',
      })

      if (!response.ok) {
        throw new Error(
          await readApiError(response, 'Sitzung konnte nicht abgemeldet werden'),
        )
      }

      await loadSessions()
      showToast({
        title: 'Sitzung abgemeldet',
        message: 'Die ausgewählte Sitzung wurde abgemeldet.',
        variant: 'success',
      })
    } catch (error) {
      showToast({
        title: 'Sitzung konnte nicht abgemeldet werden',
        message:
          error instanceof Error
            ? error.message
            : 'Sitzung konnte nicht abgemeldet werden',
        variant: 'error',
        autoClose: false,
      })
    } finally {
      setRevokingSessionId(null)
    }
  }

  function handleAvatarFileChange(event: ChangeEvent<HTMLInputElement>) {
    const file = event.target.files?.[0]

    if (!file) {
      return
    }

    if (!file.type.startsWith('image/')) {
      showToast({
        title: 'Ungültige Datei',
        message: 'Bitte wähle eine gültige Bilddatei aus.',
        variant: 'error',
      })
      event.target.value = ''
      return
    }

    if (file.size > 2 * 1024 * 1024) {
      showToast({
        title: 'Bild zu groß',
        message: 'Das Profilbild darf maximal 2 MB groß sein.',
        variant: 'error',
      })
      event.target.value = ''
      return
    }

    const reader = new FileReader()

    reader.onload = () => {
      if (typeof reader.result === 'string') {
        setAvatarPreview(reader.result)
      }
    }

    reader.readAsDataURL(file)
    event.target.value = ''
  }

  function handleRemoveAvatar() {
    setAvatarPreview('')
  }

  async function handleKontoSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()

    const form = event.currentTarget
    const formData = new FormData(form)

    setIsSavingKonto(true)

    try {
      const response = await fetch(`${API_URL}/settings/account`, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json',
        },
        credentials: 'include',
        body: JSON.stringify({
          displayName: String(formData.get('displayName') ?? ''),
          username: String(formData.get('username') ?? ''),
          email: String(formData.get('email') ?? ''),
          avatar: String(formData.get('avatar') ?? ''),
        }),
      })

      if (!response.ok) {
        throw new Error(
          await readApiError(response, 'Profil konnte nicht gespeichert werden'),
        )
      }

      const data = await response.json().catch(() => null)
      const nextUser = data?.user as User | undefined

      if (nextUser) {
        setCurrentUser(nextUser)
        setAvatarPreview(nextUser.avatar ?? '')
        onUserUpdated?.(nextUser)
      }

      showToast({
        title: 'Profil gespeichert',
        message: 'Deine Profildaten wurden gespeichert.',
        variant: 'success',
      })
    } catch (error) {
      showErrorToast({
        title: 'Profil konnte nicht gespeichert werden',
        error,
        fallback: 'Profil konnte nicht gespeichert werden',
      })
    } finally {
      setIsSavingKonto(false)
    }
  }

  async function handlePasswordSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()

    const form = event.currentTarget
    const formData = new FormData(form)

    setIsSavingPassword(true)

    try {
      const response = await fetch(`${API_URL}/settings/password`, {
        method: 'PUT',
        headers: {
          'Content-Type': 'application/json',
        },
        credentials: 'include',
        body: JSON.stringify({
          currentPassword: String(formData.get('currentPassword') ?? ''),
          newPassword: String(formData.get('newPassword') ?? ''),
          confirmPassword: String(formData.get('confirmPassword') ?? ''),
        }),
      })

      if (!response.ok) {
        throw new Error(
          await readApiError(response, 'Passwort konnte nicht gespeichert werden'),
        )
      }

      form.reset()
      showToast({
        title: 'Passwort gespeichert',
        message: 'Dein Passwort wurde erfolgreich geändert.',
        variant: 'success',
      })
    } catch (error) {
      showToast({
        title: 'Passwort konnte nicht gespeichert werden',
        message:
          error instanceof Error
            ? error.message
            : 'Passwort konnte nicht gespeichert werden',
        variant: 'error',
        autoClose: false,
      })
    } finally {
      setIsSavingPassword(false)
    }
  }

  async function handleLogoutSessionsSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()

    const form = event.currentTarget
    const formData = new FormData(form)

    setIsLoggingOutSessions(true)

    try {
      const response = await fetch(`${API_URL}/settings/logout-other-sessions`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
        },
        credentials: 'include',
        body: JSON.stringify({
          password: String(formData.get('password') ?? ''),
        }),
      })

      if (!response.ok) {
        throw new Error(
          await readApiError(
            response,
            'Andere Sitzungen konnten nicht abgemeldet werden',
          ),
        )
      }

      form.reset()
      showToast({
        title: 'Andere Sitzungen abgemeldet',
        message: 'Alle anderen aktiven Sitzungen wurden abgemeldet.',
        variant: 'success',
      })
      await loadSessions()
    } catch (error) {
      showToast({
        title: 'Andere Sitzungen konnten nicht abgemeldet werden',
        message:
          error instanceof Error
            ? error.message
            : 'Andere Sitzungen konnten nicht abgemeldet werden',
        variant: 'error',
        autoClose: false,
      })
    } finally {
      setIsLoggingOutSessions(false)
    }
  }

  async function handleDeleteKontoSubmit(event: FormEvent<HTMLFormElement>) {
    event.preventDefault()

    const form = event.currentTarget
    const formData = new FormData(form)

    const confirmed = window.confirm(
      'Konto wirklich löschen? Diese Aktion kann nicht rückgängig gemacht werden.',
    )

    if (!confirmed) {
      return
    }

    setIsDeletingKonto(true)

    try {
      const response = await fetch(`${API_URL}/settings/account`, {
        method: 'DELETE',
        headers: {
          'Content-Type': 'application/json',
        },
        credentials: 'include',
        body: JSON.stringify({
          password: String(formData.get('password') ?? ''),
        }),
      })

      if (!response.ok) {
        throw new Error(
          await readApiError(response, 'Konto konnte nicht gelöscht werden'),
        )
      }

      window.location.href = '/login'
    } catch (error) {
      showToast({
        title: 'Konto konnte nicht gelöscht werden',
        message:
          error instanceof Error
            ? error.message
            : 'Konto konnte nicht gelöscht werden',
        variant: 'error',
        autoClose: false,
      })
    } finally {
      setIsDeletingKonto(false)
    }
  }

  return (
    <>
      <div className="divide-y divide-gray-200 dark:divide-white/10">
        <Section
          title="Persönliche Informationen"
          description="Verwalte deine Profildaten und Kontoinformationen."
        >
          <form onSubmit={handleKontoSubmit}>
            <div className="grid grid-cols-1 gap-x-6 gap-y-8 sm:max-w-xl sm:grid-cols-6">
              <div className="col-span-full flex items-center gap-x-8">
                <Avatar
                  src={avatarPreview}
                  name={currentUser?.displayName || currentUser?.username || currentUser?.email}
                  size={16}
                  shape="rounded"
                />

                <div className="min-w-0 flex-1">
                  <input
                    type="hidden"
                    name="avatar"
                    value={avatarPreview}
                  />

                  <input
                    ref={avatarInputRef}
                    type="file"
                    accept="image/png,image/jpeg,image/webp,image/gif"
                    className="sr-only"
                    onChange={handleAvatarFileChange}
                  />

                  <div className="flex flex-wrap items-center gap-2">
                    <Button
                      type="button"
                      variant="secondary"
                      color="gray"
                      size="sm"
                      onClick={() => avatarInputRef.current?.click()}
                    >
                      Bild hochladen
                    </Button>

                    {avatarPreview && (
                      <Button
                        type="button"
                        variant="secondary"
                        color="red"
                        size="sm"
                        onClick={handleRemoveAvatar}
                      >
                        Entfernen
                      </Button>
                    )}
                  </div>

                  <p className="mt-2 text-xs/5 text-gray-500 dark:text-gray-400">
                    PNG, JPG, WEBP oder GIF bis maximal 2 MB.
                  </p>
                </div>
              </div>

              <div className="sm:col-span-3">
                <label
                  htmlFor="displayName"
                  className="block text-sm/6 font-medium text-gray-900 dark:text-white"
                >
                  Anzeigename
                </label>

                <div className="mt-2">
                  <input
                    id="displayName"
                    name="displayName"
                    type="text"
                    defaultValue={currentUser?.displayName ?? ''}
                    className={inputClassName}
                  />
                </div>
              </div>

              <div className="sm:col-span-3">
                <label
                  htmlFor="username"
                  className="block text-sm/6 font-medium text-gray-900 dark:text-white"
                >
                  Benutzername
                </label>

                <div className="mt-2">
                  <input
                    id="username"
                    name="username"
                    type="text"
                    required
                    defaultValue={currentUser?.username ?? ''}
                    className={inputClassName}
                  />
                </div>
              </div>

              <div className="col-span-full">
                <label
                  htmlFor="email"
                  className="block text-sm/6 font-medium text-gray-900 dark:text-white"
                >
                  E-Mail-Adresse
                </label>

                <div className="mt-2">
                  <input
                    id="email"
                    name="email"
                    type="email"
                    autoComplete="email"
                    required
                    defaultValue={currentUser?.email ?? ''}
                    className={inputClassName}
                  />
                </div>
              </div>

              <div className="col-span-full">
                <Combobox
                  label="Einheit"
                  items={
                    currentUser?.unit
                      ? [{ id: currentUser.unit, name: currentUser.unit }]
                      : []
                  }
                  value={
                    currentUser?.unit
                      ? ({
                          id: currentUser.unit,
                          name: currentUser.unit,
                        } satisfies ComboboxItem)
                      : null
                  }
                  onChange={() => undefined}
                  placeholder="Keine Einheit zugeordnet"
                  disabled
                />
                <p className="mt-2 text-xs/5 text-gray-500 dark:text-gray-400">
                  Die Einheit kann nur in der Benutzeradministration geändert werden.
                </p>
              </div>
            </div>

            <div className="mt-8 flex">
              <Button
                type="submit"
                color="indigo"
                size="md"
                isLoading={isSavingKonto}
              >
                Speichern
              </Button>
            </div>
          </form>
        </Section>

        <Section
          title="Passwort ändern"
          description="Aktualisiere das Passwort deines Benutzerkontos."
        >
          <form onSubmit={handlePasswordSubmit}>
            <div className="grid grid-cols-1 gap-x-6 gap-y-8 sm:max-w-xl sm:grid-cols-6">
              <div className="col-span-full">
                <label
                  htmlFor="current-password"
                  className="block text-sm/6 font-medium text-gray-900 dark:text-white"
                >
                  Aktuelles Passwort
                </label>

                <div className="mt-2">
                  <input
                    id="current-password"
                    name="currentPassword"
                    type="password"
                    autoComplete="current-password"
                    required
                    className={inputClassName}
                  />
                </div>
              </div>

              <div className="col-span-full">
                <label
                  htmlFor="new-password"
                  className="block text-sm/6 font-medium text-gray-900 dark:text-white"
                >
                  Neues Passwort
                </label>

                <div className="mt-2">
                  <input
                    id="new-password"
                    name="newPassword"
                    type="password"
                    autoComplete="new-password"
                    minLength={8}
                    required
                    className={inputClassName}
                  />
                </div>
              </div>

              <div className="col-span-full">
                <label
                  htmlFor="confirm-password"
                  className="block text-sm/6 font-medium text-gray-900 dark:text-white"
                >
                  Neues Passwort bestätigen
                </label>

                <div className="mt-2">
                  <input
                    id="confirm-password"
                    name="confirmPassword"
                    type="password"
                    autoComplete="new-password"
                    minLength={8}
                    required
                    className={inputClassName}
                  />
                </div>
              </div>
            </div>

            <div className="mt-8 flex">
              <Button
                type="submit"
                color="indigo"
                size="md"
                isLoading={isSavingPassword}
              >
                Passwort speichern
              </Button>
            </div>
          </form>
        </Section>

        <Section
          title="Sitzungen"
          description="Sieh dir deine aktiven Sitzungen an und melde andere Browser ab."
        >
          <div className="sm:max-w-2xl">
            {isLoadingSessions && (
              <div className="rounded-md border border-dashed border-gray-300 p-4 text-sm text-gray-500 dark:border-white/10 dark:text-gray-400">
                Sitzungen werden geladen...
              </div>
            )}

            {!isLoadingSessions && sessions.length > 0 && (
              <div className="space-y-3">
                {sessions.map((session) => (
                  <div
                    key={session.id}
                    className="rounded-xl border border-gray-200 bg-white p-4 shadow-sm dark:border-white/10 dark:bg-white/5"
                  >
                    <div className="flex items-start gap-x-4">
                      <div className="flex size-11 shrink-0 items-center justify-center rounded-lg bg-indigo-50 text-indigo-600 ring-1 ring-indigo-600/10 dark:bg-indigo-500/10 dark:text-indigo-300 dark:ring-indigo-400/20">
                        <ComputerDesktopIcon
                          aria-hidden="true"
                          className="size-6"
                        />
                      </div>

                      <div className="min-w-0 flex-1">
                        <div className="flex items-start justify-between gap-x-4">
                          <div>
                            <h3 className="text-sm font-semibold text-gray-900 dark:text-white">
                              {session.current ? 'Aktuelle Sitzung' : 'Weitere Sitzung'}
                            </h3>

                            <p className="mt-1 text-sm text-gray-700 dark:text-gray-300">
                              {session.browser} auf {session.operatingSystem}
                            </p>

                            <p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
                              {session.deviceType}
                              {session.ipAddress ? ` · ${session.ipAddress}` : ''}
                            </p>

                            <p className="mt-1 text-xs text-gray-500 dark:text-gray-400">
                              Zuletzt aktiv:{' '}
                              {new Intl.DateTimeFormat('de-DE', {
                                dateStyle: 'short',
                                timeStyle: 'short',
                              }).format(new Date(session.lastSeenAt))}
                            </p>
                          </div>

                          {session.current ? (
                            <span className="inline-flex shrink-0 items-center gap-x-1.5 rounded-md bg-green-50 px-2 py-1 text-xs font-medium text-green-700 ring-1 ring-green-600/20 ring-inset dark:bg-green-900/30 dark:text-green-300 dark:ring-green-500/30">
                              <ShieldCheckIcon
                                aria-hidden="true"
                                className="size-3.5"
                              />
                              Aktiv
                            </span>
                          ) : (
                            <Button
                              type="button"
                              variant="secondary"
                              color="red"
                              size="sm"
                              isLoading={revokingSessionId === session.id}
                              onClick={() => void handleRevokeSession(session.id)}
                            >
                              Abmelden
                            </Button>
                          )}
                        </div>
                      </div>
                    </div>
                  </div>
                ))}
              </div>
            )}

            {!isLoadingSessions && sessions.length === 0 && (
              <div className="rounded-md border border-dashed border-gray-300 p-4 text-sm text-gray-500 dark:border-white/10 dark:text-gray-400">
                Keine aktiven Sitzungen gefunden.
              </div>
            )}

            <form
              onSubmit={handleLogoutSessionsSubmit}
              className="mt-8 border-t border-gray-200 pt-6 dark:border-white/10"
            >
              <label
                htmlFor="logout-password"
                className="block text-sm/6 font-medium text-gray-900 dark:text-white"
              >
                Dein Passwort
              </label>

              <div className="mt-2">
                <input
                  id="logout-password"
                  name="password"
                  type="password"
                  autoComplete="current-password"
                  required
                  className={inputClassName}
                />
              </div>

              <p className="mt-2 text-xs/5 text-gray-500 dark:text-gray-400">
                Dadurch werden alle anderen aktiven Sitzungen abgemeldet. Diese Sitzung bleibt aktiv.
              </p>

              <div className="mt-6 flex">
                <Button
                  type="submit"
                  color="indigo"
                  size="md"
                  isLoading={isLoggingOutSessions}
                >
                  Andere Sitzungen abmelden
                </Button>
              </div>
            </form>
          </div>
        </Section>

        <Section
          title="Konto löschen"
          description="Das Löschen deines Kontos kann nicht rückgängig gemacht werden."
        >
          <form onSubmit={handleDeleteKontoSubmit}>
            <div className="grid grid-cols-1 gap-x-6 gap-y-8 sm:max-w-xl sm:grid-cols-6">
              <div className="col-span-full">
                <label
                  htmlFor="delete-konto-password"
                  className="block text-sm/6 font-medium text-gray-900 dark:text-white"
                >
                  Dein Passwort
                </label>

                <div className="mt-2">
                  <input
                    id="delete-konto-password"
                    name="password"
                    type="password"
                    autoComplete="current-password"
                    required
                    className={inputClassName}
                  />
                </div>
              </div>
            </div>

            <div className="mt-8 flex">
              <Button
                type="submit"
                color="red"
                size="md"
                isLoading={isDeletingKonto}
              >
                Konto löschen
              </Button>
            </div>
          </form>
        </Section>
      </div>
    </>
  )
}
