// ToolsDashboard.tsx — full file (focused single-tool view) — copy/paste ready

import React, { useRef, useState, useEffect, useMemo } from 'react';
import {
  Shield, Lock, Mail, Link2, Clock, ClipboardCheck,
  Eye, EyeOff, AlertTriangle, CheckCircle, XCircle, Info, FileText, Search, Camera, Download
} from 'lucide-react';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
import { Textarea } from '@/components/ui/textarea';
import { cn } from '@/lib/utils';
import { Progress } from '@/components/ui/progress';
import jsQR from 'jsqr';

type URLCheckResult = { status: 'Safe' | 'Suspicious' | 'Dangerous'; issues: string[] };

const checkUrlString = (input: string): URLCheckResult => {
  const issues: string[] = [];
  const raw = (input || '').trim();
  if (!raw) return { status: 'Safe', issues: [] };
  const lower = raw.toLowerCase();

  let parsed: URL | null = null;
  try { parsed = new URL(raw); } catch {
    try { parsed = new URL('https://' + raw); } catch { parsed = null; }
  }

  const hostname = parsed?.hostname?.toLowerCase() || '';
  const pathAndQuery = parsed ? ((parsed.pathname || '') + (parsed.search || '')).toLowerCase() : lower;

  if (parsed && parsed.protocol === 'http:') {
    issues.push('Insecure protocol (HTTP instead of HTTPS).');
  }

  if (/\.(exe|apk|bat|scr|dll|msi|jar|vbs|cmd)(?:$|\?)/i.test(pathAndQuery) || /\.(exe|apk|bat|scr|dll|msi|jar|vbs|cmd)$/i.test(lower)) {
    issues.push('Contains potentially harmful file extension - DO NOT DOWNLOAD.');
  }

  const typoPatterns = [
    { pattern: 'gooogle', real: 'google' },
    { pattern: 'facebok', real: 'facebook' },
    { pattern: 'instgram', real: 'instagram' },
    { pattern: 'twiter', real: 'twitter' },
    { pattern: 'amazom', real: 'amazon' },
    { pattern: 'paypa1', real: 'paypal' },
    { pattern: 'linkedln', real: 'linkedin' },
  ];
  typoPatterns.forEach(({ pattern, real }) => {
    if ((hostname && hostname.includes(pattern)) || lower.includes(pattern)) {
      issues.push(`Possible typosquatting: "${pattern}" looks like "${real}".`);
    }
  });

  if (/[0-9]{10,}/.test(pathAndQuery) || /[a-z]{20,}/.test(pathAndQuery) || /[A-Z0-9_-]{25,}/.test(raw)) {
    issues.push('Contains long random-looking token/ID (common in phishing or short-lived blobs).');
  }

  const suspiciousTLDs = ['xyz', 'top', 'club', 'work', 'tk', 'ml', 'ga', 'cf'];
  const tld = (hostname.split('.').slice(-1)[0] || '').toLowerCase();
  if (tld && suspiciousTLDs.includes(tld)) {
    issues.push(`Uses suspicious top-level domain (.${tld}).`);
  }

  if (/(^|[\W_])(login|signin|verify|secure|account[-_]?verify|update[-_]?payment|auth|confirm)([\W_]|$)/i.test(lower)) {
    issues.push('Contains login/verify/auth keywords commonly used in phishing URLs.');
  }

  if (/\b\d{1,3}(\.\d{1,3}){3}\b/.test(hostname || raw)) {
    issues.push('Contains raw IP address (phishing hosts sometimes use IPs).');
  }

  if (issues.some(i => i.includes('DO NOT DOWNLOAD') || i.includes('Insecure protocol'))) {
    return { status: 'Dangerous', issues };
  }
  if (issues.length >= 2) return { status: 'Dangerous', issues };
  if (issues.length === 1) return { status: 'Suspicious', issues };
  return { status: 'Safe', issues: [] };
};

const extractUrlsFromText = (text: string): string[] => {
  if (!text) return [];
  const urlRegex = /https?:\/\/[^\s"'<>]+|www\.[^\s"'<>]+/gi;
  const matches = text.match(urlRegex) || [];
  return Array.from(new Set(matches.map(m => m.startsWith('www.') ? 'http://' + m : m)));
};

const analyzeUrls = (urls: string[]) => {
  const details: { url: string; result: URLCheckResult }[] = [];
  let overall: URLCheckResult['status'] = 'Safe';
  for (const u of urls) {
    const r = checkUrlString(u);
    details.push({ url: u, result: r });
    if (r.status === 'Dangerous') overall = 'Dangerous';
    else if (r.status === 'Suspicious' && overall !== 'Dangerous') overall = 'Suspicious';
  }
  return { overall, details };
};

interface Tool {
  id: string;
  title: string;
  icon: React.ElementType;
  description: string;
  color: string;
}

const tools: Tool[] = [
  { id: 'password-strength', title: 'Password Strength Analyzer', icon: Lock, description: 'Check password strength, entropy, and crack time', color: 'primary' },
  { id: 'phishing-detector', title: 'Email Phishing Detector', icon: Mail, description: 'Detect suspicious emails and phishing attempts', color: 'secondary' },
  { id: 'url-checker', title: 'URL / File Risk Checker', icon: Link2, description: 'Analyze URLs for security risks', color: 'accent' },
  { id: 'encrypt-text', title: 'Encrypt Text Tool', icon: Shield, description: 'Encrypt your sensitive text', color: 'primary' },
  { id: 'decrypt-text', title: 'Decrypt Text Tool', icon: Eye, description: 'Decrypt encrypted messages', color: 'secondary' },

  { id: 'stego-encode', title: 'Image Steganography — Encode', icon: FileText, description: 'Hide messages inside PNG images (LSB)', color: 'accent' },
  { id: 'stego-decode', title: 'Image Steganography — Decode', icon: Search, description: 'Extract hidden messages from PNG images (and detect QR)', color: 'primary' },

  { id: 'qr-scanner', title: 'QR Code Security Scanner', icon: Camera, description: 'Scan QR and check URLs for safety', color: 'secondary' },
  { id: 'breach-checker', title: 'Local Breach Checker', icon: AlertTriangle, description: 'Check Gmail/phone against a local simulated breach dataset', color: 'accent' },
];

const ToolsDashboard: React.FC = () => {
  const [activeTool, setActiveTool] = useState<string | null>(null);

  useEffect(() => {
    const onKey = (e: KeyboardEvent) => {
      if (e.key === 'Escape') setActiveTool(null);
    };
    window.addEventListener('keydown', onKey);
    return () => window.removeEventListener('keydown', onKey);
  }, []);

  return (
    <div className="min-h-screen py-8 px-4">
      <div className="container mx-auto max-w-7xl">
        <div className="text-center mb-12">
          <h1 className="font-display text-4xl lg:text-5xl font-bold text-gradient-primary mb-4">
            Cyber Security Tools
          </h1>
          <p className="text-muted-foreground max-w-2xl mx-auto">
            Powerful utilities to analyze, protect, and educate. All tools run locally for your privacy.
          </p>
        </div>

        {!activeTool && (
          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
            {tools.map((tool) => (
              <button
                key={tool.id}
                type="button"
                data-tool-id={tool.id}
                onClick={(e) => {
                  e.preventDefault();
                  e.stopPropagation();
                  setActiveTool(tool.id);
                }}
                className={cn(
                  "glass-card-hover p-6 text-left transition-all duration-300",
                )}
              >
                <div className={cn(
                  "w-12 h-12 rounded-lg flex items-center justify-center mb-4",
                  tool.color === 'primary' && "bg-primary/20 text-primary",
                  tool.color === 'secondary' && "bg-secondary/20 text-secondary",
                  tool.color === 'accent' && "bg-accent/20 text-accent",
                )}>
                  <tool.icon className="w-6 h-6" />
                </div>
                <h3 className="font-display font-semibold text-lg mb-2">{tool.title}</h3>
                <p className="text-sm text-muted-foreground">{tool.description}</p>
              </button>
            ))}
          </div>
        )}

        {activeTool && (
          <div className="mt-8 glass-card p-8 animate-fade-in-up relative">
            <div className="flex items-start justify-between mb-4">
              <div className="flex items-center gap-3">
                <button
                  type="button"
                  onClick={(e) => { e.preventDefault(); e.stopPropagation(); setActiveTool(null); }}
                  className="inline-flex items-center gap-2 px-3 py-1 rounded bg-muted/10 hover:bg-muted/20"
                >
                  ← Back
                </button>

                <h2 className="font-display text-2xl font-bold">
                  {tools.find(t => t.id === activeTool)?.title ?? 'Tool'}
                </h2>
              </div>

              <button
                type="button"
                aria-label="Close tool panel"
                onClick={(e) => { e.preventDefault(); e.stopPropagation(); setActiveTool(null); }}
                className="px-3 py-1 rounded bg-muted/10 hover:bg-muted/20"
              >
                Close
              </button>
            </div>

            <ToolPanel toolId={activeTool} />
          </div>
        )}
      </div>
    </div>
  );
};

interface ToolPanelProps {
  toolId: string;
}

const ToolPanel: React.FC<ToolPanelProps> = ({ toolId }) => {
  switch (toolId) {
    case 'password-strength':
      return <PasswordStrengthTool />;
    case 'phishing-detector':
      return <PhishingDetectorTool />;
    case 'url-checker':
      return <URLCheckerTool />;
    case 'encrypt-text':
      return <EncryptTextTool />;
    case 'decrypt-text':
      return <DecryptTextTool />;
    case 'stego-encode':
      return <StegoEncodeTool />;
    case 'stego-decode':
      return <StegoDecodeTool />;
    case 'qr-scanner':
      return <QRScannerTool />;
    case 'breach-checker':
      return <BreachCheckerLocal />;
    default:
      return null;
  }
};

/* ---------- Password / Phishing / URL / Encrypt / Decrypt tools (full implementations) ---------- */

const PasswordStrengthTool: React.FC = () => {
  const [password, setPassword] = useState('');
  const [showPassword, setShowPassword] = useState(false);

  const hasUpper = /[A-Z]/.test(password);
  const hasLower = /[a-z]/.test(password);
  const hasNumber = /[0-9]/.test(password);
  const hasSpecial = /[!@#$%^&*(),.?":{}|<>]/.test(password);
  const hasLength = password.length >= 8;

  const analyzePassword = () => {
    const length = password.length;
    const charset = (hasUpper ? 26 : 0) + (hasLower ? 26 : 0) + (hasNumber ? 10 : 0) + (hasSpecial ? 32 : 0);
    const entropy = length * Math.log2(charset || 1);

    let strength = 0;
    if (length >= 8) strength++;
    if (length >= 12) strength++;
    if (hasUpper && hasLower) strength++;
    if (hasNumber) strength++;
    if (hasSpecial) strength++;

    const crackTimes = ['Instant', 'Seconds', 'Minutes', 'Hours', 'Days', 'Years', 'Centuries'];
    const crackTimeIndex = Math.min(Math.floor(entropy / 10), crackTimes.length - 1);

    return { strength, entropy: entropy.toFixed(2), crackTime: crackTimes[crackTimeIndex] };
  };

  const analysis = password ? analyzePassword() : null;

  return (
    <div className="space-y-6">
      <div className="flex items-center gap-3 mb-4">
        <Lock className="w-6 h-6 text-primary" />
        <h2 className="font-display text-2xl font-bold">Password Strength Analyzer</h2>
      </div>

      <div className="p-4 bg-muted/30 rounded-lg">
        <p className="text-sm text-muted-foreground mb-2">
          <Info className="w-4 h-4 inline mr-1" />
         Enter a password to analyze its strength, entropy, and estimated crack time.
        </p>
      </div>

      <div className="relative">
        <Input
          type={showPassword ? 'text' : 'password'}
          placeholder="Enter password to analyze..."
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          className="pr-12"
        />
        <button
          onClick={() => setShowPassword(!showPassword)}
          className="absolute right-4 top-1/2 -translate-y-1/2 text-muted-foreground hover:text-foreground"
        >
          {showPassword ? <EyeOff className="w-5 h-5" /> : <Eye className="w-5 h-5" />}
        </button>
      </div>

      {analysis && (
        <div className="space-y-4 animate-fade-in-up">
          <div className="p-4 bg-muted/30 rounded-lg">
            <p className="text-sm text-muted-foreground mb-2">Requirements</p>
            <ul className="grid grid-cols-2 gap-y-2 gap-x-4 list-none p-0 m-0">
              <li className={hasLength ? "text-green-400" : "text-red-400"}>8+ characters</li>
              <li className={hasUpper ? "text-green-400" : "text-red-400"}>Uppercase</li>
              <li className={hasLower ? "text-green-400" : "text-red-400"}>Lowercase</li>
              <li className={hasNumber ? "text-green-400" : "text-red-400"}>Number</li>
              <li className={hasSpecial ? "text-green-400" : "text-red-400"}>Special char</li>
            </ul>
          </div>

          <div className="grid grid-cols-1 md:grid-cols-3 gap-4">
            <div className="p-4 bg-muted/30 rounded-lg">
              <p className="text-sm text-muted-foreground mb-2">Strength</p>
              <div className="flex items-center gap-2">
                <Progress value={(analysis.strength / 5) * 100} className="h-3" />
                <span className="font-display font-bold">{analysis.strength}/5</span>
              </div>
            </div>

            <div className="p-4 bg-muted/30 rounded-lg">
              <p className="text-sm text-muted-foreground mb-2">Entropy</p>
              <p className="font-display text-xl font-bold text-accent">{analysis.entropy} bits</p>
            </div>

            <div className="p-4 bg-muted/30 rounded-lg">
              <p className="text-sm text-muted-foreground mb-2">Crack Time (est.)</p>
              <p className="font-display text-xl font-bold text-primary">{analysis.crackTime}</p>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

const PhishingDetectorTool: React.FC = () => {
  const [emailContent, setEmailContent] = useState('');
  const [result, setResult] = useState<{ status: string; issues: string[] } | null>(null);

  const analyzeEmail = () => {
    const issues: string[] = [];
    const content = emailContent.toLowerCase();

    const urgentWords = ['urgent', 'otp', 'upload', 'personal', 'click', 'information', 'immediately', 'act now', 'limited time', 'expires', 'suspended', 'verify now'];
    if (urgentWords.some(word => content.includes(word))) {
      issues.push('Contains urgent/pressure language');
    }

    const ctaPatterns = ['click here', 'verify your account', 'update your payment', 'confirm your', 'click to'];
    if (ctaPatterns.some(p => content.includes(p))) {
      issues.push('Contains suspicious call-to-action phrases');
    }

    const dangerousWords = ['password', 'personal', 'ssn', 'social security', 'bank account', 'credit card'];
    if (dangerousWords.some(word => content.includes(word))) {
      issues.push('Requests sensitive information');
    }

    if (content.includes('dear customer') || content.includes('dear user') || content.includes('sir/madam')) {
      issues.push('Uses generic greeting (potential red flag)');
    }

    let status = 'Safe';
    if (issues.length >= 3) status = 'Dangerous';
    else if (issues.length >= 1) status = 'Suspicious';

    setResult({ status, issues });
  };

  return (
    <div className="space-y-6">
      <div className="flex items-center gap-3 mb-4">
        <Mail className="w-6 h-6 text-secondary" />
        <h2 className="font-display text-2xl font-bold">Email Phishing Detector</h2>
      </div>

      <div className="p-4 bg-muted/30 rounded-lg">
        <p className="text-sm text-muted-foreground">
          <Info className="w-4 h-4 inline mr-1" />
          Paste email content to detect phishing indicators: urgent language, suspicious links, and dangerous requests.
        </p>
      </div>

      <Textarea
        placeholder="Paste email content here..."
        value={emailContent}
        onChange={(e) => setEmailContent(e.target.value)}
        rows={6}
      />

      <Button variant="neon" onClick={analyzeEmail} disabled={!emailContent.trim()}>
        Analyze Email
      </Button>

      {result && (
        <div className={cn(
          "p-6 rounded-lg animate-fade-in-up",
          result.status === 'Safe' && "bg-green-500/10 border border-green-500/30",
          result.status === 'Suspicious' && "bg-yellow-500/10 border border-yellow-500/30",
          result.status === 'Dangerous' && "bg-destructive/10 border border-destructive/30",
        )}>
          <div className="flex items-center gap-3 mb-4">
            {result.status === 'Safe' && <CheckCircle className="w-6 h-6 text-green-500" />}
            {result.status === 'Suspicious' && <AlertTriangle className="w-6 h-6 text-yellow-500" />}
            {result.status === 'Dangerous' && <XCircle className="w-6 h-6 text-destructive" />}
            <span className="font-display text-xl font-bold">{result.status}</span>
          </div>
          {result.issues.length > 0 && (
            <ul className="space-y-2">
              {result.issues.map((issue, i) => (
                <li key={i} className="text-sm text-muted-foreground flex items-start gap-2">
                  <AlertTriangle className="w-4 h-4 mt-0.5 shrink-0" />
                  {issue}
                </li>
              ))}
            </ul>
          )}
          {result.issues.length === 0 && (
            <p className="text-sm text-muted-foreground">No suspicious indicators detected.</p>
          )}
        </div>
      )}
    </div>
  );
};

const URLCheckerTool: React.FC = () => {
  const [url, setUrl] = useState('');
  const [result, setResult] = useState<URLCheckResult | null>(null);

  const checkURL = () => {
    const res = checkUrlString(url);
    setResult(res);
  };

  return (
    <div className="space-y-6">
      <div className="flex items-center gap-3 mb-4">
        <Link2 className="w-6 h-6 text-accent" />
        <h2 className="font-display text-2xl font-bold">URL / File Risk Checker</h2>
      </div>

      <div className="p-4 bg-muted/30 rounded-lg">
        <p className="text-sm text-muted-foreground">
          <Info className="w-4 h-4 inline mr-1" />
          Enter a URL or filename to check for insecure protocols, typosquatting, suspicious patterns, and harmful file extensions.
        </p>
      </div>

      <Input
        placeholder="Enter URL or filename to analyze (e.g., https://example.com or file.exe)"
        value={url}
        onChange={(e) => setUrl(e.target.value)}
      />

      <Button variant="neon-blue" onClick={checkURL} disabled={!url.trim()}>
        Check URL / File
      </Button>

      {result && (
        <div className={cn(
          "p-6 rounded-lg animate-fade-in-up",
          result.status === 'Safe' && "bg-green-500/10 border border-green-500/30",
          result.status === 'Suspicious' && "bg-yellow-500/10 border border-yellow-500/30",
          result.status === 'Dangerous' && "bg-destructive/10 border border-destructive/30",
        )}>
          <div className="flex items-center gap-3 mb-4">
            {result.status === 'Safe' && <CheckCircle className="w-6 h-6 text-green-500" />}
            {result.status === 'Suspicious' && <AlertTriangle className="w-6 h-6 text-yellow-500" />}
            {result.status === 'Dangerous' && <XCircle className="w-6 h-6 text-destructive" />}
            <span className="font-display text-xl font-bold">{result.status}</span>
          </div>

          {result.issues.length > 0 ? (
            <ul className="space-y-2">
              {result.issues.map((issue, i) => (
                <li key={i} className="text-sm text-muted-foreground flex items-start gap-2">
                  <AlertTriangle className="w-4 h-4 mt-0.5 shrink-0 text-yellow-500" />
                  {issue}
                </li>
              ))}
            </ul>
          ) : (
            <p className="text-sm text-green-500">No security issues detected with this URL / file.</p>
          )}
        </div>
      )}
    </div>
  );
};

const EncryptTextTool: React.FC = () => {
  const [text, setText] = useState('');
  const [encrypted, setEncrypted] = useState('');

  const encrypt = () => {
    const base64 = btoa(text);
    const shuffled = base64.split('').reverse().join('');
    setEncrypted(`CX${shuffled}XC`);
  };

  return (
    <div className="space-y-6">
      <div className="flex items-center gap-3 mb-4">
        <Shield className="w-6 h-6 text-primary" />
        <h2 className="font-display text-2xl font-bold">Encrypt Text Tool</h2>
      </div>

      <div className="p-4 bg-muted/30 rounded-lg">
        <p className="text-sm text-muted-foreground">
          <Info className="w-4 h-4 inline mr-1" />
          Enter text to encrypt. This is a simulation for educational purposes.
        </p>
      </div>

      <Textarea
        placeholder="Enter text to encrypt..."
        value={text}
        onChange={(e) => setText(e.target.value)}
        rows={4}
      />

      <Button variant="neon" onClick={encrypt} disabled={!text.trim()}>
        Encrypt Text
      </Button>

      {encrypted && (
        <div className="p-4 bg-muted/30 rounded-lg font-mono text-sm break-all animate-fade-in-up">
          <p className="text-muted-foreground mb-2">Encrypted Output:</p>
          <p className="text-accent">{encrypted}</p>
        </div>
      )}
    </div>
  );
};

const DecryptTextTool: React.FC = () => {
  const [encrypted, setEncrypted] = useState('');
  const [decrypted, setDecrypted] = useState('');

  const decrypt = () => {
    try {
      const cleaned = encrypted.replace(/^CX/, '').replace(/XC$/, '');
      const reversed = cleaned.split('').reverse().join('');
      const decoded = atob(reversed);
      setDecrypted(decoded);
    } catch {
      setDecrypted('Error: Invalid encrypted text format');
    }
  };

  return (
    <div className="space-y-6">
      <div className="flex items-center gap-3 mb-4">
        <Eye className="w-6 h-6 text-secondary" />
        <h2 className="font-display text-2xl font-bold">Decrypt Text Tool</h2>
      </div>

      <div className="p-4 bg-muted/30 rounded-lg">
        <p className="text-sm text-muted-foreground">
          <Info className="w-4 h-4 inline mr-1" />
          Paste encrypted text from our Encrypt tool to decrypt it.
        </p>
      </div>

      <Textarea
        placeholder="Paste encrypted text..."
        value={encrypted}
        onChange={(e) => setEncrypted(e.target.value)}
        rows={4}
      />

      <Button variant="neon-pink" onClick={decrypt} disabled={!encrypted.trim()}>
        Decrypt Text
      </Button>

      {decrypted && (
        <div className="p-4 bg-muted/30 rounded-lg animate-fade-in-up">
          <p className="text-muted-foreground mb-2">Decrypted Output:</p>
          <p className="text-foreground">{decrypted}</p>
        </div>
      )}
    </div>
  );
};

/* ---------------- Updated StegoEncodeTool (minimal filename/link heuristics) ---------------- */
const StegoEncodeTool: React.FC = () => {
  const fileRef = useRef<HTMLInputElement | null>(null);
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const [message, setMessage] = useState('');
  const [imageName, setImageName] = useState<string | null>(null);
  const [info, setInfo] = useState<string | null>(null);
  const [capacity, setCapacity] = useState<number | null>(null);
  const [safetyStatus, setSafetyStatus] = useState<URLCheckResult | null>(null);

  const toBits = (bytes: Uint8Array) => {
    const bits: number[] = [];
    for (let b of bytes) {
      for (let i = 7; i >= 0; i--) bits.push((b >> i) & 1);
    }
    return bits;
  };

  const computeCapacity = (width: number, height: number) => {
    const totalLSB = width * height * 3;
    const usable = totalLSB - 32;
    return Math.floor(usable / 8);
  };

  const analyzeFilename = (name: string | null) => {
    if (!name) return null;
    const issues: string[] = [];
    const lower = name.toLowerCase();
    if (/\.(exe|apk|bat|scr|dll|msi|jar|vbs|cmd)$/.test(lower)) {
      issues.push('Filename uses executable/installer extension — risky.');
    }
    if (/\.png\.exe$/.test(lower) || (/\.[^\.]{1,4}\.[a-z]{2,4}$/.test(lower) && lower.split('.').length > 2)) {
      issues.push('Double-extension in filename (possible trick).');
    }
    if (issues.length === 0) return null;
    return { status: issues.length > 0 ? 'Suspicious' : 'Safe', issues } as URLCheckResult;
  };

  const handleImageLoad = async (file?: File) => {
    setInfo(null); setCapacity(null); setSafetyStatus(null);
    const f = file || (fileRef.current?.files ? fileRef.current.files[0] : undefined);
    if (!f) { setInfo('Please choose an image file (PNG recommended).'); return; }
    setImageName(f.name);
    const dataUrl = await new Promise<string>((res, rej) => {
      const reader = new FileReader();
      reader.onload = () => res(String(reader.result));
      reader.onerror = rej;
      reader.readAsDataURL(f);
    });
    const img = new Image();
    img.src = dataUrl;
    img.onload = () => {
      const canvas = canvasRef.current!;
      const ctx = canvas.getContext('2d')!;
      canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0);
      setCapacity(computeCapacity(img.width, img.height));

      const fnSafety = analyzeFilename(f.name);
      if (fnSafety) {
        setSafetyStatus(fnSafety);
        setInfo(`Loaded ${img.width}x${img.height}. Warning: suspicious filename detected.`);
      } else {
        setInfo(`Loaded ${img.width}x${img.height} — capacity ${computeCapacity(img.width, img.height)} bytes`);
      }
    };
    img.onerror = () => setInfo('Failed to load image.');
  };

  const handleEncode = () => {
    setInfo(null);
    if (safetyStatus && safetyStatus.status === 'Dangerous') return setInfo('Encoding blocked: image flagged DANGEROUS.');
    const canvas = canvasRef.current!; const ctx = canvas.getContext('2d'); if (!ctx) return setInfo('Canvas not ready.');
    const width = canvas.width; const height = canvas.height; if (!width || !height) return setInfo('Load an image first.');

    const encoder = new TextEncoder(); const msgBytes = encoder.encode(message);
    const max = computeCapacity(width, height);
    if (msgBytes.length > max) return setInfo(`Message too long. Max ${max} bytes for this image.`);

    const lengthBytes = new Uint8Array(4);
    const len = msgBytes.length;
    lengthBytes[0] = (len >> 24) & 0xff; lengthBytes[1] = (len >> 16) & 0xff; lengthBytes[2] = (len >> 8) & 0xff; lengthBytes[3] = len & 0xff;

    const payload = new Uint8Array(4 + msgBytes.length); payload.set(lengthBytes, 0); payload.set(msgBytes, 4);
    const bits = toBits(payload);

    const imageData = ctx.getImageData(0, 0, width, height);
    const data = imageData.data;

    let bitIndex = 0;
    for (let i = 0; i < data.length && bitIndex < bits.length; i += 4) {
      data[i] = (data[i] & 0xfe) | bits[bitIndex++]; if (bitIndex >= bits.length) break;
      data[i + 1] = (data[i + 1] & 0xfe) | bits[bitIndex++]; if (bitIndex >= bits.length) break;
      data[i + 2] = (data[i + 2] & 0xfe) | bits[bitIndex++];
    }

    ctx.putImageData(imageData, 0, 0);
    setInfo(`Message encoded successfully (${msgBytes.length} bytes). Use "Download Encoded Image" to save as PNG.`);
  };

  const handleDownload = () => {
    if (safetyStatus && safetyStatus.status === 'Dangerous') return setInfo('Download blocked: image flagged DANGEROUS.');
    const canvas = canvasRef.current!; if (!canvas) return setInfo('Nothing to download.');
    const url = canvas.toDataURL('image/png'); const a = document.createElement('a'); a.href = url;
    const base = imageName ? imageName.replace(/\.[^/.]+$/, '') : 'stego-image'; a.download = `${base}-encoded.png`; a.click();
  };

  return (
    <div className="space-y-6">
      <div className="flex items-center gap-3 mb-2">
        <FileText className="w-6 h-6 text-primary" />
        <h2 className="font-display text-2xl font-bold">Image Steganography — Encode</h2>
      </div>

      <div className="p-4 bg-muted/30 rounded-lg">
        <p className="text-sm text-muted-foreground">Upload a PNG image, enter a secret message, then Encode and Download the encoded PNG. The tool will flag suspicious filenames and block download if dangerous.</p>
      </div>

      <div className="flex gap-3 items-center">
        <div className="flex items-center gap-2">
          <input ref={fileRef} type="file" accept="image/*" onChange={() => handleImageLoad()} />
        </div>
        <div className="ml-auto text-sm text-muted-foreground">{imageName ?? 'No image loaded'}</div>
      </div>

      {capacity !== null && (<p className="text-sm text-muted-foreground">Capacity: <strong>{capacity}</strong> bytes (approx).</p>)}

      <Textarea placeholder="Enter secret message to hide inside the image..." value={message} onChange={(e) => setMessage(e.target.value)} rows={6} />

      <div className="flex gap-3">
        <Button variant="neon" onClick={handleEncode} disabled={!message.trim()}>Encode</Button>
        <Button variant="outline" onClick={handleDownload}>Download Encoded Image</Button>
      </div>

      {safetyStatus && (
        <div className={cn("p-3 rounded-md mt-3 text-sm font-semibold",
          safetyStatus.status === 'Dangerous' ? "bg-destructive/10 border border-destructive/30 text-destructive" : "bg-yellow-500/10 border border-yellow-500/30 text-yellow-300"
        )}>
          <div>Filename safety: <strong>{safetyStatus.status}</strong></div>
          {safetyStatus.issues.length > 0 && <ul className="mt-2 text-sm">{safetyStatus.issues.map((i,idx)=>(<li key={idx}>• {i}</li>))}</ul>}
          {safetyStatus.status === 'Dangerous' && <div className="mt-2 font-semibold">DANGER: Encoding/download blocked.</div>}
        </div>
      )}

      {info && <p className="text-sm text-muted-foreground">{info}</p>}
      <canvas ref={canvasRef} className="hidden" />
    </div>
  );
};

/* ---------------- Updated StegoDecodeTool (detect links in LSB and QR payloads) ---------------- */
const StegoDecodeTool: React.FC = () => {
  const fileRef = useRef<HTMLInputElement | null>(null);
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const [imageName, setImageName] = useState<string | null>(null);
  const [decoded, setDecoded] = useState<string | null>(null);
  const [info, setInfo] = useState<string | null>(null);
  const [qrPayload, setQrPayload] = useState<string | null>(null);
  const [urlFindings, setUrlFindings] = useState<{ url: string; result: URLCheckResult }[]>([]);
  const [overallUrlStatus, setOverallUrlStatus] = useState<URLCheckResult['status'] | null>(null);

  const fromBitsToBytes = (bits: number[]) => {
    const bytes: number[] = [];
    for (let i = 0; i < bits.length; i += 8) {
      let val = 0;
      for (let j = 0; j < 8 && i + j < bits.length; j++) {
        val = (val << 1) | bits[i + j];
      }
      bytes.push(val);
    }
    return new Uint8Array(bytes);
  };

  const tryLSBDecode = (ctx: CanvasRenderingContext2D, width: number, height: number): string | null => {
    try {
      const imageData = ctx.getImageData(0, 0, width, height);
      const data = imageData.data;
      const bits: number[] = [];
      for (let i = 0; i < data.length; i += 4) { bits.push(data[i] & 1); bits.push(data[i + 1] & 1); bits.push(data[i + 2] & 1); }
      if (bits.length < 32) return null;
      const lenBits = bits.slice(0, 32);
      let msgLen = 0;
      for (let i = 0; i < 32; i++) msgLen = (msgLen << 1) | lenBits[i];
      if (msgLen === 0) return '';
      const totalBitsNeeded = 32 + msgLen * 8;
      if (bits.length < totalBitsNeeded) return null;
      const msgBits = bits.slice(32, 32 + msgLen * 8);
      const bytes = fromBitsToBytes(msgBits);
      try { return new TextDecoder().decode(bytes); } catch { return null; }
    } catch (e) {
      return null;
    }
  };

  const handleImageLoad = async (file?: File) => {
    setInfo(null); setDecoded(null); setQrPayload(null); setUrlFindings([]); setOverallUrlStatus(null);
    const f = file || (fileRef.current?.files ? fileRef.current.files[0] : undefined);
    if (!f) { setInfo('Please choose an image file.'); return; }
    setImageName(f.name);
    const dataUrl = await new Promise<string>((res, rej) => {
      const reader = new FileReader(); reader.onload = () => res(String(reader.result)); reader.onerror = rej; reader.readAsDataURL(f);
    });
    const img = new Image(); img.src = dataUrl;
    img.onload = () => {
      const canvas = canvasRef.current!; const ctx = canvas.getContext('2d')!; canvas.width = img.width; canvas.height = img.height; ctx.drawImage(img, 0, 0);
      setInfo(`Loaded ${img.width}x${img.height}. Ready to decode.`);
      try {
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const code = jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: 'attemptBoth' });
        if (code && code.data) {
          setQrPayload(code.data);
        }
      } catch (e) {}
    };
    img.onerror = () => setInfo('Failed to load image.');
  };

  const analyzeExtractedTexts = (texts: string[]) => {
    const urls = texts.flatMap(t => extractUrlsFromText(t || ''));
    const { overall, details } = analyzeUrls(urls);
    setOverallUrlStatus(overall as URLCheckResult['status']);
    setUrlFindings(details.map(d => ({ url: d.url, result: d.result })));
    return { urls, overall };
  };

  const handleDecode = () => {
    setInfo(null); setDecoded(null); setUrlFindings([]); setOverallUrlStatus(null);
    const canvas = canvasRef.current!; const ctx = canvas.getContext('2d'); if (!ctx) return setInfo('Canvas not ready.');
    const width = canvas.width; const height = canvas.height; if (!width || !height) return setInfo('Load an image first.');

    const lsbText = tryLSBDecode(ctx, width, height);
    if (lsbText === null) {
      if (qrPayload) { setInfo('No LSB hidden message found. QR payload detected instead.'); analyzeExtractedTexts([qrPayload]); return; }
      return setInfo('Image too small or no data found.');
    }

    if (lsbText === '') {
      if (qrPayload) { setInfo('No hidden LSB message (length=0). QR payload found.'); analyzeExtractedTexts([qrPayload]); return; }
      setDecoded(''); setInfo('No hidden message (length = 0).'); analyzeExtractedTexts([""]); return;
    }

    setDecoded(lsbText);
    setInfo(`Decoded ${lsbText.length} characters from LSB.`);

    analyzeExtractedTexts([lsbText, qrPayload ?? '']);
  };

  const handleCopy = async () => {
    if (!decoded && !qrPayload) return;
    if (overallUrlStatus === 'Dangerous') return setInfo('Copy blocked: image contains links flagged DANGEROUS.');
    try {
      await navigator.clipboard.writeText(decoded ?? qrPayload ?? '');
      setInfo('Copied to clipboard.'); setTimeout(() => setInfo(null), 1200);
    } catch {
      setInfo('Copy failed.');
    }
  };

  return (
    <div className="space-y-6">
      <div className="flex items-center gap-3 mb-2">
        <Search className="w-6 h-6 text-primary" />
        <h2 className="font-display text-2xl font-bold">Image Steganography — Decode (Link Detector)</h2>
      </div>

      <div className="p-4 bg-muted/30 rounded-lg">
        <p className="text-sm text-muted-foreground">Upload a PNG encoded with this tool. The tool extracts LSB hidden text and detects URLs (also reads QR payload). Any detected URLs are checked and flagged as Safe / Suspicious / Dangerous.</p>
      </div>

      <div className="flex gap-3 items-center">
        <div className="flex items-center gap-2">
          <input ref={fileRef} type="file" accept="image/*" onChange={() => handleImageLoad()} />
        </div>
        <div className="ml-auto text-sm text-muted-foreground">{imageName ?? 'No image loaded'}</div>
      </div>

      <div className="flex gap-3">
        <Button variant="neon" onClick={handleDecode}>Decode</Button>
        <Button variant="outline" onClick={handleCopy} disabled={!decoded && !qrPayload || overallUrlStatus === 'Dangerous'}>Copy Result</Button>
      </div>

      {overallUrlStatus && (
        <div className={cn("p-4 rounded-md mt-3 text-sm",
          overallUrlStatus === 'Dangerous' ? "bg-destructive/10 border border-destructive/30 text-destructive" :
          overallUrlStatus === 'Suspicious' ? "bg-yellow-500/10 border border-yellow-500/30 text-yellow-300" : "bg-emerald-900/10 border border-emerald-500/20 text-emerald-300"
        )}>
          <div className="font-semibold">Detected link safety: {overallUrlStatus}</div>
          {urlFindings.length > 0 && (
            <div className="mt-2 text-sm">
              <div className="text-xs text-muted-foreground">Found links and their analysis:</div>
              <ul className="mt-2 text-sm space-y-1">
                {urlFindings.map((f, idx) => (
                  <li key={idx} className="font-mono break-words">
                    <div className="flex items-start justify-between gap-3">
                      <div className="truncate">{f.url}</div>
                      <div className="text-sm ml-3">{f.result.status}</div>
                    </div>
                    {f.result.issues.length > 0 && (
                      <ul className="text-xs text-muted-foreground mt-1">
                        {f.result.issues.map((iss, i) => (<li key={i}>• {iss}</li>))}
                      </ul>
                    )}
                  </li>
                ))}
              </ul>
            </div>
          )}

          {overallUrlStatus === 'Dangerous' && <div className="mt-2 font-semibold">DANGER: Image contains link(s) flagged as DANGEROUS. Do not follow links or download content from them.</div>}
        </div>
      )}

      {decoded !== null && (
        <div className="p-4 bg-muted/20 rounded-lg">
          <p className="text-sm text-muted-foreground mb-2">Decoded message:</p>
          <div className="font-mono text-sm break-words">{decoded.length ? decoded : <em>(empty)</em>}</div>
        </div>
      )}

      {qrPayload && (
        <div className="p-4 bg-muted/10 rounded border">
          <p className="text-sm text-muted-foreground mb-2">Detected QR payload (image):</p>
          <div className="font-mono text-sm break-words">{qrPayload}</div>
        </div>
      )}

      {info && <p className="text-sm text-muted-foreground">{info}</p>}

      <canvas ref={canvasRef} className="hidden" />
    </div>
  );
};

/* ---------------- QRScanner & BreachChecker (complete) ---------------- */

const QRScannerTool: React.FC = () => {
  const videoRef = useRef<HTMLVideoElement | null>(null);
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const scanIntervalRef = useRef<number | null>(null);
  const streamRef = useRef<MediaStream | null>(null);

  const [scanning, setScanning] = useState(false);
  const [cameraAvailable, setCameraAvailable] = useState<boolean | null>(null);
  const [payload, setPayload] = useState<string | null>(null);
  const [info, setInfo] = useState<string | null>(null);
  const [urlCheckResult, setUrlCheckResult] = useState<URLCheckResult | null>(null);
  const [paymentInfo, setPaymentInfo] = useState<{ isPayment: boolean; upiId?: string; name?: string; phone?: string } | null>(null);

  const USE_BROWSER_ALERT = true;

  useEffect(() => {
    (async () => {
      try {
        const devices = await navigator.mediaDevices.enumerateDevices();
        setCameraAvailable(devices.some(d => d.kind === 'videoinput'));
      } catch (err) {
        console.warn('enumerateDevices failed', err);
        setCameraAvailable(false);
      }
    })();

    return () => stopCamera();
  }, []);

  const setStatus = (s: string | null) => {
    setInfo(s);
  };

  const emitAlert = (status: URLCheckResult['status'] | 'SafeText', message: string) => {
    if (!USE_BROWSER_ALERT) return;
    try {
      if (status === 'Dangerous') {
        window.alert(`DANGEROUS QR\n\n${message}`);
      } else if (status === 'Suspicious') {
        window.alert(`Suspicious QR\n\n${message}`);
      } else if (status === 'Safe' || status === 'SafeText') {
        window.alert(`Safe QR\n\n${message}`);
      }
    } catch (e) {
      console.warn('alert blocked or failed', e);
    }
  };

  const isLikelyUrl = (s: string) => {
    try {
      const u = new URL(s);
      return !!u.protocol && (u.protocol.startsWith('http') || u.protocol.startsWith('https'));
    } catch {
      return false;
    }
  };

  const sentenceForStatus = (status: URLCheckResult['status']) => {
    if (status === 'Safe') return 'This QR code is Safe — no harmful or suspicious activity detected.';
    if (status === 'Suspicious') return 'This QR code is Suspicious — it contains unusual patterns that may indicate a phishing attempt.';
    return 'This QR code is Dangerous — it contains harmful or phishing-related content and should not be opened.';
  };

  const parsePaymentFromPayload = (p: string | null) => {
    if (!p) return { isPayment: false } as const;
    const s = p.trim();
    const lower = s.toLowerCase();

    let upiId: string | undefined;
    let name: string | undefined;
    let phone: string | undefined;
    let isPayment = false;

    try {
      let urlForParams: URL | null = null;
      if (s.includes('://')) {
        try { urlForParams = new URL(s); } catch (e) { urlForParams = null; }
      } else if (s.includes('?')) {
        try { urlForParams = new URL('https://dummy?' + s.split('?')[1]); } catch (e) { urlForParams = null; }
      }

      if (urlForParams) {
        const params = new URLSearchParams(urlForParams.search || urlForParams.hash?.substring(1) || '');
        const pa = params.get('pa') || params.get('vpa') || params.get('upi') || params.get('address');
        const pn = params.get('pn') || params.get('name');
        const phoneParam = params.get('phone') || params.get('mobile') || params.get('msisdn');

        if (pa) {
          upiId = pa;
          isPayment = true;
        }
        if (pn) name = pn;
        if (phoneParam) phone = phoneParam;
      }
    } catch (e) {}

    if (!upiId) {
      const vpaMatch = s.match(/[a-z0-9.\-_]{2,}@[a-z0-9.\-]{2,}/i);
      if (vpaMatch) {
        upiId = vpaMatch[0];
        isPayment = true;
      }
    }

    if (!phone) {
      const phoneParamMatch = s.match(/(?:phone|mobile|msisdn|tel|ph)=\+?([0-9\-\s\(\)]{6,20})/i);
      if (phoneParamMatch) {
        phone = phoneParamMatch[1].replace(/\D/g, '');
      } else {
        const digitMatch = s.match(/(\+91|91|0)?([6-9][0-9]{9})/);
        if (digitMatch) {
          const prefix = digitMatch[1] || '';
          const core = digitMatch[2];
          phone = (prefix || '') + core;
        } else {
          const any10 = s.match(/([0-9]{10,12})/);
          if (any10) phone = any10[1];
        }
      }
    }

    if (!isPayment && (lower.startsWith('upi:') || lower.includes('upi://') || lower.includes('upi=') || lower.includes('paytm') || lower.includes('googlepay') || lower.includes('gpay'))) {
      isPayment = true;
    }

    return { isPayment, upiId, name, phone };
  };

  const startCamera = async () => {
    setPayload(null);
    setUrlCheckResult(null);
    setPaymentInfo(null);
    setStatus('Requesting camera access...');
    try {
      let stream: MediaStream | null = null;
      try {
        stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: { ideal: 'environment' } } });
      } catch (err) {
        console.warn('facingMode failed, trying generic camera', err);
        stream = await navigator.mediaDevices.getUserMedia({ video: true });
      }

      streamRef.current = stream!;
      if (videoRef.current) {
        const v = videoRef.current;
        v.srcObject = stream;
        v.muted = true;
        v.playsInline = true;
        v.autoplay = true;
        await v.play();
      }

      setScanning(true);
      setStatus('Camera started — scanning...');
      if (scanIntervalRef.current) {
        window.clearInterval(scanIntervalRef.current);
        scanIntervalRef.current = null;
      }
      scanIntervalRef.current = window.setInterval(runScan, 200);
    } catch (err: any) {
      console.error('[QRScanner] startCamera error', err);
      if (err && err.name === 'NotAllowedError') setStatus('Camera permission denied. Allow camera to scan.');
      else setStatus('Camera not available or permission blocked. Try image upload.');
      setScanning(false);
    }
  };

  const stopCamera = () => {
    setScanning(false);
    if (scanIntervalRef.current) {
      window.clearInterval(scanIntervalRef.current);
      scanIntervalRef.current = null;
    }
    const stream = streamRef.current;
    if (stream) {
      stream.getTracks().forEach(t => t.stop());
      streamRef.current = null;
    }
    if (videoRef.current) {
      try { videoRef.current.pause(); } catch {}
      // @ts-ignore
      videoRef.current.srcObject = null;
    }
    setStatus('Camera stopped.');
  };

  const runScan = () => {
    try {
      const video = videoRef.current;
      const canvas = canvasRef.current;
      if (!video || !canvas) return;
      if (video.readyState !== HTMLMediaElement.HAVE_ENOUGH_DATA) return;

      if (canvas.width !== video.videoWidth || canvas.height !== video.videoHeight) {
        canvas.width = video.videoWidth;
        canvas.height = video.videoHeight;
      }
      const ctx = canvas.getContext('2d');
      if (!ctx) return;
      ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
      const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);

      const code = jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: 'attemptBoth' });
      if (code && code.data) {
        setPayload(code.data);
        setStatus('QR decoded from camera.');
        stopCamera();

        const payment = parsePaymentFromPayload(code.data);
        setPaymentInfo(payment.isPayment ? payment : null);

        if (isLikelyUrl(code.data)) {
          const res = checkUrlString(code.data);
          setUrlCheckResult(res);
          const issuesMsg = res.issues.length ? res.issues.map(i => `• ${i}`).join('\n') : 'No obvious risks detected.';
          if (res.status === 'Dangerous') {
            setStatus('⚠️ Dangerous QR detected — do not open the link.');
            emitAlert(res.status, `This QR contains a URL flagged as DANGEROUS.\n\nIssues:\n${issuesMsg}`);
          } else if (res.status === 'Suspicious') {
            setStatus('⚠️ Suspicious QR detected — proceed with caution.');
            emitAlert(res.status, `This QR contains a URL flagged as SUSPICIOUS.\n\nIssues:\n${issuesMsg}`);
          } else {
            setStatus('✅ Safe QR detected.');
            emitAlert(res.status, 'This QR contains a URL and no obvious risks were found.');
          }
        } else {
          setUrlCheckResult(null);
          if (payment.isPayment) {
            emitAlert('SafeText', 'Payment QR decoded — extracted payment fields.');
            setStatus('✅ Payment QR decoded — extracted fields below.');
          } else {
            emitAlert('SafeText', 'QR decoded (non-URL payload).');
          }
        }
      }
    } catch (err) {
      console.error('[QRScanner] runScan error', err);
    }
  };

  const handleImageUpload = async (file?: File) => {
    setPayload(null);
    setUrlCheckResult(null);
    setPaymentInfo(null);
    setStatus('Processing image...');
    try {
      const f = file ?? (document.getElementById('qr-file') as HTMLInputElement)?.files?.[0];
      if (!f) return setStatus('Choose an image file with a QR code.');
      const dataUrl = await new Promise<string>((res, rej) => {
        const r = new FileReader();
        r.onload = () => res(String(r.result));
        r.onerror = rej;
        r.readAsDataURL(f);
      });
      const img = new Image();
      img.src = dataUrl;
      img.onload = () => {
        const canvas = canvasRef.current!;
        canvas.width = img.width;
        canvas.height = img.height;
        const ctx = canvas.getContext('2d')!;
        ctx.drawImage(img, 0, 0);
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
        const result = jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: 'attemptBoth' });
        if (result && result.data) {
          setPayload(result.data);
          setStatus('QR decoded from image.');

          const payment = parsePaymentFromPayload(result.data);
          setPaymentInfo(payment.isPayment ? payment : null);

          if (isLikelyUrl(result.data)) {
            const res = checkUrlString(result.data);
            setUrlCheckResult(res);
            const issuesMsg = res.issues.length ? res.issues.map(i => `• ${i}`).join('\n') : 'No obvious risks detected.';
            if (res.status === 'Dangerous') {
              setStatus('⚠️ Dangerous QR detected — do not open the link.');
              emitAlert(res.status, `This QR contains a URL flagged as DANGEROUS.\n\nIssues:\n${issuesMsg}`);
            } else if (res.status === 'Suspicious') {
              setStatus('⚠️ Suspicious QR detected — proceed with caution.');
              emitAlert(res.status, `This QR contains a URL flagged as SUSPICIOUS.\n\nIssues:\n${issuesMsg}`);
            } else {
              setStatus('✅ Safe QR detected.');
              emitAlert(res.status, 'This QR contains a URL and no obvious risks were found.');
            }
          } else {
            if (payment.isPayment) {
              emitAlert('SafeText', 'Payment QR decoded — extracted payment fields.');
              setStatus('✅ Payment QR decoded — extracted fields below.');
            } else {
              setUrlCheckResult(null);
              emitAlert('SafeText', 'QR decoded (non-URL payload).');
            }
          }
        } else {
          setStatus('No QR code found in image.');
        }
      };
      img.onerror = () => {
        console.error('[QRScanner] image load failed');
        setStatus('Failed to load image.');
      };
    } catch (err) {
      console.error('[QRScanner] handleImageUpload error', err);
      setStatus('Image processing failed.');
    }
  };

  const handleManual = (txt: string) => {
    setPayload(txt || null);
    setUrlCheckResult(null);
    setPaymentInfo(null);
    if (txt) {
      const payment = parsePaymentFromPayload(txt);
      setPaymentInfo(payment.isPayment ? payment : null);

      if (isLikelyUrl(txt)) {
        const res = checkUrlString(txt);
        setUrlCheckResult(res);
        const issuesMsg = res.issues.length ? res.issues.map(i => `• ${i}`).join('\n') : 'No obvious risks detected.';
        if (res.status === 'Dangerous') {
          setStatus('⚠️ Dangerous URL (manual input).');
          emitAlert(res.status, `Manual URL flagged DANGEROUS.\n\nIssues:\n${issuesMsg}`);
        } else if (res.status === 'Suspicious') {
          setStatus('⚠️ Suspicious URL (manual input).');
          emitAlert(res.status, `Manual URL flagged SUSPICIOUS.\n\nIssues:\n${issuesMsg}`);
        } else {
          setStatus('✅ Safe URL (manual input).');
          emitAlert(res.status, 'Manual input looks safe.');
        }
      } else if (payment.isPayment) {
        setStatus('✅ Payment string detected (manual input).');
        emitAlert('SafeText', 'Manual input looks like a payment/UPI payload.');
      }
    }
  };

  const singleSentenceOutput = (() => {
    if (!payload) return null;
    if (isLikelyUrl(payload) && urlCheckResult) {
      return sentenceForStatus(urlCheckResult.status);
    }
    return 'This QR code is Safe — no harmful or suspicious activity detected.';
  })();

  return (
    <div className="space-y-6">
      <div className="flex items-center gap-3 mb-2">
        <Camera className="w-6 h-6 text-primary" />
        <h2 className="font-display text-2xl font-bold">QR Code Security Scanner</h2>
      </div>

      <div className="p-4 bg-muted/30 rounded-lg">
        <p className="text-sm text-muted-foreground">
          <Info className="w-4 h-4 inline mr-1" />
          Scan QR via camera or upload an image. If QR contains a URL, the tool will automatically check it for risks.
          If it looks like a payment/UPI QR, the tool will attempt to extract UPI ID and phone number.
        </p>
      </div>

      <div className="flex gap-3 items-center">
        <Button variant="neon" onClick={() => (scanning ? stopCamera() : startCamera())}>
          {scanning ? 'Stop Camera' : 'Start Camera'}
        </Button>

        <input id="qr-file" type="file" accept="image/*" onChange={() => handleImageUpload()} />

        <div className="ml-auto text-sm text-muted-foreground">
          {cameraAvailable === null ? 'Checking camera...' : cameraAvailable ? 'Camera available' : 'No camera'}
        </div>
      </div>

      <video ref={videoRef} className={cn(scanning ? 'block w-full rounded-lg' : 'hidden')} style={{ maxHeight: 360 }} playsInline muted autoPlay />

      <canvas ref={canvasRef} className="hidden" />

      <div>
        <p className="text-sm text-muted-foreground mb-2">Decoded payload:</p>
        <Textarea
          placeholder="Decoded QR payload shows here..."
          value={payload ?? ''}
          onChange={(e) => handleManual(e.target.value)}
          rows={4}
        />
      </div>

      {payload && (
        <div className="p-4 rounded-lg space-y-3" style={{ border: '1px solid rgba(255,255,255,0.03)' }}>
          <div className="flex items-start justify-between gap-3">
            <div className="font-mono break-all text-sm">{payload}</div>

            <div className="flex items-center gap-2">
              <Button variant="outline" onClick={() => navigator.clipboard.writeText(payload)}>Copy</Button>

              {isLikelyUrl(payload) ? (
                <>
                  <Button variant="neon" onClick={() => {
                    const res = checkUrlString(payload);
                    setUrlCheckResult(res);
                    const issuesMsg = res.issues.length ? res.issues.map(i => `• ${i}`).join('\n') : 'No obvious risks detected.';
                    if (res.status === 'Dangerous') {
                      setStatus('⚠️ Dangerous QR detected — do not open the link.');
                      emitAlert(res.status, `This QR contains a URL flagged as DANGEROUS.\n\nIssues:\n${issuesMsg}`);
                    } else if (res.status === 'Suspicious') {
                      setStatus('⚠️ Suspicious QR detected — proceed with caution.');
                      emitAlert(res.status, `This QR contains a URL flagged as SUSPICIOUS.\n\nIssues:\n${issuesMsg}`);
                    } else {
                      setStatus('✅ Safe QR detected.');
                      emitAlert(res.status, 'This QR contains a URL and no obvious risks were found.');
                    }
                  }}>Run URL Check</Button>

                  <a
                    href={payload}
                    target="_blank"
                    rel="noreferrer"
                    className={cn("underline px-3 py-1 rounded", urlCheckResult?.status === 'Dangerous' ? 'text-destructive' : '')}
                    onClick={(e) => {
                      if (urlCheckResult?.status === 'Dangerous') {
                        if (!confirm('Warning: this link is flagged DANGEROUS. Are you sure you want to open it?')) e.preventDefault();
                      }
                    }}
                  >
                    Open
                  </a>
                </>
              ) : null}
            </div>
          </div>

          {singleSentenceOutput && (
            <div
              className={cn(
                "p-3 rounded-md mt-2 text-sm font-semibold",
                (urlCheckResult && urlCheckResult.status !== 'Safe') ?
                  "bg-red-900/20 border border-red-500/30 text-red-300 shadow-[0_0_12px_rgba(239,68,68,0.12)]" :
                  "bg-emerald-900/20 border border-emerald-500/30 text-emerald-300 shadow-[0_0_12px_rgba(16,185,129,0.10)]"
              )}
            >
              {singleSentenceOutput}
            </div>
          )}

          {paymentInfo && paymentInfo.isPayment && (
            <div className="mt-2 p-3 bg-muted/10 rounded-md">
              <div className="flex items-center gap-2 mb-2">
                <CheckCircle className="w-5 h-5 text-green-400" />
                <span className="font-display font-semibold">Payment fields detected</span>
              </div>

              <div className="space-y-2 text-sm">
                {paymentInfo.upiId && (
                  <div className="flex items-center justify-between gap-3">
                    <div className="truncate">
                      <div className="text-xs text-muted-foreground">UPI ID</div>
                      <div className="font-mono">{paymentInfo.upiId}</div>
                    </div>
                    <div className="flex gap-2">
                      <Button variant="outline" onClick={() => navigator.clipboard.writeText(paymentInfo.upiId)}>Copy</Button>
                    </div>
                  </div>
                )}

                {paymentInfo.name && (
                  <div className="flex items-center justify-between gap-3">
                    <div>
                      <div className="text-xs text-muted-foreground">Payee Name</div>
                      <div className="truncate">{paymentInfo.name}</div>
                    </div>
                    <div>
                      <Button variant="outline" onClick={() => paymentInfo.name && navigator.clipboard.writeText(paymentInfo.name)}>Copy</Button>
                    </div>
                  </div>
                )}

                {paymentInfo.phone && (
                  <div className="flex items-center justify-between gap-3">
                    <div>
                      <div className="text-xs text-muted-foreground">Phone</div>
                      <div className="font-mono">{paymentInfo.phone}</div>
                    </div>
                    <div>
                      <Button variant="outline" onClick={() => paymentInfo.phone && navigator.clipboard.writeText(paymentInfo.phone)}>Copy</Button>
                    </div>
                  </div>
                )}

                {!paymentInfo.upiId && !paymentInfo.phone && (
                  <div className="text-sm text-muted-foreground">Detected as payment-like payload but no UPI ID or phone could be extracted.</div>
                )}
              </div>
            </div>
          )}

          {urlCheckResult && (
            <div className="mt-2">
              <div className="flex items-center gap-2 mb-2">
                {urlCheckResult.status === 'Safe' && <CheckCircle className="w-5 h-5 text-green-400" />}
                {urlCheckResult.status === 'Suspicious' && <AlertTriangle className="w-5 h-5 text-yellow-500" />}
                {urlCheckResult.status === 'Dangerous' && <XCircle className="w-5 h-5 text-destructive" />}
                <span className="font-display font-semibold">{urlCheckResult.status}</span>
              </div>

              {urlCheckResult.issues.length > 0 ? (
                <ul className="text-sm space-y-1">
                  {urlCheckResult.issues.map((iss, i) => (
                    <li key={i} className="flex items-start gap-2">
                      <AlertTriangle className="w-4 h-4 mt-0.5 text-yellow-500" />
                      <span className="text-muted-foreground">{iss}</span>
                    </li>
                  ))}
                </ul>
              ) : (
                <p className="text-sm text-muted-foreground">No obvious risks detected.</p>
              )}
            </div>
          )}
        </div>
      )}
      {info && <p className="text-sm text-muted-foreground">{info}</p>}
    </div>
  );
};

const normalizeGmail = (email: string) => {
  const e = email.trim().toLowerCase();
  if (!e.includes('@')) return e;
  const [local, domain] = e.split('@');
  if (!domain.includes('gmail.com') && !domain.includes('googlemail.com')) {
    return e;
  }
  const localNoPlus = local.split('+')[0].replace(/\./g, '');
  return `${localNoPlus}@gmail.com`;
};

const normalizePhone = (p: string) => {
  if (!p) return '';
  const digits = p.replace(/[^\d]/g, '');
  if (digits.length <= 10) return digits;
  return digits.slice(-10);
};

const isGmailAddress = (s: string) => /\b[A-Za-z0-9._%+-]+@gmail\.com\b/i.test(s);

export const VECTOR_LABELS: Record<string, string> = {
  'third-party': 'Third-party vendor compromise',
  'misconfigured-storage': 'Misconfigured storage / public backup',
  'scraped': 'Scraped from public profiles / forums',
  'paste-site': 'Posted on paste/forum site',
  'credential-stuffing': 'Credential stuffing / password reuse',
  'insider': 'Insider leak / accidental exposure',
  'unknown': 'Unknown / not specified'
};

const simulatedBreaches = [
  {
    id: 'breach-1',
    title: 'Example Social Site Breach',
    date: '2023-05-10',
    fields: ['email', 'password', 'name'],
    sampleMatches: ['alice@example.com', 'bob@gmail.com', '+919876543210'],
    vector: 'third-party',
    notes: 'Demo breach from a third-party vendor integration',
    confidence: 'high',
  },
  {
    id: 'breach-2',
    title: 'Forum Dump 2022',
    date: '2022-11-01',
    fields: ['email', 'phone'],
    sampleMatches: ['charlie@gmail.com', '9876543210'],
    vector: 'paste-site',
    notes: 'Public paste site leak example',
    confidence: 'medium',
  },
];

const BreachCheckerLocal: React.FC = () => {
  const [identifier, setIdentifier] = useState('');
  const [consent, setConsent] = useState(false);
  const [results, setResults] = useState<Array<{
    breachId: string; breachTitle: string; breachDate: string; leakedFields: string[]; reason: string; vector?: string; notes?: string; confidence?: string; for?: string;
  }> | null>(null);
  const [info, setInfo] = useState<string | null>(null);

  const breaches = useMemo(() => simulatedBreaches, []);

  const runSingleCheck = () => {
    setInfo(null);
    setResults(null);
    if (!identifier.trim()) return setInfo('Please enter an email or phone number.');
    if (!consent) return setInfo('Please give consent before checking.');

    const id = identifier.trim();
    const matches: any[] = [];

    const isEmail = id.includes('@');
    const normalizedEmail = isEmail ? id.toLowerCase() : '';
    const normalizedGmail = isEmail && isGmailAddress(id) ? normalizeGmail(id) : null;
    const normalizedPhone = !isEmail ? normalizePhone(id) : null;

    for (const b of breaches) {
      for (const sample of b.sampleMatches) {
        const sRaw = String(sample);
        const s = sRaw.toLowerCase();

        if (isEmail && s === normalizedEmail) {
          matches.push({ breachId: b.id, breachTitle: b.title, breachDate: b.date, leakedFields: b.fields, reason: 'Exact match', vector: b.vector, notes: b.notes, confidence: b.confidence });
          break;
        }
        if (!isEmail && normalizedPhone && s.replace(/[^\d]/g, '').endsWith(normalizedPhone)) {
          matches.push({ breachId: b.id, breachTitle: b.title, breachDate: b.date, leakedFields: b.fields, reason: 'Phone suffix match', vector: b.vector, notes: b.notes, confidence: b.confidence });
          break;
        }

        if (normalizedGmail) {
          const sampleNorm = isGmailAddress(s) ? normalizeGmail(s) : s;
          if (sampleNorm === normalizedGmail) {
            matches.push({ breachId: b.id, breachTitle: b.title, breachDate: b.date, leakedFields: b.fields, reason: 'Gmail-normalized match', vector: b.vector, notes: b.notes, confidence: b.confidence });
            break;
          }
        }

        if (isEmail && normalizedEmail.includes('@')) {
          const enteredDomain = normalizedEmail.split('@')[1];
          if (s.includes('@' + enteredDomain)) {
            matches.push({ breachId: b.id, breachTitle: b.title, breachDate: b.date, leakedFields: b.fields, reason: `Domain match (${enteredDomain})`, vector: b.vector, notes: b.notes, confidence: b.confidence });
            break;
          }
        }
      }
    }

    const priority: Record<string, number> = { 'Exact match': 4, 'Gmail-normalized match': 3, 'Phone suffix match': 3, 'Domain match': 1 };
    const dedup = new Map<string, any>();
    for (const m of matches) {
      const cur = dedup.get(m.breachId);
      const reasonKey = m.reason.startsWith('Domain match') ? 'Domain match' : m.reason;
      const curReasonKey = cur ? (cur.reason.startsWith('Domain match') ? 'Domain match' : cur.reason) : null;
      if (!cur || (priority[reasonKey] || 0) > (priority[curReasonKey] || 0)) dedup.set(m.breachId, m);
    }
    const final = Array.from(dedup.values());
    setResults(final.length ? final : []);
    if (final.length === 0) setInfo('No breaches found in local dataset (demo only).');
  };

  const handleCsvUpload = async (file?: File) => {
    setInfo(null);
    setResults(null);
    if (!file) return setInfo('Choose a CSV file (one email/phone per line).');
    if (!consent) return setInfo('Please give consent before bulk checking.');

    const text = await file.text();
    const lines = text.split(/\r?\n/).map(l => l.trim()).filter(Boolean);
    const aggregate: any[] = [];

    for (const line of lines) {
      const id = line;
      const isEmail = id.includes('@');
      const normEmail = isEmail ? id.toLowerCase() : '';
      const normGmail = isEmail && isGmailAddress(id) ? normalizeGmail(id) : null;
      const normPhone = !isEmail ? normalizePhone(id) : null;

      const localMatches: any[] = [];
      for (const b of breaches) {
        for (const sample of b.sampleMatches) {
          const sRaw = String(sample);
          const s = sRaw.toLowerCase();
          if (isEmail && s === normEmail) {
            localMatches.push({ breachId: b.id, breachTitle: b.title, breachDate: b.date, leakedFields: b.fields, reason: 'Exact match', vector: b.vector, notes: b.notes, confidence: b.confidence });
            break;
          }
          if (!isEmail && normPhone && s.replace(/[^\d]/g, '').endsWith(normPhone)) {
            localMatches.push({ breachId: b.id, breachTitle: b.title, breachDate: b.date, leakedFields: b.fields, reason: 'Phone suffix match', vector: b.vector, notes: b.notes, confidence: b.confidence });
            break;
          }
          if (normGmail) {
            const sampleNorm = isGmailAddress(s) ? normalizeGmail(s) : s;
            if (sampleNorm === normGmail) {
              localMatches.push({ breachId: b.id, breachTitle: b.title, breachDate: b.date, leakedFields: b.fields, reason: 'Gmail-normalized match', vector: b.vector, notes: b.notes, confidence: b.confidence });
              break;
            }
          }
          if (isEmail && normEmail.includes('@')) {
            const enteredDomain = normEmail.split('@')[1];
            if (s.includes('@' + enteredDomain)) {
              localMatches.push({ breachId: b.id, breachTitle: b.title, breachDate: b.date, leakedFields: b.fields, reason: `Domain match (${enteredDomain})`, vector: b.vector, notes: b.notes, confidence: b.confidence });
              break;
            }
          }
        }
      }
      const ded = new Map<string, any>();
      const priority: Record<string, number> = { 'Exact match': 4, 'Gmail-normalized match': 3, 'Phone suffix match': 3, 'Domain match': 1 };
      for (const m of localMatches) {
        const key = m.reason.startsWith('Domain match') ? 'Domain match' : m.reason;
        const cur = ded.get(m.breachId);
        const curKey = cur ? (cur.reason.startsWith('Domain match') ? 'Domain match' : cur.reason) : null;
        if (!cur || (priority[key] || 0) > (priority[curKey] || 0)) ded.set(m.breachId, m);
      }
      for (const m of Array.from(ded.values())) {
        aggregate.push({ ...m, for: id });
      }
    }

    setResults(aggregate.length ? aggregate : []);
    if (aggregate.length === 0) setInfo('No matches for any entries in uploaded CSV (demo dataset).');
  };

  const exportJSON = () => {
    const payload = { checkedAt: new Date().toISOString(), identifier, results };
    const blob = new Blob([JSON.stringify(payload, null, 2)], { type: 'application/json' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `breach-check-${(identifier || 'bulk').replace(/[^a-z0-9]/gi,'_')}.json`;
    a.click();
    URL.revokeObjectURL(url);
  };

  const exportCSV = () => {
    if (!results || results.length === 0) return;
    const lines = ['breachId,breachTitle,breachDate,leakedFields,reason,for'];
    for (const r of results) {
      lines.push([r.breachId, r.breachTitle, r.breachDate, (r.leakedFields || []).join('|'), r.reason, r.for || identifier].map(v => `"${String(v).replace(/"/g,'""')}"`).join(','));
    }
    const blob = new Blob([lines.join('\n')], { type: 'text/csv' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = `breach-results-${(identifier || 'bulk').replace(/[^a-z0-9]/gi,'_')}.csv`;
    a.click();
    URL.revokeObjectURL(url);
  };

  return (
    <div className="space-y-6">
      <div className="flex items-center gap-3">
        <AlertTriangle className="w-6 h-6 text-accent" />
        <h2 className="font-display text-2xl font-bold">Local Breach Checker (Gmail & Phone)</h2>
      </div>

      <p className="text-sm text-muted-foreground">
        This tool runs <strong>locally</strong> and uses a simulated breach dataset. No external API is called.
      </p>

      <div className="grid grid-cols-1 md:grid-cols-3 gap-3 items-center">
        <Input placeholder="Enter Gmail or phone (e.g., alice@gmail.com or +919876543210)" value={identifier} onChange={(e) => setIdentifier(e.target.value)} />
        <label className="flex items-center gap-2 text-sm">
          <input type="checkbox" checked={consent} onChange={(e) => setConsent(e.target.checked)} />
          <span>I consent to local checking (no data leaves this device)</span>
        </label>
        <div className="flex gap-2">
          <Button variant="neon" onClick={runSingleCheck}>Check</Button>
          <label className="cursor-pointer inline-flex items-center px-3 py-2 border rounded bg-muted/10">
            <FileText className="w-4 h-4 mr-2" />
            <input type="file" accept=".csv" style={{ display: 'none' }} onChange={(e) => e.target.files && handleCsvUpload(e.target.files[0])} />
            Bulk CSV
          </label>
        </div>
      </div>

      {info && <p className="text-sm text-muted-foreground">{info}</p>}

      {results && results.length > 0 && (
        <div className="space-y-4">
          {results.map((r, idx) => (
            <div key={idx} className="p-4 bg-muted/20 rounded border">
              <div className="flex items-start justify-between">
                <div>
                  <h4 className="font-semibold">{r.breachTitle} <span className="text-sm text-muted-foreground">({r.breachDate})</span></h4>
                  <p className="text-sm text-muted-foreground mt-1">Leaked fields: {r.leakedFields.join(', ')}</p>
                  <p className="text-sm text-muted-foreground mt-1"><strong>How data breach (inferred):</strong> {VECTOR_LABELS[r.vector || 'unknown']}</p>
                  {r.notes && <p className="text-sm text-muted-foreground mt-1"><em>{r.notes}</em></p>}
                </div>
                <div className="text-right">
                  <div className="text-xs text-muted-foreground">Matched because</div>
                  <div className="font-mono text-sm">{r.reason}{r.for ? ` (for ${r.for})` : ''}</div>
                  <div className="text-xs text-muted-foreground mt-2">Confidence</div>
                  <div className="font-semibold">{r.confidence || 'medium'}</div>
                </div>
              </div>
            </div>
          ))}

          <div className="flex gap-2">
            <Button variant="outline" onClick={() => navigator.clipboard.writeText(JSON.stringify(results, null, 2))}>Copy Results</Button>
            <Button variant="neon" onClick={exportJSON}><Download className="w-4 h-4 mr-2 inline" />Download JSON</Button>
            <Button variant="outline" onClick={exportCSV}>Download CSV</Button>
          </div>
        </div>
      )}
    </div>
  );
};

export default ToolsDashboard;
