'use client';

import { useState } from 'react';
import { useQuery } from 'convex/react';
import { KeyRound, Loader2, UserRound } from 'lucide-react';
import { toast } from 'sonner';

import { api } from '@spoon/backend/convex/_generated/api.js';
import {
  Button,
  Card,
  CardContent,
  CardHeader,
  CardTitle,
  Input,
} from '@spoon/ui';

// Mirrors the worker's linuxUsername (runtime/box-user.ts) so the identity
// line shows the actual passwd entry.
const linuxUsername = (spoonUsername: string): string => {
  const cleaned = spoonUsername
    .toLowerCase()
    .replaceAll(/[^a-z0-9_-]/g, '-')
    .slice(0, 32);
  if (!cleaned) return 'spoon';
  return /^[a-z_]/.test(cleaned) ? cleaned : `u${cleaned}`.slice(0, 32);
};

/**
 * The box's Linux account: identity (user@host) and the user-settable
 * password. Password is write-only — the UI only ever knows whether one is
 * set. Saving stores it (encrypted) and applies it to a running box live;
 * clearing reverts sudo to passwordless.
 */
export const BoxUserCard = ({ username }: { username: string }) => {
  const passwordState = useQuery(api.boxSettings.hasMine, {});
  const hasPassword = passwordState?.hasPassword ?? false;
  const user = linuxUsername(username || 'spoon');

  const [password, setPassword] = useState('');
  const [confirm, setConfirm] = useState('');
  const [saving, setSaving] = useState(false);
  const [pendingRestart, setPendingRestart] = useState(false);

  const tooShort = password.length > 0 && password.length < 8;
  const mismatch = confirm.length > 0 && confirm !== password;
  const canSave =
    !saving && password.length >= 8 && password.length <= 128 && !mismatch;

  const submit = (next: string | null) => {
    setSaving(true);
    void (async () => {
      try {
        const response = await fetch('/api/box/set-password', {
          method: 'POST',
          body: JSON.stringify({ password: next }),
        });
        if (!response.ok) throw new Error(await response.text());
        const data = (await response.json()) as {
          hasPassword: boolean;
          applied: boolean;
        };
        setPassword('');
        setConfirm('');
        setPendingRestart(!data.applied);
        toast.success(
          next === null ? 'Password removed.' : 'Password updated.',
        );
      } catch (error) {
        console.error(error);
        toast.error('Could not update the box password.');
      } finally {
        setSaving(false);
      }
    })();
  };

  return (
    <Card>
      <CardHeader>
        <CardTitle className='flex items-center gap-2 text-base'>
          <UserRound className='size-4' />
          Box user
        </CardTitle>
      </CardHeader>
      <CardContent className='space-y-4'>
        <div className='flex flex-wrap items-center gap-x-6 gap-y-2'>
          <div>
            <p className='text-muted-foreground text-[11px] font-medium tracking-wide uppercase'>
              Identity
            </p>
            <p className='font-mono text-sm'>
              {user}@{user}-box
            </p>
          </div>
          <div>
            <p className='text-muted-foreground text-[11px] font-medium tracking-wide uppercase'>
              Password
            </p>
            <p className='text-sm'>{hasPassword ? 'Set' : 'Not set'}</p>
          </div>
        </div>

        <p className='text-muted-foreground text-xs'>
          Without a password, <code className='font-mono'>sudo</code> works
          without prompting. Once you set one, sudo asks for it — like a normal
          machine.
        </p>

        <form
          className='flex flex-wrap items-end gap-2'
          onSubmit={(event) => {
            event.preventDefault();
            if (canSave) submit(password);
          }}
        >
          <div className='space-y-1'>
            <label
              className='text-muted-foreground text-xs'
              htmlFor='box-password'
            >
              New password
            </label>
            <Input
              id='box-password'
              type='password'
              autoComplete='new-password'
              value={password}
              onChange={(event) => setPassword(event.target.value)}
              className='w-56'
            />
          </div>
          <div className='space-y-1'>
            <label
              className='text-muted-foreground text-xs'
              htmlFor='box-password-confirm'
            >
              Confirm
            </label>
            <Input
              id='box-password-confirm'
              type='password'
              autoComplete='new-password'
              value={confirm}
              onChange={(event) => setConfirm(event.target.value)}
              className='w-56'
            />
          </div>
          <Button type='submit' size='sm' disabled={!canSave}>
            {saving ? (
              <Loader2 className='size-4 animate-spin' />
            ) : (
              <KeyRound className='size-4' />
            )}
            {hasPassword ? 'Change password' : 'Set password'}
          </Button>
          {hasPassword ? (
            <Button
              type='button'
              variant='outline'
              size='sm'
              disabled={saving}
              onClick={() => submit(null)}
            >
              Remove password
            </Button>
          ) : null}
        </form>

        {tooShort ? (
          <p className='text-destructive text-xs'>
            Password must be at least 8 characters.
          </p>
        ) : null}
        {mismatch ? (
          <p className='text-destructive text-xs'>Passwords do not match.</p>
        ) : null}
        {pendingRestart ? (
          <p className='text-muted-foreground text-xs'>
            Saved — your machine is not running, so it takes effect the next
            time it starts.
          </p>
        ) : null}
      </CardContent>
    </Card>
  );
};
