import { Link, router, usePage } from '@inertiajs/react'
import { Ban, CheckCircle2, Clock3, Plus, Search, UserRoundX, UsersRound } from 'lucide-react'
import type { FormEvent } from 'react'
import { useMemo, useState } from 'react'

import { ConfirmDestructive } from '@/components/ConfirmDestructive'
import { DataTable, createDataTableColumns, tableUrl } from '@/components/DataTable'
import type { DataTableColumn, TablePage } from '@/components/DataTable'
import { DataTimestamp } from '@/components/DataTimestamp'
import { FilterBar } from '@/components/FilterBar'
import { FilterSelect } from '@/components/FilterSelect'
import { FormDialog } from '@/components/FormDialog'
import { SecretRevealDialog } from '@/components/SecretRevealDialog'
import { StatusBadge } from '@/components/StatusBadge'
import { Button } from '@/components/ui/button'
import { Input } from '@/components/ui/input'
import {
  Select,
  SelectContent,
  SelectItem,
  SelectTrigger,
  SelectValue,
} from '@/components/ui/select'
import { AppLayout } from '@/layouts/AppLayout'
import { t } from '@/lib/i18n'
import { formatAbsolute } from '@/lib/format'
import { route } from '@/lib/routes'

type Role = 'ADMIN' | 'VIEWER'

interface UserRow {
  id: string
  email: string
  role: Role
  is_active: boolean
  status: 'ACTIVE' | 'INVITED' | 'INACTIVE'
  created_at: string
}

interface Props {
  users: TablePage<UserRow>
}

const columnHelper = createDataTableColumns<UserRow>()

export default function Users({ users }: Props) {
  const { auth } = usePage().props
  const [query, setQuery] = useState(users.filters.query ?? '')
  const [pendingRoleId, setPendingRoleId] = useState<string | null>(null)
  const [toDeactivate, setToDeactivate] = useState<UserRow | null>(null)
  const [deactivating, setDeactivating] = useState(false)
  const [inviteEmail, setInviteEmail] = useState('')
  const [inviteRole, setInviteRole] = useState<Role>('VIEWER')
  const [inviting, setInviting] = useState(false)
  const [inviteError, setInviteError] = useState('')
  const [createdInvite, setCreatedInvite] = useState<{ email: string; invite_url: string; expires_at: string } | null>(null)

  const changeRole = (user: UserRow, role: Role) => {
    if (role === user.role) return
    router.post(
      route('user.role', { account_id: user.id }),
      { role },
      {
        preserveScroll: true,
        onStart: () => setPendingRoleId(user.id),
        onFinish: () => setPendingRoleId(null),
      }
    )
  }

  const columns = useMemo<DataTableColumn<UserRow>[]>(
    () => [
      columnHelper.accessor('email', {
        id: 'email',
        header: t('users.column.email'),
        cell: (info) => (
          <span translate="no" className="font-medium">
            {info.getValue()}
          </span>
        ),
      }),
      columnHelper.accessor('role', {
        id: 'role',
        header: t('users.column.role'),
        cell: (info) => {
          const user = info.row.original
          const busy = pendingRoleId === user.id
          return (
            <Select
              value={user.role}
              onValueChange={(value) => changeRole(user, value as Role)}
              disabled={busy}
            >
              <SelectTrigger
                size="sm"
                aria-label={t('users.role.changeFor', { email: user.email })}
                aria-busy={busy}
              >
                <SelectValue />
              </SelectTrigger>
              <SelectContent>
                <SelectItem value="ADMIN">{t('users.role.admin')}</SelectItem>
                <SelectItem value="VIEWER">{t('users.role.viewer')}</SelectItem>
              </SelectContent>
            </Select>
          )
        },
      }),
      columnHelper.accessor('status', {
        id: 'status',
        header: t('users.column.status'),
        cell: (info) =>
          info.getValue() === 'ACTIVE' ? (
            <StatusBadge tone="positive" icon={CheckCircle2}>
              {t('users.status.active')}
            </StatusBadge>
          ) : info.getValue() === 'INVITED' ? (
            <StatusBadge tone="attention" icon={Clock3}>
              {t('users.status.invited')}
            </StatusBadge>
          ) : (
            <StatusBadge tone="neutral" icon={Ban}>
              {t('users.status.inactive')}
            </StatusBadge>
          ),
      }),
      columnHelper.accessor('created_at', {
        id: 'created',
        header: t('users.column.created'),
        cell: (info) => <DataTimestamp value={info.getValue()} />,
      }),
      columnHelper.display({
        id: 'actions',
        header: t('users.column.actions'),
        cell: (info) => {
          const user = info.row.original
          if (!user.is_active || user.id === auth?.id) return null
          return (
            <Button variant="destructive" size="sm" onClick={() => setToDeactivate(user)}>
              <UserRoundX aria-hidden />
              {t('users.deactivate.action')}
            </Button>
          )
        },
      }),
    ],
    [auth?.id, pendingRoleId]
  )

  const search = (event: FormEvent) => {
    event.preventDefault()
    router.get(
      tableUrl(users, { query: query.trim() || null, page: null }),
      {},
      { preserveScroll: true, preserveState: true }
    )
  }

  const deactivate = () => {
    if (!toDeactivate) return
    router.post(
      route('user.deactivate', { account_id: toDeactivate.id }),
      {},
      {
        preserveScroll: true,
        onStart: () => setDeactivating(true),
        onFinish: () => setDeactivating(false),
        onSuccess: () => setToDeactivate(null),
      }
    )
  }

  const hasFilters = Object.values(users.filters).some(Boolean)

  const createInvite = async (event: FormEvent) => {
    event.preventDefault()
    setInviteError('')
    setInviting(true)
    try {
      const response = await fetch(route('user.invite'), {
        method: 'POST',
        credentials: 'same-origin',
        headers: { 'Content-Type': 'application/json', 'X-CSRFToken': csrfToken() },
        body: JSON.stringify({ email: inviteEmail.trim(), role: inviteRole }),
      })
      const body = await response.json()
      if (!response.ok) {
        setInviteError(t(`users.invite.error.${body.error?.code ?? 'UNKNOWN'}` as Parameters<typeof t>[0]))
        return
      }
      setCreatedInvite(body)
      setInviteEmail('')
      setInviteRole('VIEWER')
      router.reload({ only: ['users'] })
    } catch {
      setInviteError(t('users.invite.error.NETWORK'))
    } finally {
      setInviting(false)
    }
  }

  return (
    <AppLayout
      title={t('users.title')}
      description={t('users.description')}
      actions={
        <Button asChild>
          <Link href={tableUrl(users, { invite: 1 })} preserveScroll preserveState>
            <Plus aria-hidden />
            {t('users.invite.action')}
          </Link>
        </Button>
      }
    >
      <FilterBar clearHref={hasFilters ? users.path : undefined}>
        <form onSubmit={search} className="flex flex-col gap-1.5">
          <label htmlFor="user-search" className="text-muted-foreground text-xs">
            {t('users.search.label')}
          </label>
          <div className="flex gap-2">
            <Input
              id="user-search"
              value={query}
              onChange={(event) => setQuery(event.target.value)}
              placeholder={t('users.search.placeholder')}
              className="h-8 w-64"
            />
            <Button type="submit" size="sm" variant="outline">
              <Search aria-hidden />
              {t('users.search.action')}
            </Button>
          </div>
        </form>
        <FilterSelect
          label={t('users.filter.role')}
          param="role"
          table={users}
          allLabel={t('table.all')}
          options={[
            { value: 'admin', label: t('users.role.admin') },
            { value: 'viewer', label: t('users.role.viewer') },
          ]}
        />
        <FilterSelect
          label={t('users.filter.status')}
          param="status"
          table={users}
          allLabel={t('table.all')}
          options={[
            { value: 'active', label: t('users.status.active') },
            { value: 'invited', label: t('users.status.invited') },
            { value: 'inactive', label: t('users.status.inactive') },
          ]}
        />
      </FilterBar>

      <DataTable
        data={users}
        columns={columns}
        unit={t('users.unit')}
        unitSingular={t('users.unitSingular')}
        caption={t('users.caption')}
        rowId={(user) => user.id}
        empty={{
          icon: UsersRound,
          title: t('users.empty.title'),
          description: hasFilters ? t('users.empty.filtered') : t('users.empty.description'),
        }}
      />

      {toDeactivate ? (
        <ConfirmDestructive
          open
          onOpenChange={(open) => {
            if (!open) setToDeactivate(null)
          }}
          title={t('users.deactivate.title', { email: toDeactivate.email })}
          consequence={t('users.deactivate.consequence')}
          confirmLabel={t('users.deactivate.confirm')}
          busyLabel={t('users.deactivate.busy')}
          busy={deactivating}
          onConfirm={deactivate}
        />
      ) : null}

      <FormDialog
        param="invite"
        title={t('users.invite.title')}
        description={t('users.invite.description')}
        submitLabel={t('users.invite.confirm')}
        pendingLabel={t('users.invite.pending')}
        pending={inviting}
        onSubmit={createInvite}
      >
        <div className="grid gap-2">
          <label htmlFor="invite-email" className="text-sm font-medium">{t('users.column.email')}</label>
          <Input id="invite-email" type="email" autoComplete="off" value={inviteEmail} onChange={(event) => setInviteEmail(event.target.value)} required />
        </div>
        <div className="grid gap-2">
          <label htmlFor="invite-role" className="text-sm font-medium">{t('users.column.role')}</label>
          <Select value={inviteRole} onValueChange={(value) => setInviteRole(value as Role)}>
            <SelectTrigger id="invite-role" className="w-full"><SelectValue /></SelectTrigger>
            <SelectContent>
              <SelectItem value="ADMIN">{t('users.role.admin')}</SelectItem>
              <SelectItem value="VIEWER">{t('users.role.viewer')}</SelectItem>
            </SelectContent>
          </Select>
        </div>
        <p className="text-muted-foreground text-sm">{t('users.invite.expires')}</p>
        {inviteError ? <p role="alert" className="text-destructive text-sm">{inviteError}</p> : null}
      </FormDialog>

      {createdInvite ? (
        <SecretRevealDialog
          open
          title={t('users.invite.created.title')}
          description={t('users.invite.created.description', { email: createdInvite.email })}
          value={createdInvite.invite_url}
          valueLabel={t('users.invite.link')}
          footnote={t('users.invite.created.footnote', { expires: formatAbsolute(createdInvite.expires_at, 'America/New_York') })}
          onDismiss={() => {
            setCreatedInvite(null)
            router.get(users.path, {}, { replace: true })
          }}
        />
      ) : null}
    </AppLayout>
  )
}

function csrfToken(): string {
  const entry = document.cookie.split('; ').find((chunk) => chunk.startsWith('csrftoken='))
  return entry ? decodeURIComponent(entry.slice('csrftoken='.length)) : ''
}
