import React, { useState, useEffect, useRef, useCallback } from 'react';
import { useQuery, useConvex } from 'convex/react';
import { api } from '../../../../convex/_generated/api';
import Avatar from './Avatar';
import AvatarCropModal from './AvatarCropModal';
import { useTheme, THEMES, THEME_LABELS } from '../contexts/ThemeContext';
import { useVoice } from '../contexts/VoiceContext';
import { useSearch } from '../contexts/SearchContext';
import { usePlatform } from '../platform';

const THEME_PREVIEWS = {
  [THEMES.LIGHT]: { bg: '#ffffff', sidebar: '#f2f3f5', tertiary: '#e3e5e8', text: '#313338' },
  [THEMES.DARK]: { bg: '#313338', sidebar: '#2b2d31', tertiary: '#1e1f22', text: '#f2f3f5' },
  [THEMES.ASH]: { bg: '#202225', sidebar: '#1a1b1e', tertiary: '#111214', text: '#f0f1f3' },
  [THEMES.ONYX]: { bg: '#0c0c14', sidebar: '#080810', tertiary: '#000000', text: '#e0def0' },
};

const TABS = [
  { id: 'account', label: 'My Account', section: 'USER SETTINGS' },
  { id: 'security', label: 'Security', section: 'USER SETTINGS' },
  { id: 'appearance', label: 'Appearance', section: 'USER SETTINGS' },
  { id: 'voice', label: 'Voice & Video', section: 'APP SETTINGS' },
  { id: 'keybinds', label: 'Keybinds', section: 'APP SETTINGS' },
  { id: 'search', label: 'Search', section: 'APP SETTINGS' },
];

const UserSettings = ({ onClose, userId, username, onLogout }) => {
  const [activeTab, setActiveTab] = useState('account');

  useEffect(() => {
    const handleKey = (e) => {
      if (e.key === 'Escape') onClose();
    };
    window.addEventListener('keydown', handleKey);
    return () => window.removeEventListener('keydown', handleKey);
  }, [onClose]);

  const renderSidebar = () => {
    let lastSection = null;
    const items = [];

    TABS.forEach((tab, i) => {
      if (tab.section !== lastSection) {
        if (lastSection !== null) {
          items.push(<div key={`sep-${i}`} style={{ height: '1px', backgroundColor: 'var(--border-subtle)', margin: '8px 10px' }} />);
        }
        items.push(
          <div key={`hdr-${tab.section}`} style={{
            fontSize: '12px', fontWeight: '700', color: 'var(--text-muted)',
            marginBottom: '6px', textTransform: 'uppercase', padding: '0 10px'
          }}>
            {tab.section}
          </div>
        );
        lastSection = tab.section;
      }
      items.push(
        <div
          key={tab.id}
          onClick={() => setActiveTab(tab.id)}
          style={{
            padding: '6px 10px', borderRadius: '4px', cursor: 'pointer', marginBottom: '2px', fontSize: '15px',
            backgroundColor: activeTab === tab.id ? 'var(--background-modifier-selected)' : 'transparent',
            color: activeTab === tab.id ? 'var(--header-primary)' : 'var(--header-secondary)',
          }}
        >
          {tab.label}
        </div>
      );
    });

    items.push(<div key="sep-logout" style={{ height: '1px', backgroundColor: 'var(--border-subtle)', margin: '8px 10px' }} />);
    items.push(
      <div
        key="logout"
        onClick={onLogout}
        style={{
          padding: '6px 10px', borderRadius: '4px', cursor: 'pointer', fontSize: '15px',
          color: '#ed4245', display: 'flex', justifyContent: 'space-between', alignItems: 'center',
        }}
      >
        Log Out
        <svg width="16" height="16" viewBox="0 0 24 24" fill="none">
          <path d="M16 17L21 12L16 7" stroke="#ed4245" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
          <path d="M21 12H9" stroke="#ed4245" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
          <path d="M9 21H5C4.46957 21 3.96086 20.7893 3.58579 20.4142C3.21071 20.0391 3 19.5304 3 19V5C3 4.46957 3.21071 3.96086 3.58579 3.58579C3.96086 3.21071 4.46957 3 5 3H9" stroke="#ed4245" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
        </svg>
      </div>
    );

    return items;
  };

  return (
    <div style={{
      position: 'fixed', top: 0, left: 0, right: 0, bottom: 0,
      backgroundColor: 'var(--bg-primary)', zIndex: 1000,
      display: 'flex', color: 'var(--text-normal)',
    }}>
      {/* Sidebar */}
      <div style={{
        width: '218px', backgroundColor: 'var(--bg-secondary)',
        display: 'flex', flexDirection: 'column', alignItems: 'flex-end',
        padding: '60px 6px 60px 20px', overflowY: 'auto',
      }}>
        <div style={{ width: '100%' }}>
          {renderSidebar()}
        </div>
      </div>

      {/* Content */}
      <div style={{ flex: 1, display: 'flex', justifyContent: 'flex-start', overflowY: 'auto' }}>
        <div style={{ flex: 1, maxWidth: '740px', padding: '60px 40px 80px', position: 'relative' }}>
          {activeTab === 'account' && <MyAccountTab userId={userId} username={username} />}
          {activeTab === 'security' && <SecurityTab />}
          {activeTab === 'appearance' && <AppearanceTab />}
          {activeTab === 'voice' && <VoiceVideoTab />}
          {activeTab === 'keybinds' && <KeybindsTab />}
          {activeTab === 'search' && <SearchTab userId={userId} />}
        </div>

        {/* Right spacer with close button */}
        <div style={{ flex: '0 0 36px', paddingTop: '60px', marginLeft: '8px' }}>
          <button
            onClick={onClose}
            style={{
              width: '36px', height: '36px', borderRadius: '50%',
              border: '2px solid var(--header-secondary)', background: 'transparent',
              color: 'var(--header-secondary)', cursor: 'pointer',
              display: 'flex', alignItems: 'center', justifyContent: 'center',
              fontSize: '18px',
            }}
          >
            ✕
          </button>
          <div style={{ fontSize: '13px', fontWeight: '600', color: 'var(--header-secondary)', textAlign: 'center', marginTop: '4px' }}>
            ESC
          </div>
        </div>
        <div style={{ flex: '0.5' }} />
      </div>
    </div>
  );
};

/* =========================================
   MY ACCOUNT TAB
   ========================================= */
const MyAccountTab = ({ userId, username }) => {
  const allUsers = useQuery(api.auth.getPublicKeys);
  const convex = useConvex();

  const currentUser = allUsers?.find(u => u.id === userId);

  const [displayName, setDisplayName] = useState('');
  const [aboutMe, setAboutMe] = useState('');
  const [customStatus, setCustomStatus] = useState('');
  const [avatarFile, setAvatarFile] = useState(null);
  const [avatarPreview, setAvatarPreview] = useState(null);
  const [saving, setSaving] = useState(false);
  const [hasChanges, setHasChanges] = useState(false);
  const [showCropModal, setShowCropModal] = useState(false);
  const [rawImageUrl, setRawImageUrl] = useState(null);
  const fileInputRef = useRef(null);
  const [joinSoundFile, setJoinSoundFile] = useState(null);
  const [joinSoundPreviewName, setJoinSoundPreviewName] = useState(null);
  const [removeJoinSound, setRemoveJoinSound] = useState(false);
  const joinSoundInputRef = useRef(null);
  const joinSoundAudioRef = useRef(null);

  useEffect(() => {
    if (currentUser) {
      setDisplayName(currentUser.displayName || '');
      setAboutMe(currentUser.aboutMe || '');
      setCustomStatus(currentUser.customStatus || '');
    }
  }, [currentUser]);

  useEffect(() => {
    if (!currentUser) return;
    const changed =
      displayName !== (currentUser.displayName || '') ||
      aboutMe !== (currentUser.aboutMe || '') ||
      customStatus !== (currentUser.customStatus || '') ||
      avatarFile !== null ||
      joinSoundFile !== null ||
      removeJoinSound;
    setHasChanges(changed);
  }, [displayName, aboutMe, customStatus, avatarFile, joinSoundFile, removeJoinSound, currentUser]);

  const handleAvatarChange = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    const url = URL.createObjectURL(file);
    setRawImageUrl(url);
    setShowCropModal(true);
    e.target.value = '';
  };

  const handleCropApply = (blob) => {
    const file = new File([blob], 'avatar.png', { type: 'image/png' });
    setAvatarFile(file);
    const previewUrl = URL.createObjectURL(blob);
    setAvatarPreview(previewUrl);
    if (rawImageUrl) URL.revokeObjectURL(rawImageUrl);
    setRawImageUrl(null);
    setShowCropModal(false);
  };

  const handleCropCancel = () => {
    if (rawImageUrl) URL.revokeObjectURL(rawImageUrl);
    setRawImageUrl(null);
    setShowCropModal(false);
  };

  const handleJoinSoundChange = (e) => {
    const file = e.target.files?.[0];
    if (!file) return;
    if (file.size > 10 * 1024 * 1024) {
      alert('Join sound must be under 10MB');
      e.target.value = '';
      return;
    }
    setJoinSoundFile(file);
    setJoinSoundPreviewName(file.name);
    setRemoveJoinSound(false);
    e.target.value = '';
  };

  const handleJoinSoundPreview = () => {
    if (joinSoundAudioRef.current) {
      joinSoundAudioRef.current.pause();
      joinSoundAudioRef.current = null;
    }
    let src;
    if (joinSoundFile) {
      src = URL.createObjectURL(joinSoundFile);
    } else if (currentUser?.joinSoundUrl) {
      src = currentUser.joinSoundUrl;
    }
    if (src) {
      const audio = new Audio(src);
      audio.volume = 0.5;
      audio.play().catch(e => console.error('Preview failed', e));
      joinSoundAudioRef.current = audio;
    }
  };

  const handleRemoveJoinSound = () => {
    setJoinSoundFile(null);
    setJoinSoundPreviewName(null);
    setRemoveJoinSound(true);
  };

  const handleSave = async () => {
    if (!userId || saving) return;
    setSaving(true);
    try {
      let avatarStorageId;
      if (avatarFile) {
        const uploadUrl = await convex.mutation(api.files.generateUploadUrl);
        const res = await fetch(uploadUrl, {
          method: 'POST',
          headers: { 'Content-Type': avatarFile.type },
          body: avatarFile,
        });
        const { storageId } = await res.json();
        avatarStorageId = storageId;
      }
      let joinSoundStorageId;
      if (joinSoundFile) {
        const jsUploadUrl = await convex.mutation(api.files.generateUploadUrl);
        const jsRes = await fetch(jsUploadUrl, {
          method: 'POST',
          headers: { 'Content-Type': joinSoundFile.type },
          body: joinSoundFile,
        });
        const jsData = await jsRes.json();
        joinSoundStorageId = jsData.storageId;
      }
      const args = { userId, displayName, aboutMe, customStatus };
      if (avatarStorageId) args.avatarStorageId = avatarStorageId;
      if (joinSoundStorageId) args.joinSoundStorageId = joinSoundStorageId;
      if (removeJoinSound) args.removeJoinSound = true;
      await convex.mutation(api.auth.updateProfile, args);
      setAvatarFile(null);
      setJoinSoundFile(null);
      setJoinSoundPreviewName(null);
      setRemoveJoinSound(false);
      if (avatarPreview) {
        URL.revokeObjectURL(avatarPreview);
        setAvatarPreview(null);
      }
    } catch (err) {
      console.error('Failed to save profile:', err);
      alert('Failed to save profile: ' + err.message);
    } finally {
      setSaving(false);
    }
  };

  const handleReset = () => {
    if (currentUser) {
      setDisplayName(currentUser.displayName || '');
      setAboutMe(currentUser.aboutMe || '');
      setCustomStatus(currentUser.customStatus || '');
    }
    setAvatarFile(null);
    setJoinSoundFile(null);
    setJoinSoundPreviewName(null);
    setRemoveJoinSound(false);
    if (avatarPreview) {
      URL.revokeObjectURL(avatarPreview);
      setAvatarPreview(null);
    }
    if (rawImageUrl) {
      URL.revokeObjectURL(rawImageUrl);
      setRawImageUrl(null);
    }
    setShowCropModal(false);
  };

  const avatarUrl = avatarPreview || currentUser?.avatarUrl;

  return (
    <div>
      <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>My Account</h2>

      {/* Profile card */}
      <div style={{ backgroundColor: 'var(--bg-secondary)', borderRadius: '8px', overflow: 'hidden' }}>
        {/* Banner */}
        <div style={{ height: '100px', backgroundColor: 'var(--brand-experiment)' }} />

        {/* Profile body */}
        <div style={{ padding: '0 16px 16px', position: 'relative' }}>
          {/* Avatar */}
          <div
            className="user-settings-avatar-wrapper"
            onClick={() => fileInputRef.current?.click()}
            style={{ marginTop: '-40px', marginBottom: '12px', width: 'fit-content', cursor: 'pointer', position: 'relative' }}
          >
            <Avatar username={username} avatarUrl={avatarUrl} size={80} />
            <div className="user-settings-avatar-overlay">
              CHANGE<br/>AVATAR
            </div>
            <input
              ref={fileInputRef}
              type="file"
              accept="image/*"
              onChange={handleAvatarChange}
              style={{ display: 'none' }}
            />
          </div>

          {/* Fields */}
          <div style={{ display: 'flex', flexDirection: 'column', gap: '16px' }}>
            {/* Username (read-only) */}
            <div>
              <label style={{
                display: 'block', color: 'var(--header-secondary)', fontSize: '12px',
                fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px',
              }}>
                Username
              </label>
              <div style={{
                backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', padding: '10px',
                color: 'var(--text-muted)', fontSize: '16px',
              }}>
                {username}
              </div>
            </div>

            {/* Display Name */}
            <div>
              <label style={{
                display: 'block', color: 'var(--header-secondary)', fontSize: '12px',
                fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px',
              }}>
                Display Name
              </label>
              <input
                type="text"
                value={displayName}
                onChange={(e) => setDisplayName(e.target.value)}
                placeholder="How others see you in chat"
                style={{
                  width: '100%', backgroundColor: 'var(--bg-tertiary)', border: 'none',
                  borderRadius: '4px', padding: '10px', color: 'var(--text-normal)',
                  fontSize: '16px', outline: 'none', boxSizing: 'border-box',
                }}
              />
            </div>

            {/* About Me */}
            <div>
              <label style={{
                display: 'block', color: 'var(--header-secondary)', fontSize: '12px',
                fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px',
              }}>
                About Me
              </label>
              <textarea
                value={aboutMe}
                onChange={(e) => setAboutMe(e.target.value.slice(0, 190))}
                placeholder="Tell others about yourself"
                rows={3}
                style={{
                  width: '100%', backgroundColor: 'var(--bg-tertiary)', border: 'none',
                  borderRadius: '4px', padding: '10px', color: 'var(--text-normal)',
                  fontSize: '16px', outline: 'none', resize: 'none', fontFamily: 'inherit',
                  boxSizing: 'border-box',
                }}
              />
              <div style={{ fontSize: '12px', color: 'var(--text-muted)', textAlign: 'right' }}>
                {aboutMe.length}/190
              </div>
            </div>

            {/* Custom Status */}
            <div>
              <label style={{
                display: 'block', color: 'var(--header-secondary)', fontSize: '12px',
                fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px',
              }}>
                Custom Status
              </label>
              <input
                type="text"
                value={customStatus}
                onChange={(e) => setCustomStatus(e.target.value)}
                placeholder="Set a custom status"
                style={{
                  width: '100%', backgroundColor: 'var(--bg-tertiary)', border: 'none',
                  borderRadius: '4px', padding: '10px', color: 'var(--text-normal)',
                  fontSize: '16px', outline: 'none', boxSizing: 'border-box',
                }}
              />
            </div>

            {/* Voice Channel Join Sound */}
            <div>
              <label style={{
                display: 'block', color: 'var(--header-secondary)', fontSize: '12px',
                fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px',
              }}>
                Voice Channel Join Sound
              </label>
              <div style={{ display: 'flex', alignItems: 'center', gap: '8px', flexWrap: 'wrap' }}>
                <button
                  onClick={() => joinSoundInputRef.current?.click()}
                  style={{
                    backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none',
                    borderRadius: '4px', padding: '8px 16px', cursor: 'pointer',
                    fontSize: '14px', fontWeight: '500',
                  }}
                >
                  Upload Sound
                </button>
                {(joinSoundPreviewName || (!removeJoinSound && currentUser?.joinSoundUrl)) && (
                  <button
                    onClick={handleJoinSoundPreview}
                    title="Preview join sound"
                    style={{
                      backgroundColor: 'var(--bg-tertiary)', color: 'var(--text-normal)', border: 'none',
                      borderRadius: '4px', padding: '8px 12px', cursor: 'pointer', fontSize: '14px',
                      display: 'flex', alignItems: 'center', gap: '4px',
                    }}
                  >
                    <svg width="14" height="14" viewBox="0 0 24 24" fill="currentColor">
                      <polygon points="5,3 19,12 5,21" />
                    </svg>
                    Preview
                  </button>
                )}
                {(joinSoundPreviewName || (!removeJoinSound && currentUser?.joinSoundUrl)) && (
                  <button
                    onClick={handleRemoveJoinSound}
                    style={{
                      backgroundColor: 'transparent', color: '#ed4245', border: '1px solid #ed4245',
                      borderRadius: '4px', padding: '7px 12px', cursor: 'pointer', fontSize: '14px',
                    }}
                  >
                    Remove
                  </button>
                )}
                <input
                  ref={joinSoundInputRef}
                  type="file"
                  accept="audio/mpeg,audio/wav,audio/ogg,audio/webm"
                  onChange={handleJoinSoundChange}
                  style={{ display: 'none' }}
                />
              </div>
              <div style={{ fontSize: '12px', color: 'var(--text-muted)', marginTop: '6px' }}>
                {joinSoundPreviewName
                  ? `Selected: ${joinSoundPreviewName}`
                  : removeJoinSound
                    ? 'Join sound will be removed on save'
                    : currentUser?.joinSoundUrl
                      ? 'Custom sound set — plays when you join a voice channel'
                      : 'Upload a short audio file (max 10MB) — plays for everyone when you join a voice channel'
                }
              </div>
            </div>
          </div>
        </div>
      </div>

      {/* Save bar */}
      {hasChanges && (
        <div style={{
          position: 'sticky', bottom: '0', left: 0, right: 0,
          backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px',
          padding: '10px 16px', marginTop: '16px',
          display: 'flex', alignItems: 'center', justifyContent: 'flex-end', gap: '12px',
          boxShadow: '0 -2px 10px rgba(0,0,0,0.2)',
        }}>
          <span style={{ color: 'var(--text-muted)', fontSize: '14px', marginRight: 'auto' }}>
            Careful — you have unsaved changes!
          </span>
          <button
            onClick={handleReset}
            style={{
              background: 'transparent', border: 'none', color: 'var(--header-primary)',
              cursor: 'pointer', fontSize: '14px', fontWeight: '500', padding: '8px 16px',
            }}
          >
            Reset
          </button>
          <button
            onClick={handleSave}
            disabled={saving}
            style={{
              backgroundColor: '#3ba55c', color: 'white', border: 'none',
              borderRadius: '4px', padding: '8px 24px', cursor: saving ? 'not-allowed' : 'pointer',
              fontSize: '14px', fontWeight: '500', opacity: saving ? 0.7 : 1,
            }}
          >
            {saving ? 'Saving...' : 'Save Changes'}
          </button>
        </div>
      )}

      {showCropModal && rawImageUrl && (
        <AvatarCropModal
          imageUrl={rawImageUrl}
          onApply={handleCropApply}
          onCancel={handleCropCancel}
        />
      )}
    </div>
  );
};

/* =========================================
   SECURITY TAB
   ========================================= */
const SecurityTab = () => {
  const [masterKey, setMasterKey] = useState(null);
  const [revealed, setRevealed] = useState(false);
  const [confirmed, setConfirmed] = useState(false);
  const [copied, setCopied] = useState(false);

  useEffect(() => {
    const mk = sessionStorage.getItem('masterKey');
    setMasterKey(mk);
  }, []);

  const formatKey = (hex) => {
    if (!hex) return '';
    return hex.match(/.{1,4}/g).join(' ');
  };

  const handleCopy = () => {
    if (!masterKey) return;
    navigator.clipboard.writeText(masterKey).then(() => {
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    });
  };

  const handleDownload = () => {
    if (!masterKey) return;
    const content = [
      '=== RECOVERY KEY ===',
      '',
      'This is your Recovery Key for your encrypted account.',
      'Store it in a safe place. If you lose your password, this key is the ONLY way to recover your account.',
      '',
      'DO NOT share this key with anyone.',
      '',
      `Recovery Key: ${masterKey}`,
      '',
      `Exported: ${new Date().toISOString()}`,
    ].join('\n');
    const blob = new Blob([content], { type: 'text/plain' });
    const url = URL.createObjectURL(blob);
    const a = document.createElement('a');
    a.href = url;
    a.download = 'recovery-key.txt';
    a.click();
    URL.revokeObjectURL(url);
  };

  const labelStyle = {
    display: 'block', color: 'var(--header-secondary)', fontSize: '12px',
    fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px',
  };

  return (
    <div>
      <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>Security</h2>

      <div style={{ backgroundColor: 'var(--bg-secondary)', borderRadius: '8px', padding: '20px' }}>
        <h3 style={{ color: 'var(--header-primary)', margin: '0 0 8px', fontSize: '16px', fontWeight: '600' }}>
          Recovery Key
        </h3>
        <p style={{ color: 'var(--text-muted)', fontSize: '14px', margin: '0 0 16px', lineHeight: '1.4' }}>
          Your Recovery Key allows you to reset your password without losing access to your encrypted messages.
          Store it somewhere safe — if you forget your password, this is the <strong style={{ color: 'var(--text-normal)' }}>only way</strong> to recover your account.
        </p>

        {!masterKey ? (
          <div style={{
            backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', padding: '16px',
            color: 'var(--text-muted)', fontSize: '14px',
          }}>
            Recovery Key is not available in this session. Please log out and log back in to access it.
          </div>
        ) : !revealed ? (
          <div>
            {/* Warning box */}
            <div style={{
              backgroundColor: 'rgba(250, 166, 26, 0.1)', border: '1px solid rgba(250, 166, 26, 0.4)',
              borderRadius: '4px', padding: '12px', marginBottom: '16px',
            }}>
              <div style={{ color: '#faa61a', fontSize: '14px', fontWeight: '600', marginBottom: '4px' }}>
                Warning
              </div>
              <div style={{ color: 'var(--text-normal)', fontSize: '13px', lineHeight: '1.4' }}>
                Anyone with your Recovery Key can reset your password and take control of your account.
                Only reveal it in a private, secure environment.
              </div>
            </div>

            <label style={{
              display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer',
              color: 'var(--text-normal)', fontSize: '14px', marginBottom: '16px',
            }}>
              <input
                type="checkbox"
                checked={confirmed}
                onChange={(e) => setConfirmed(e.target.checked)}
                style={{ width: '16px', height: '16px', accentColor: 'var(--brand-experiment)' }}
              />
              I understand and want to reveal my Recovery Key
            </label>

            <button
              onClick={() => setRevealed(true)}
              disabled={!confirmed}
              style={{
                backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none',
                borderRadius: '4px', padding: '10px 20px', cursor: confirmed ? 'pointer' : 'not-allowed',
                fontSize: '14px', fontWeight: '500', opacity: confirmed ? 1 : 0.5,
              }}
            >
              Reveal Recovery Key
            </button>
          </div>
        ) : (
          <div>
            <label style={labelStyle}>Your Recovery Key</label>
            <div style={{
              backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', padding: '16px',
              fontFamily: 'Consolas, "Courier New", monospace', fontSize: '15px',
              color: 'var(--text-normal)', wordBreak: 'break-all', lineHeight: '1.6',
              letterSpacing: '1px', marginBottom: '12px', userSelect: 'all',
            }}>
              {formatKey(masterKey)}
            </div>

            <div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
              <button
                onClick={handleCopy}
                style={{
                  backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none',
                  borderRadius: '4px', padding: '8px 16px', cursor: 'pointer',
                  fontSize: '14px', fontWeight: '500',
                }}
              >
                {copied ? 'Copied!' : 'Copy'}
              </button>
              <button
                onClick={handleDownload}
                style={{
                  backgroundColor: 'var(--bg-tertiary)', color: 'var(--text-normal)', border: 'none',
                  borderRadius: '4px', padding: '8px 16px', cursor: 'pointer',
                  fontSize: '14px', fontWeight: '500',
                }}
              >
                Download
              </button>
              <button
                onClick={() => { setRevealed(false); setConfirmed(false); }}
                style={{
                  backgroundColor: 'transparent', color: 'var(--text-muted)', border: '1px solid var(--border-subtle)',
                  borderRadius: '4px', padding: '8px 16px', cursor: 'pointer',
                  fontSize: '14px', fontWeight: '500',
                }}
              >
                Hide
              </button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
};

/* =========================================
   APPEARANCE TAB
   ========================================= */
const AppearanceTab = () => {
  const { theme, setTheme } = useTheme();

  return (
    <div>
      <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>Appearance</h2>
      <div style={{ marginBottom: '16px' }}>
        <label style={{
          display: 'block', color: 'var(--header-secondary)', fontSize: '12px',
          fontWeight: '700', textTransform: 'uppercase', marginBottom: '12px',
        }}>
          Theme
        </label>
        <div className="theme-selector-grid">
          {Object.values(THEMES).map((themeKey) => {
            const preview = THEME_PREVIEWS[themeKey];
            const isActive = theme === themeKey;
            return (
              <div
                key={themeKey}
                className={`theme-card ${isActive ? 'active' : ''}`}
                onClick={() => setTheme(themeKey)}
              >
                <div className="theme-preview" style={{ backgroundColor: preview.bg }}>
                  <div className="theme-preview-sidebar" style={{ backgroundColor: preview.sidebar }}>
                    <div className="theme-preview-channel" style={{ backgroundColor: preview.tertiary }} />
                    <div className="theme-preview-channel" style={{ backgroundColor: preview.tertiary, width: '60%' }} />
                  </div>
                  <div className="theme-preview-chat">
                    <div className="theme-preview-message" style={{ backgroundColor: preview.sidebar, opacity: 0.6 }} />
                    <div className="theme-preview-message" style={{ backgroundColor: preview.sidebar, opacity: 0.4, width: '70%' }} />
                  </div>
                </div>
                <div className="theme-card-label">
                  <div className={`theme-radio ${isActive ? 'active' : ''}`}>
                    {isActive && <div className="theme-radio-dot" />}
                  </div>
                  <span>{THEME_LABELS[themeKey]}</span>
                </div>
              </div>
            );
          })}
        </div>
      </div>
    </div>
  );
};

/* =========================================
   VOICE & VIDEO TAB
   ========================================= */
const VoiceVideoTab = () => {
  const { switchDevice, setGlobalOutputVolume } = useVoice();
  const [inputDevices, setInputDevices] = useState([]);
  const [outputDevices, setOutputDevices] = useState([]);
  const [selectedInput, setSelectedInput] = useState(() => localStorage.getItem('voiceInputDevice') || 'default');
  const [selectedOutput, setSelectedOutput] = useState(() => localStorage.getItem('voiceOutputDevice') || 'default');
  const [inputVolume, setInputVolume] = useState(() => parseInt(localStorage.getItem('voiceInputVolume') || '100'));
  const [outputVolume, setOutputVolume] = useState(() => parseInt(localStorage.getItem('voiceOutputVolume') || '100'));
  const [micTesting, setMicTesting] = useState(false);
  const [micLevel, setMicLevel] = useState(0);
  const micStreamRef = useRef(null);
  const animFrameRef = useRef(null);
  const analyserRef = useRef(null);

  useEffect(() => {
    const enumerate = async () => {
      try {
        // Request permission to get labels
        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });
        stream.getTracks().forEach(t => t.stop());

        const devices = await navigator.mediaDevices.enumerateDevices();
        setInputDevices(devices.filter(d => d.kind === 'audioinput'));
        setOutputDevices(devices.filter(d => d.kind === 'audiooutput'));
      } catch (err) {
        console.error('Failed to enumerate devices:', err);
      }
    };
    enumerate();
  }, []);

  useEffect(() => {
    localStorage.setItem('voiceInputDevice', selectedInput);
    switchDevice('audioinput', selectedInput);
  }, [selectedInput]);

  useEffect(() => {
    localStorage.setItem('voiceOutputDevice', selectedOutput);
    switchDevice('audiooutput', selectedOutput);
  }, [selectedOutput]);

  useEffect(() => {
    localStorage.setItem('voiceInputVolume', String(inputVolume));
  }, [inputVolume]);

  useEffect(() => {
    localStorage.setItem('voiceOutputVolume', String(outputVolume));
    setGlobalOutputVolume(outputVolume);
  }, [outputVolume]);

  const startMicTest = async () => {
    try {
      const constraints = { audio: selectedInput !== 'default' ? { deviceId: { exact: selectedInput } } : true };
      const stream = await navigator.mediaDevices.getUserMedia(constraints);
      micStreamRef.current = stream;

      const audioCtx = new AudioContext();
      const source = audioCtx.createMediaStreamSource(stream);
      const analyser = audioCtx.createAnalyser();
      analyser.fftSize = 256;
      source.connect(analyser);
      analyserRef.current = analyser;

      setMicTesting(true);

      const dataArray = new Uint8Array(analyser.frequencyBinCount);
      const tick = () => {
        analyser.getByteFrequencyData(dataArray);
        const avg = dataArray.reduce((sum, v) => sum + v, 0) / dataArray.length;
        setMicLevel(Math.min(100, (avg / 128) * 100));
        animFrameRef.current = requestAnimationFrame(tick);
      };
      tick();
    } catch (err) {
      console.error('Mic test failed:', err);
    }
  };

  const stopMicTest = useCallback(() => {
    if (micStreamRef.current) {
      micStreamRef.current.getTracks().forEach(t => t.stop());
      micStreamRef.current = null;
    }
    if (animFrameRef.current) {
      cancelAnimationFrame(animFrameRef.current);
      animFrameRef.current = null;
    }
    setMicTesting(false);
    setMicLevel(0);
  }, []);

  useEffect(() => {
    return () => stopMicTest();
  }, [stopMicTest]);

  const selectStyle = {
    width: '100%', backgroundColor: 'var(--bg-tertiary)', border: 'none',
    borderRadius: '4px', padding: '10px', color: 'var(--text-normal)',
    fontSize: '14px', outline: 'none', boxSizing: 'border-box',
  };

  const labelStyle = {
    display: 'block', color: 'var(--header-secondary)', fontSize: '12px',
    fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px',
  };

  return (
    <div>
      <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>Voice & Video</h2>

      <div style={{ display: 'flex', gap: '16px', marginBottom: '24px' }}>
        {/* Input Device */}
        <div style={{ flex: 1 }}>
          <label style={labelStyle}>Input Device</label>
          <select
            value={selectedInput}
            onChange={(e) => setSelectedInput(e.target.value)}
            style={selectStyle}
          >
            <option value="default">Default</option>
            {inputDevices.map(d => (
              <option key={d.deviceId} value={d.deviceId}>
                {d.label || `Microphone ${d.deviceId.slice(0, 8)}`}
              </option>
            ))}
          </select>
        </div>

        {/* Output Device */}
        <div style={{ flex: 1 }}>
          <label style={labelStyle}>Output Device</label>
          <select
            value={selectedOutput}
            onChange={(e) => setSelectedOutput(e.target.value)}
            style={selectStyle}
          >
            <option value="default">Default</option>
            {outputDevices.map(d => (
              <option key={d.deviceId} value={d.deviceId}>
                {d.label || `Speaker ${d.deviceId.slice(0, 8)}`}
              </option>
            ))}
          </select>
        </div>
      </div>

      {/* Input Volume */}
      <div style={{ marginBottom: '20px' }}>
        <label style={labelStyle}>Input Volume</label>
        <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
          <input
            type="range"
            min="0"
            max="100"
            value={inputVolume}
            onChange={(e) => setInputVolume(parseInt(e.target.value))}
            className="voice-slider"
          />
          <span style={{ color: 'var(--text-normal)', fontSize: '14px', minWidth: '36px' }}>{inputVolume}%</span>
        </div>
      </div>

      {/* Output Volume */}
      <div style={{ marginBottom: '24px' }}>
        <label style={labelStyle}>Output Volume</label>
        <div style={{ display: 'flex', alignItems: 'center', gap: '12px' }}>
          <input
            type="range"
            min="0"
            max="100"
            value={outputVolume}
            onChange={(e) => setOutputVolume(parseInt(e.target.value))}
            className="voice-slider"
          />
          <span style={{ color: 'var(--text-normal)', fontSize: '14px', minWidth: '36px' }}>{outputVolume}%</span>
        </div>
      </div>

      {/* Mic Test */}
      <div style={{ marginBottom: '24px' }}>
        <label style={labelStyle}>Mic Test</label>
        <div style={{ display: 'flex', alignItems: 'center', gap: '16px' }}>
          <button
            onClick={micTesting ? stopMicTest : startMicTest}
            style={{
              backgroundColor: micTesting ? '#ed4245' : 'var(--brand-experiment)',
              color: 'white', border: 'none', borderRadius: '4px',
              padding: '8px 16px', cursor: 'pointer', fontSize: '14px', fontWeight: '500',
              flexShrink: 0,
            }}
          >
            {micTesting ? 'Stop Testing' : 'Let\'s Check'}
          </button>
          <div className="mic-level-bar">
            <div className="mic-level-fill" style={{ width: `${micLevel}%` }} />
          </div>
        </div>
      </div>
    </div>
  );
};

/* =========================================
   KEYBINDS TAB
   ========================================= */
const KeybindsTab = () => {
  const keybinds = [
    { action: 'Quick Switcher', keys: 'Ctrl+K' },
    { action: 'Toggle Mute', keys: 'Ctrl+Shift+M' },
  ];

  return (
    <div>
      <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>Keybinds</h2>

      <div style={{
        backgroundColor: 'var(--bg-secondary)', borderRadius: '8px', padding: '16px',
        marginBottom: '16px',
      }}>
        <p style={{ color: 'var(--text-muted)', fontSize: '14px', margin: '0 0 16px' }}>
          Keybind configuration coming soon. Current keybinds are shown below.
        </p>

        <div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
          {keybinds.map(kb => (
            <div key={kb.action} style={{
              display: 'flex', alignItems: 'center', justifyContent: 'space-between',
              padding: '10px 12px', backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px',
            }}>
              <span style={{ color: 'var(--text-normal)', fontSize: '14px' }}>{kb.action}</span>
              <kbd style={{
                backgroundColor: 'var(--bg-primary)', padding: '4px 8px', borderRadius: '4px',
                fontSize: '13px', color: 'var(--header-primary)', fontFamily: 'inherit',
                border: '1px solid var(--border-subtle)',
              }}>
                {kb.keys}
              </kbd>
            </div>
          ))}
        </div>
      </div>
    </div>
  );
};

/* =========================================
   SEARCH TAB
   ========================================= */
const TAG_HEX_LEN = 32;

const SearchTab = ({ userId }) => {
  const convex = useConvex();
  const { crypto } = usePlatform();
  const searchCtx = useSearch();

  const [status, setStatus] = useState('idle'); // idle | rebuilding | done | error
  const [progress, setProgress] = useState({ currentChannel: '', channelIndex: 0, totalChannels: 0, messagesIndexed: 0 });
  const [errorMsg, setErrorMsg] = useState('');
  const cancelledRef = useRef(false);

  const handleRebuild = async () => {
    if (!userId || !crypto || !searchCtx?.isReady) return;

    cancelledRef.current = false;
    setStatus('rebuilding');
    setProgress({ currentChannel: '', channelIndex: 0, totalChannels: 0, messagesIndexed: 0 });
    setErrorMsg('');

    try {
      // 1. Gather channels + DMs
      const [channels, dmChannels, rawKeys] = await Promise.all([
        convex.query(api.channels.list, {}),
        convex.query(api.dms.listDMs, { userId }),
        convex.query(api.channelKeys.getKeysForUser, { userId }),
      ]);

      // 2. Decrypt channel keys
      const privateKey = sessionStorage.getItem('privateKey');
      if (!privateKey) throw new Error('Private key not found in session. Please re-login.');

      const decryptedKeys = {};
      for (const item of rawKeys) {
        try {
          const bundleJson = await crypto.privateDecrypt(privateKey, item.encrypted_key_bundle);
          Object.assign(decryptedKeys, JSON.parse(bundleJson));
        } catch (e) {
          // Skip channels we can't decrypt
        }
      }

      // 3. Build channel list: text channels + DMs that have keys
      const textChannels = channels
        .filter(c => c.type === 'text' && decryptedKeys[c._id])
        .map(c => ({ id: c._id, name: '#' + c.name, key: decryptedKeys[c._id] }));

      const dmItems = (dmChannels || [])
        .filter(dm => decryptedKeys[dm.channel_id])
        .map(dm => ({ id: dm.channel_id, name: '@' + dm.other_username, key: decryptedKeys[dm.channel_id] }));

      const allChannels = [...textChannels, ...dmItems];

      if (allChannels.length === 0) {
        setStatus('done');
        setProgress(p => ({ ...p, totalChannels: 0 }));
        return;
      }

      setProgress(p => ({ ...p, totalChannels: allChannels.length }));

      let totalIndexed = 0;

      // 4. For each channel, paginate and decrypt
      for (let i = 0; i < allChannels.length; i++) {
        if (cancelledRef.current) break;

        const ch = allChannels[i];
        setProgress(p => ({ ...p, currentChannel: ch.name, channelIndex: i + 1 }));

        let cursor = null;
        let isDone = false;

        while (!isDone) {
          if (cancelledRef.current) break;

          const paginationOpts = { numItems: 100, cursor };
          const result = await convex.query(api.messages.fetchBulkPage, {
            channelId: ch.id,
            paginationOpts,
          });

          if (result.page.length > 0) {
            // Build decrypt batch
            const decryptItems = [];
            const msgMap = [];

            for (const msg of result.page) {
              if (msg.ciphertext && msg.ciphertext.length >= TAG_HEX_LEN) {
                const tag = msg.ciphertext.slice(-TAG_HEX_LEN);
                const content = msg.ciphertext.slice(0, -TAG_HEX_LEN);
                decryptItems.push({ ciphertext: content, key: ch.key, iv: msg.nonce, tag });
                msgMap.push(msg);
              }
            }

            if (decryptItems.length > 0) {
              const decryptResults = await crypto.decryptBatch(decryptItems);

              const indexItems = [];
              for (let j = 0; j < decryptResults.length; j++) {
                const plaintext = decryptResults[j];
                if (plaintext && plaintext !== '[Decryption Error]') {
                  indexItems.push({
                    id: msgMap[j].id,
                    channel_id: msgMap[j].channel_id,
                    sender_id: msgMap[j].sender_id,
                    username: msgMap[j].username,
                    content: plaintext,
                    created_at: msgMap[j].created_at,
                    pinned: msgMap[j].pinned,
                    replyToId: msgMap[j].replyToId,
                  });
                }
              }

              if (indexItems.length > 0) {
                searchCtx.indexMessages(indexItems);
                totalIndexed += indexItems.length;
                setProgress(p => ({ ...p, messagesIndexed: totalIndexed }));
              }
            }
          }

          isDone = result.isDone;
          cursor = result.continueCursor;

          // Yield to UI between pages
          await new Promise(r => setTimeout(r, 10));
        }
      }

      // 5. Save
      await searchCtx.save();
      setStatus(cancelledRef.current ? 'idle' : 'done');
      setProgress(p => ({ ...p, messagesIndexed: totalIndexed }));
    } catch (err) {
      console.error('Search index rebuild failed:', err);
      setErrorMsg(err.message || 'Unknown error');
      setStatus('error');
    }
  };

  const handleCancel = () => {
    cancelledRef.current = true;
  };

  const formatNumber = (n) => n.toLocaleString();

  return (
    <div>
      <h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>Search</h2>

      <div style={{ backgroundColor: 'var(--bg-secondary)', borderRadius: '8px', padding: '20px' }}>
        <h3 style={{ color: 'var(--header-primary)', margin: '0 0 8px', fontSize: '16px', fontWeight: '600' }}>
          Search Index
        </h3>
        <p style={{ color: 'var(--text-muted)', fontSize: '14px', margin: '0 0 16px', lineHeight: '1.4' }}>
          Rebuild your local search index by downloading and decrypting all messages from the server. This may take a while for large servers.
        </p>

        {status === 'idle' && (
          <button
            onClick={handleRebuild}
            disabled={!searchCtx?.isReady}
            style={{
              backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none',
              borderRadius: '4px', padding: '10px 20px', cursor: searchCtx?.isReady ? 'pointer' : 'not-allowed',
              fontSize: '14px', fontWeight: '500', opacity: searchCtx?.isReady ? 1 : 0.5,
            }}
          >
            Rebuild Search Index
          </button>
        )}

        {status === 'rebuilding' && (
          <div>
            {/* Progress bar */}
            <div style={{
              backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', height: '8px',
              overflow: 'hidden', marginBottom: '12px',
            }}>
              <div style={{
                height: '100%', borderRadius: '4px',
                backgroundColor: 'var(--brand-experiment)',
                width: progress.totalChannels > 0
                  ? `${(progress.channelIndex / progress.totalChannels) * 100}%`
                  : '0%',
                transition: 'width 0.3s ease',
              }} />
            </div>

            <div style={{ color: 'var(--text-normal)', fontSize: '14px', marginBottom: '4px' }}>
              Indexing {progress.currentChannel}... ({progress.channelIndex} of {progress.totalChannels} channels)
            </div>
            <div style={{ color: 'var(--text-muted)', fontSize: '13px', marginBottom: '12px' }}>
              {formatNumber(progress.messagesIndexed)} messages indexed
            </div>

            <button
              onClick={handleCancel}
              style={{
                backgroundColor: 'transparent', color: '#ed4245', border: '1px solid #ed4245',
                borderRadius: '4px', padding: '8px 16px', cursor: 'pointer',
                fontSize: '14px', fontWeight: '500',
              }}
            >
              Cancel
            </button>
          </div>
        )}

        {status === 'done' && (
          <div>
            <div style={{ color: '#3ba55c', fontSize: '14px', marginBottom: '12px', fontWeight: '500' }}>
              Complete! {formatNumber(progress.messagesIndexed)} messages indexed across {progress.totalChannels} channels.
            </div>
            <button
              onClick={() => setStatus('idle')}
              style={{
                backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none',
                borderRadius: '4px', padding: '10px 20px', cursor: 'pointer',
                fontSize: '14px', fontWeight: '500',
              }}
            >
              Rebuild Again
            </button>
          </div>
        )}

        {status === 'error' && (
          <div>
            <div style={{ color: '#ed4245', fontSize: '14px', marginBottom: '12px' }}>
              Error: {errorMsg}
            </div>
            <button
              onClick={() => setStatus('idle')}
              style={{
                backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none',
                borderRadius: '4px', padding: '10px 20px', cursor: 'pointer',
                fontSize: '14px', fontWeight: '500',
              }}
            >
              Retry
            </button>
          </div>
        )}
      </div>
    </div>
  );
};

export default UserSettings;
