/**
 * Detail view for a single source. URL: /sources/{tenantId}/{sourceId}.
 *
 * Layout:
 *   - Header: name + status badge + back link
 *   - Edit form (name, type, rate_limit, description, mtls_required)
 *   - Status actions: Suspend / Activate
 *   - Secrets card: shows "set" / "not set" per kind, with a
 *     "Rotate secrets" button that opens the same one-time
 *     modal as the create flow.
 *   - Cert card: placeholder for M14. Shows the mtls_required
 *     flag and a "Certs: coming in M14" hint.
 *   - JSONB inspection panel (allowed_targets, match_expr) for
 *     read-only debugging; editable in M13c routing rules.
 *   - Metadata panel: created, company_id, etc.
 */

import { useEffect, useState } from 'react';
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { z } from 'zod';
import { ArrowLeft, KeyRound, Lock, 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 { Checkbox } from '@/components/ui/checkbox';
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
} from '@/components/ui/dialog';

import {
  getErrorMessage,
  useRotateSourceSecrets,
  useSetSourceStatus,
  useSource,
  useUpdateSource,
} from './api';
import {
  JsonView,
  StatusBadge,
  formatDate,
  formatRateLimit,
  typeDescription,
  typeLabel,
} from './format';
import type { CreateOrRotateResponse, SecretsPayload, Source, SourceStatus, SourceType } from './types';

const formSchema = z.object({
  name: z.string().min(1, 'Name is required').max(200, 'Name must be \u2264 200 characters'),
  type: z.enum(['http', 'mqtt', 'ws', 'grpc']),
  rate_limit_per_sec: z
    .number({ invalid_type_error: 'Enter a number' })
    .int('Whole number only')
    .min(1)
    .max(1_000_000),
  description: z.string().max(500).optional().or(z.literal('')),
  mtls_required: z.boolean(),
});

const EMPTY_DEFAULTS = {
  name: '',
  type: 'http',
  rate_limit_per_sec: 1000,
  description: '',
  mtls_required: false,
};

export function SourceDetailPage() {
  const params = useParams();
  const navigate = useNavigate();
  const tenantId = params.id ?? '';
  const sourceId = params.sid ?? '';

  const sourceQ = useSource(tenantId, sourceId);
  const update = useUpdateSource(tenantId, sourceId);
  const setStatus = useSetSourceStatus(tenantId, sourceId);
  const rotate = useRotateSourceSecrets(tenantId, sourceId);

  const [secretsModal, setSecretsModal] = useState<CreateOrRotateResponse | null>(null);

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

  useEffect(() => {
    const s = sourceQ.data;
    if (!s) return;
    form.setValue('name', s.name);
    form.setValue('type', s.type as SourceType);
    form.setValue('rate_limit_per_sec', s.rate_limit_per_sec);
    form.setValue('description', s.description ?? '');
    form.setValue('mtls_required', s.mtls_required);
  }, [sourceQ.data, form]);

  const onSubmit = form.handleSubmit(async (values) => {
    try {
      await update.mutateAsync({
        name: values.name.trim(),
        type: values.type as SourceType,
        rate_limit_per_sec: values.rate_limit_per_sec,
        description: values.description ?? '',
        mtls_required: values.mtls_required,
      });
      toast.success('Source updated.');
    } catch (err) {
      toast.error(getErrorMessage(err));
    }
  });

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

  const onRotate = async () => {
    try {
      const resp = await rotate.mutateAsync();
      setSecretsModal(resp);
    } catch (err) {
      toast.error(getErrorMessage(err));
    }
  };

  if (sourceQ.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 source.</p>
        <p className="mt-1 text-xs opacity-80">
          {sourceQ.error instanceof Error ? sourceQ.error.message : 'Unknown error'}
        </p>
      </div>
    );
  }
  if (!sourceQ.data) {
    return (
      <div className="rounded-md border bg-card p-8 text-center text-sm text-muted-foreground">
        Loading…
      </div>
    );
  }
  const s = sourceQ.data;

  return (
    <div className="flex flex-col gap-4">
      <div className="flex items-center gap-2">
        <Button variant="ghost" size="sm" onClick={() => navigate(`/sources/${tenantId}`)}>
          <ArrowLeft className="mr-1 h-3 w-3" />
          Sources
        </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">
            <h1 className="text-2xl font-semibold tracking-tight">{s.name}</h1>
            <StatusBadge status={s.status} />
          </div>
          <p className="mt-1 text-sm text-muted-foreground">
            <code className="rounded bg-muted px-1.5 py-0.5 text-xs">{s.id}</code>
            <span className="mx-2">\u00b7</span>
            {typeLabel(s.type)} ({typeDescription(s.type)})
          </p>
        </div>
        <div className="flex items-center gap-2">
          {s.status === 'active' ? (
            <Button variant="outline" size="sm" onClick={() => onSetStatus('suspended')} disabled={setStatus.isPending}>
              Suspend
            </Button>
          ) : (
            <Button variant="outline" size="sm" onClick={() => onSetStatus('active')} disabled={setStatus.isPending}>
              Activate
            </Button>
          )}
        </div>
      </div>

      <Card>
        <CardHeader>
          <CardTitle>Configuration</CardTitle>
          <CardDescription>Edit the source's name, type, rate limit, and mTLS requirement.</CardDescription>
        </CardHeader>
        <CardContent>
          <form onSubmit={onSubmit} className="grid grid-cols-2 gap-3">
            <div className="col-span-2 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="col-span-1 flex flex-col gap-1">
              <Label htmlFor="d-type">Type</Label>
              <select
                id="d-type"
                className="h-9 rounded-md border border-input bg-background px-3 text-sm"
                {...form.register('type')}
              >
                <option value="http">HTTP</option>
                <option value="mqtt">MQTT</option>
                <option value="ws">WebSocket</option>
                <option value="grpc">gRPC</option>
              </select>
            </div>
            <div className="col-span-1 flex flex-col gap-1">
              <Label htmlFor="d-rl">Rate limit (/s)</Label>
              <Input
                id="d-rl"
                type="number"
                min={1}
                {...form.register('rate_limit_per_sec', { valueAsNumber: true })}
              />
            </div>
            <div className="col-span-2 flex flex-col gap-1">
              <Label htmlFor="d-desc">Description</Label>
              <Textarea id="d-desc" rows={2} {...form.register('description')} />
            </div>
            <div className="col-span-2 flex items-center gap-2 pt-1">
              <Checkbox id="d-mtls" {...form.register('mtls_required')} />
              <Label htmlFor="d-mtls" className="cursor-pointer">
                Require mTLS client cert (M14)
              </Label>
            </div>
            <div className="col-span-2 flex items-center gap-2 pt-2">
              <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" />
            Secrets
          </CardTitle>
          <CardDescription>
            The plaintext values are not stored — only their bcrypt hashes. Use
            rotate to generate new values (returned once).
          </CardDescription>
        </CardHeader>
        <CardContent>
          <div className="grid grid-cols-2 gap-3 text-sm">
            <SecretIndicator label="HMAC secret" set={s.hmac_set} />
            <SecretIndicator label="API key" set={s.api_key_set} />
          </div>
          <div className="mt-4 flex items-center gap-2">
            <Button variant="outline" size="sm" onClick={onRotate} disabled={rotate.isPending}>
              {rotate.isPending ? 'Rotating…' : 'Rotate both secrets'}
            </Button>
            <span className="text-xs text-muted-foreground">
              Old values are invalidated immediately. The source's clients must
              be updated to the new values within their cache TTL.
            </span>
          </div>
        </CardContent>
      </Card>

      <Card>
        <CardHeader>
          <CardTitle className="flex items-center gap-2">
            <ShieldCheck className="h-4 w-4" />
            mTLS / Client cert
          </CardTitle>
          <CardDescription>
            M14 will wire up client cert provisioning, rotation, and revocation
            for sources with <code>mtls_required</code> = true.
          </CardDescription>
        </CardHeader>
        <CardContent>
          <div className="text-sm text-muted-foreground">
            <p>
              <strong className="text-foreground">Currently:</strong>{' '}
              {s.mtls_required ? (
                <span>mTLS is required. Cert lifecycle: <em>coming in M14.</em></span>
              ) : (
                <span>Not required. Toggle the flag above when you want to enforce client certs.</span>
              )}
            </p>
          </div>
        </CardContent>
      </Card>

      <Card>
        <CardHeader>
          <CardTitle className="flex items-center gap-2">
            <Lock className="h-4 w-4" />
            Routing (read-only in W2)
          </CardTitle>
          <CardDescription>
            M13c adds the routing-rule editor. W2 only shows the current
            allowed_targets and match_expr as JSONB.
          </CardDescription>
        </CardHeader>
        <CardContent className="grid grid-cols-2 gap-3">
          <div className="flex flex-col gap-1">
            <Label>Allowed targets</Label>
            <JsonView value={s.allowed_targets} />
          </div>
          <div className="flex flex-col gap-1">
            <Label>Match expression</Label>
            <JsonView value={s.match_expr} />
          </div>
        </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">Source ID</dt>
            <dd><code className="rounded bg-muted px-1.5 py-0.5 text-xs">{s.id}</code></dd>
            <dt className="text-muted-foreground">Company</dt>
            <dd><code className="rounded bg-muted px-1.5 py-0.5 text-xs">{s.company_id}</code></dd>
            <dt className="text-muted-foreground">Type</dt>
            <dd>{typeLabel(s.type)}</dd>
            <dt className="text-muted-foreground">Rate limit</dt>
            <dd>{formatRateLimit(s.rate_limit_per_sec)}</dd>
            <dt className="text-muted-foreground">Created</dt>
            <dd>{formatDate(s.created_at)}</dd>
          </dl>
        </CardContent>
      </Card>

      <Dialog open={Boolean(secretsModal)} onOpenChange={(open) => !open && setSecretsModal(null)}>
        <DialogContent className="sm:max-w-lg">
          <DialogHeader>
            <DialogTitle className="flex items-center gap-2">
              <ShieldCheck className="h-5 w-5 text-amber-500" />
              New secrets — save them now
            </DialogTitle>
            <DialogDescription>
              Rotation succeeded. The new HMAC secret and API key are shown
              <strong> once</strong>. After you close this dialog, only the
              hashes are stored.
            </DialogDescription>
          </DialogHeader>
          {secretsModal ? <RotatedSecretsView secrets={secretsModal.secrets} /> : null}
          <DialogFooter>
            <Button onClick={() => setSecretsModal(null)}>I have saved these — close</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </div>
  );
}

function SecretIndicator({ label, set }: { label: string; set: boolean }) {
  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">{set ? 'Configured' : 'Not set'}</span>
      </div>
      <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'}
      />
    </div>
  );
}

function RotatedSecretsView({ secrets }: { secrets: SecretsPayload | undefined }) {
  const [showHmac, setShowHmac] = useState(false);
  const [showApi, setShowApi] = useState(false);
  return (
    <div className="flex flex-col gap-3">
      <SecretRow
        label="New HMAC secret"
        value={secrets?.hmac_secret ?? ''}
        show={showHmac}
        onToggle={() => setShowHmac((v) => !v)}
      />
      <SecretRow
        label="New API key"
        value={secrets?.api_key ?? ''}
        show={showApi}
        onToggle={() => setShowApi((v) => !v)}
      />
    </div>
  );
}

function SecretRow({ label, value, show, onToggle }: { label: string; value: string; show: boolean; onToggle: () => void }) {
  const [copied, setCopied] = useState(false);
  return (
    <div className="flex flex-col gap-1">
      <div className="flex items-center justify-between">
        <span className="text-xs font-medium text-muted-foreground">{label}</span>
        <div className="flex items-center gap-1">
          <Button type="button" size="sm" variant="ghost" onClick={onToggle}>
            {show ? 'Hide' : 'Show'}
          </Button>
          <Button
            type="button"
            size="sm"
            variant="ghost"
            onClick={async () => {
              try {
                await navigator.clipboard.writeText(value);
                setCopied(true);
                setTimeout(() => setCopied(false), 1500);
              } catch {
                // ignore
              }
            }}
          >
            {copied ? 'Copied' : 'Copy'}
          </Button>
        </div>
      </div>
      <pre className="overflow-x-auto rounded-md border bg-muted/40 p-2 text-xs leading-relaxed">
        <code>{show ? value : '\u2022'.repeat(Math.min(value.length || 8, 32))}</code>
      </pre>
    </div>
  );
}
