/**
 * Detail view for a single telegram bot. URL: /telegram/{tenantId}/{botId}.
 *
 * Layout:
 *   - Header: name + status badge + back link
 *   - Edit form (name, welcome_message, default_source_id, description)
 *   - Status actions: Pause / Activate
 *   - Bot token card: shows "Configured" / "Not set", with a
 *     "Rotate token" button that opens a one-time modal. The
 *     operator pastes a new token from @BotFather; the server
 *     stores it and bcrypt-hashes it. The plaintext is never
 *     echoed back; the operator already has it.
 *   - Metadata panel: created, last_seen, last_rotated, etc.
 */

import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { ArrowLeft, Eye, EyeOff, KeyRound, Send, ShieldCheck } from 'lucide-react';
import { toast } from 'sonner';
import { useNavigate, useParams } from 'react-router-dom';

import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Textarea } from '@/components/ui/textarea';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';

import {
  getErrorMessage,
  useRotateTelegramBotToken,
  useSetTelegramBotStatus,
  useTelegramBot,
  useUpdateTelegramBot,
} from './api';
import {
  StatusBadge,
  TokenSetBadge,
  formatDate,
  formatDateTime,
} from './format';
import type { TelegramBot, TelegramBotStatus } from './types';

const ID_RE = /^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$/;
const TOKEN_RE = /^\d+:[A-Za-z0-9_-]{35,}$/;

const formSchema = z.object({
  name: z.string().min(1, 'Name is required').max(200, 'Name must be \u2264 200 characters'),
  welcome_message: z
    .string()
    .max(4096, 'Welcome message must be \u2264 4096 characters')
    .optional()
    .or(z.literal('')),
  default_source_id: z
    .string()
    .regex(ID_RE, 'Lowercase letters, digits, and dashes only')
    .optional()
    .or(z.literal('')),
  description: z
    .string()
    .max(500, 'Description must be \u2264 500 characters')
    .optional()
    .or(z.literal('')),
});

const EMPTY_DEFAULTS = {
  name: '',
  welcome_message: '',
  default_source_id: '',
  description: '',
};

export function TelegramBotDetailPage() {
  const params = useParams();
  const navigate = useNavigate();
  const tenantId = params.id ?? '';
  const botId = params.bid ?? '';

  const botQ = useTelegramBot(tenantId, botId);
  const update = useUpdateTelegramBot(tenantId, botId);
  const setStatus = useSetTelegramBotStatus(tenantId, botId);
  const rotate = useRotateTelegramBotToken(tenantId, botId);

  const [rotateOpen, setRotateOpen] = useState(false);

  const form = useForm({
    resolver: zodResolver(formSchema),
    defaultValues: EMPTY_DEFAULTS,
  });

  useEffect(() => {
    const b = botQ.data;
    if (!b) return;
    form.setValue('name', b.name);
    form.setValue('welcome_message', b.welcome_message ?? '');
    form.setValue('default_source_id', b.default_source_id ?? '');
    form.setValue('description', b.description ?? '');
  }, [botQ.data, form]);

  const onSubmit = form.handleSubmit(async (values) => {
    try {
      await update.mutateAsync({
        name: values.name.trim(),
        welcome_message: values.welcome_message || undefined,
        default_source_id: values.default_source_id || undefined,
        description: values.description || undefined,
      });
      toast.success('Bot updated.');
    } catch (err) {
      toast.error(getErrorMessage(err));
    }
  });

  const onSetStatus = async (next: TelegramBotStatus) => {
    try {
      await setStatus.mutateAsync(next);
      toast.success(`Bot ${next}.`);
    } catch (err) {
      toast.error(getErrorMessage(err));
    }
  };

  if (botQ.isError) {
    return (
      <div className="rounded-md border border-destructive/40 bg-destructive/5 p-4 text-sm text-destructive">
        <p className="font-medium">Failed to load bot.</p>
        <p className="mt-1 text-xs opacity-80">
          {botQ.error instanceof Error ? botQ.error.message : 'Unknown error'}
        </p>
      </div>
    );
  }
  if (!botQ.data) {
    return (
      <div className="rounded-md border bg-card p-8 text-center text-sm text-muted-foreground">
        Loading…
      </div>
    );
  }
  const b = botQ.data;

  return (
    <div className="flex flex-col gap-4">
      <div className="flex items-center gap-2">
        <Button variant="ghost" size="sm" onClick={() => navigate(`/telegram/${tenantId}`)}>
          <ArrowLeft className="mr-1 h-3 w-3" />
          Telegram bots
        </Button>
      </div>
      <div className="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
        <div>
          <div className="flex items-center gap-2">
            <Send className="h-5 w-5 text-muted-foreground" />
            <h1 className="text-2xl font-semibold tracking-tight">{b.name}</h1>
            <StatusBadge status={b.status} />
          </div>
          <p className="mt-1 text-sm text-muted-foreground">
            <code className="rounded bg-muted px-1.5 py-0.5 text-xs">{b.id}</code>
            <span className="mx-2">\u00b7</span>
            company
            <code className="ml-1 rounded bg-muted px-1.5 py-0.5 text-xs">{b.company_id}</code>
          </p>
        </div>
        <div className="flex items-center gap-2">
          {b.status === 'active' ? (
            <Button
              variant="outline"
              size="sm"
              onClick={() => onSetStatus('paused')}
              disabled={setStatus.isPending}
            >
              Pause
            </Button>
          ) : (
            <Button
              variant="outline"
              size="sm"
              onClick={() => onSetStatus('active')}
              disabled={setStatus.isPending}
            >
              Activate
            </Button>
          )}
        </div>
      </div>

      <Card>
        <CardHeader>
          <CardTitle>Configuration</CardTitle>
          <CardDescription>
            Edit the bot's name, welcome message, default source, and description.
          </CardDescription>
        </CardHeader>
        <CardContent>
          <form onSubmit={onSubmit} className="flex flex-col gap-3">
            <div className="flex flex-col gap-1">
              <Label htmlFor="d-name">Name</Label>
              <Input id="d-name" {...form.register('name')} />
              {form.formState.errors.name ? (
                <p className="text-xs text-destructive">{form.formState.errors.name.message}</p>
              ) : null}
            </div>
            <div className="grid grid-cols-2 gap-3">
              <div className="col-span-1 flex flex-col gap-1">
                <Label htmlFor="d-default-source">Default source ID</Label>
                <Input
                  id="d-default-source"
                  placeholder="primary"
                  {...form.register('default_source_id')}
                />
                {form.formState.errors.default_source_id ? (
                  <p className="text-xs text-destructive">
                    {form.formState.errors.default_source_id.message}
                  </p>
                ) : null}
              </div>
              <div className="col-span-1 flex flex-col gap-1">
                <Label htmlFor="d-desc">Description</Label>
                <Input id="d-desc" {...form.register('description')} />
                {form.formState.errors.description ? (
                  <p className="text-xs text-destructive">
                    {form.formState.errors.description.message}
                  </p>
                ) : null}
              </div>
            </div>
            <div className="flex flex-col gap-1">
              <Label htmlFor="d-welcome">Welcome message</Label>
              <Textarea id="d-welcome" rows={3} {...form.register('welcome_message')} />
              <p className="text-xs text-muted-foreground">
                Sent in response to /start. M13c wires this into telegramd.
              </p>
              {form.formState.errors.welcome_message ? (
                <p className="text-xs text-destructive">
                  {form.formState.errors.welcome_message.message}
                </p>
              ) : null}
            </div>
            <div className="flex items-center gap-2 pt-1">
              <Button type="submit" disabled={update.isPending}>
                {update.isPending ? 'Saving…' : 'Save changes'}
              </Button>
            </div>
          </form>
        </CardContent>
      </Card>

      <Card>
        <CardHeader>
          <CardTitle className="flex items-center gap-2">
            <KeyRound className="h-4 w-4" />
            Bot token
          </CardTitle>
          <CardDescription>
            The plaintext token is never returned after save. The server stores
            the hash so it can render "configured"; telegramd reads the
            plaintext from the same row. Use rotate to swap to a new token
            (e.g. after revoking the old one in @BotFather).
          </CardDescription>
        </CardHeader>
        <CardContent>
          <div className="grid grid-cols-2 gap-3 text-sm">
            <SecretIndicator label="Bot token" set={b.bot_token_set} />
            <SecretIndicator label="Last rotated" text={formatDateTime(b.last_rotated_at)} />
          </div>
          <div className="mt-4 flex items-center gap-2">
            <Button
              variant="outline"
              size="sm"
              onClick={() => setRotateOpen(true)}
              disabled={rotate.isPending}
            >
              {rotate.isPending ? 'Rotating…' : 'Rotate token'}
            </Button>
            <span className="text-xs text-muted-foreground">
              The new token is set on the row immediately. telegramd picks it
              up on its next reload (or sooner with a future notify channel).
            </span>
          </div>
        </CardContent>
      </Card>

      <Card>
        <CardHeader>
          <CardTitle className="flex items-center gap-2">
            <ShieldCheck className="h-4 w-4" />
            Security notes
          </CardTitle>
        </CardHeader>
        <CardContent className="text-sm text-muted-foreground">
          <ul className="ml-5 list-disc space-y-1">
            <li>
              Bot tokens are write-only. The server never returns the plaintext
              — only a <code>bot_token_set</code> boolean.
            </li>
            <li>
              The plaintext is stored alongside its bcrypt hash so telegramd
              can use it. The M11 security milestone will replace this with
              AES-256-GCM encryption and a decryption sidecar.
            </li>
            <li>
              Every state change (create, update, status, rotate) writes an
              audit row visible in the Audit log.
            </li>
          </ul>
        </CardContent>
      </Card>

      <Card>
        <CardHeader>
          <CardTitle>Metadata</CardTitle>
        </CardHeader>
        <CardContent>
          <dl className="grid grid-cols-2 gap-x-6 gap-y-2 text-sm">
            <dt className="text-muted-foreground">Bot ID</dt>
            <dd>
              <code className="rounded bg-muted px-1.5 py-0.5 text-xs">{b.id}</code>
            </dd>
            <dt className="text-muted-foreground">Company</dt>
            <dd>
              <code className="rounded bg-muted px-1.5 py-0.5 text-xs">{b.company_id}</code>
            </dd>
            <dt className="text-muted-foreground">Status</dt>
            <dd>
              <StatusBadge status={b.status} /> <TokenSetBadge set={b.bot_token_set} />
            </dd>
            <dt className="text-muted-foreground">Last rotated</dt>
            <dd>{formatDateTime(b.last_rotated_at)}</dd>
            <dt className="text-muted-foreground">Last seen</dt>
            <dd>{formatDateTime(b.last_seen_at)}</dd>
            <dt className="text-muted-foreground">Created</dt>
            <dd>{formatDate(b.created_at)}</dd>
            <dt className="text-muted-foreground">Updated</dt>
            <dd>{formatDateTime(b.updated_at)}</dd>
          </dl>
        </CardContent>
      </Card>

      <RotateTokenDialog
        open={rotateOpen}
        onOpenChange={setRotateOpen}
        onSubmit={async (token) => {
          try {
            await rotate.mutateAsync({ bot_token: token });
            toast.success('Token rotated.');
            setRotateOpen(false);
          } catch (err) {
            toast.error(getErrorMessage(err));
          }
        }}
        isPending={rotate.isPending}
      />
    </div>
  );
}

function SecretIndicator({
  label,
  set,
  text,
}: {
  label: string;
  set?: boolean;
  text?: string;
}) {
  return (
    <div className="flex items-center justify-between rounded-md border bg-muted/20 p-3">
      <div className="flex flex-col">
        <span className="text-xs text-muted-foreground">{label}</span>
        <span className="text-sm font-medium">{text ?? (set ? 'Configured' : 'Not set')}</span>
      </div>
      {set !== undefined ? (
        <span
          className={
            set
              ? 'inline-flex h-2 w-2 rounded-full bg-emerald-500'
              : 'inline-flex h-2 w-2 rounded-full bg-amber-400'
          }
          aria-label={set ? 'set' : 'not set'}
        />
      ) : null}
    </div>
  );
}

function RotateTokenDialog({
  open,
  onOpenChange,
  onSubmit,
  isPending,
}: {
  open: boolean;
  onOpenChange: (v: boolean) => void;
  onSubmit: (token: string) => Promise<void>;
  isPending: boolean;
}) {
  const [token, setToken] = useState('');
  const [show, setShow] = useState(false);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (open) {
      setToken('');
      setShow(false);
      setError(null);
    }
  }, [open]);

  const submit = async () => {
    const t = token.trim();
    if (!TOKEN_RE.test(t)) {
      setError('Token must look like 12345678:AbCdEfGh... (35+ chars after the colon).');
      return;
    }
    setError(null);
    await onSubmit(t);
  };

  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent className="sm:max-w-lg">
        <DialogHeader>
          <DialogTitle className="flex items-center gap-2">
            <ShieldCheck className="h-5 w-5 text-amber-500" />
            Rotate bot token
          </DialogTitle>
          <DialogDescription>
            Paste the new token from <strong>@BotFather</strong>. The server
            stores the plaintext (so telegramd can use it) and bcrypt-hashes
            it for the audit-friendly <code>bot_token_hash</code> column. The
            plaintext is <strong>never</strong> echoed back.
          </DialogDescription>
        </DialogHeader>
        <div className="flex flex-col gap-2">
          <Label htmlFor="rotate-token" className="text-xs">
            New bot token
          </Label>
          <div className="flex items-center gap-2">
            <Input
              id="rotate-token"
              type={show ? 'text' : 'password'}
              autoComplete="off"
              spellCheck={false}
              placeholder="123456789:AbCdEfGhIjKlMnOpQrStUvWxYz-12345"
              value={token}
              onChange={(e) => setToken(e.target.value)}
            />
            <Button
              type="button"
              variant="ghost"
              size="sm"
              onClick={() => setShow((v) => !v)}
              aria-label={show ? 'Hide' : 'Show'}
            >
              {show ? <EyeOff className="h-3 w-3" /> : <Eye className="h-3 w-3" />}
            </Button>
          </div>
          {error ? <p className="text-xs text-destructive">{error}</p> : null}
        </div>
        <DialogFooter>
          <Button type="button" variant="ghost" onClick={() => onOpenChange(false)}>
            Cancel
          </Button>
          <Button type="button" onClick={submit} disabled={isPending}>
            {isPending ? 'Rotating…' : 'Rotate token'}
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

// referenced in the props below to keep tsc happy if we ever
// drop the import; do not remove.
export type _TelegramBotRef = TelegramBot;
