import React, { ReactElement, useEffect, useState } from 'react';
import { toNumber } from 'lodash';

import { useLiveQuery } from 'dexie-react-hooks';
import {
  getBackupEveryNSeconds,
  setBackupEveryNSeconds,
  getBackupDirectoryHandle,
  getBackupPath,
  setBackupPath,
  getLatestBackup,
  getLatestBackupSize,
  getIsBackupOverdue,
  getBackupStatus,
  BackupStatus,
  getIgnoredUrls,
  setIgnoredUrls,
  getNormalizeRules,
  setNormalizeRules,
  getSimilarityScaler,
  setSimilarityScaler,
  getTagSimilarityScaler,
  setTagSimilarityScaler,
} from '../db/prefs';
import { loadPrefsFromDB } from '../db/prefsLoader';
import { db } from '../db/db';
import { maybeBackup, setBackupDir, restore, requestBackupPermission } from '../db/backup';
import { enqueueOperation } from '../db/queue';
import { useBackgroundProgress } from '../db/progress';
import {
  Button,
  CssBaseline,
  CssVarsProvider,
  FormControl,
  FormLabel,
  Input,
  Sheet,
  Typography,
  Stack,
  Divider,
  Alert,
  IconButton,
  Box,
  Slider,
  LinearProgress,
  Modal,
  ModalDialog,
  DialogTitle,
  DialogContent,
  DialogActions,
} from '@mui/joy';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import {
  faDownload,
  faFolderOpen,
  faTriangleExclamation,
  faSave,
  faTrash,
  faPlus,
  faUpload,
  faBug,
} from '@fortawesome/free-solid-svg-icons';
import '@fontsource/inter';

const Options = (): ReactElement => {
  const backupEveryNSeconds = useLiveQuery(getBackupEveryNSeconds);
  const isBackupOverdue = useLiveQuery(getIsBackupOverdue);
  const backupStatus = useLiveQuery(getBackupStatus);
  const latestBackup = useLiveQuery(getLatestBackup);
  const latestBackupSize = useLiveQuery(getLatestBackupSize);
  const backupDirHandle = useLiveQuery(getBackupDirectoryHandle);
  const dbBackupPath = useLiveQuery(getBackupPath);

  const ignoredUrls = useLiveQuery(getIgnoredUrls);
  const normalizeRules = useLiveQuery(getNormalizeRules);
  const similarityScaler = useLiveQuery(getSimilarityScaler);
  const tagSimilarityScaler = useLiveQuery(getTagSimilarityScaler);

  const [localIgnoredUrls, setLocalIgnoredUrls] = useState<string[]>([]);
  const [localNormalizeRules, setLocalNormalizeRules] = useState<[string, string][]>([]);
  const [saveStatus, setSaveStatus] = useState<string>('');
  const [isRestoreModalOpen, setIsRestoreModalOpen] = useState(false);
  const [isRestoring, setIsRestoring] = useState(false);

  const handleRestore = async () => {
    try {
      setIsRestoring(true);
      await restore();
      setSaveStatus('Backup restored successfully! Restarting...');
      setIsRestoreModalOpen(false);
      setTimeout(() => {
        if (typeof chrome !== 'undefined' && chrome.runtime && chrome.runtime.reload) {
          chrome.runtime.reload();
        } else {
          window.location.reload();
        }
      }, 2000);
    } catch (e: any) {
      if (e.name !== 'AbortError') {
        setSaveStatus('Error restoring backup: ' + e.message);
      }
      setIsRestoreModalOpen(false);
    } finally {
      setIsRestoring(false);
    }
  };

  // Ensure DB is initialized
  useEffect(() => {
      loadPrefsFromDB();
  }, []);

  // Sync from DB to local state when db data loads
  useEffect(() => {
    if (ignoredUrls !== undefined) {
      setLocalIgnoredUrls(ignoredUrls);
    }
  }, [ignoredUrls]);

  useEffect(() => {
    if (normalizeRules !== undefined) {
      setLocalNormalizeRules(normalizeRules);
    }
  }, [normalizeRules]);



  const handleSave = async () => {
    try {
      await setIgnoredUrls(localIgnoredUrls.filter(u => u.trim() !== ''));
      await setNormalizeRules(localNormalizeRules.filter(r => r[0].trim() !== ''));


      // Reload in this process
      await loadPrefsFromDB();

      // Notify background
      await chrome.runtime.sendMessage({ type: 'reloadPrefs' }).catch(() => {});

      setSaveStatus('Saved!');
      setTimeout(() => setSaveStatus(''), 2000);
    } catch (e: any) {
      setSaveStatus('Error: ' + e.message);
    }
  };

  const handleClearLogs = async () => {
    try {
      setSaveStatus('Clearing logs...');
      await db.logs.clear();
      setSaveStatus('Logs cleared!');
      setTimeout(() => setSaveStatus(''), 2000);
    } catch (e: any) {
      setSaveStatus('Error clearing logs: ' + e.message);
    }
  };

  const handleDownloadLogs = async () => {
    try {
      setSaveStatus('Fetching logs...');
      const logs = await db.logs.orderBy('timestamp').reverse().limit(1000).toArray();
      const text = logs.map(
        l => `[${new Date(l.timestamp).toISOString()}] ${l.level.toUpperCase()} ${l.context} [Mem: ${l.memoryUsage ? Math.round(l.memoryUsage / 1024 / 1024) + 'MB' : 'N/A'}] - ${l.message}`
      ).join('\n');
      
      const blob = new Blob([text], { type: 'text/plain' });
      const url = URL.createObjectURL(blob);
      const a = document.createElement('a');
      a.href = url;
      a.download = `tabvana-debug-logs-${new Date().toISOString()}.txt`;
      document.body.appendChild(a);
      a.click();
      document.body.removeChild(a);
      URL.revokeObjectURL(url);
      setSaveStatus('Logs downloaded!');
      setTimeout(() => setSaveStatus(''), 2000);
    } catch (e: any) {
      setSaveStatus('Error getting logs: ' + e.message);
    }
  };

  const addIgnoredUrl = () => {
    setLocalIgnoredUrls([...localIgnoredUrls, '']);
  };

  const removeIgnoredUrl = (index: number) => {
    const newUrls = [...localIgnoredUrls];
    newUrls.splice(index, 1);
    setLocalIgnoredUrls(newUrls);
  };

  const updateIgnoredUrl = (index: number, value: string) => {
    const newUrls = [...localIgnoredUrls];
    newUrls[index] = value;
    setLocalIgnoredUrls(newUrls);
  };

  const addNormalizeRule = () => {
    setLocalNormalizeRules([...localNormalizeRules, ['', '$1']]);
  };

  const removeNormalizeRule = (index: number) => {
    const newRules = [...localNormalizeRules];
    newRules.splice(index, 1);
    setLocalNormalizeRules(newRules);
  };

  const updateNormalizeRule = (index: number, field: 0 | 1, value: string) => {
    const newRules = [...localNormalizeRules];
    newRules[index][field] = value;
    setLocalNormalizeRules(newRules);
  };

  return (
    <CssVarsProvider
      defaultMode="dark"
      colorSchemeSelector="#demo_dark-mode-by-default"
      modeStorageKey="demo_dark-mode-by-default"
      disableNestedContext
    >
      <CssBaseline />
      <Sheet
        sx={{
          width: '100vw',
          height: '100vh',
          display: 'flex',
          flexDirection: 'column',
          alignItems: 'center',
          justifyContent: 'center',
          p: 2,
          overflow: 'auto', // Allow scrolling if content is tall
        }}
      >
        <Sheet
          variant="outlined"
          sx={{
            width: '100%',
            maxWidth: 800,
            borderRadius: 'md',
            p: 3,
            boxShadow: 'lg',
            maxHeight: '100%',
            overflow: 'auto',
          }}
        >
          <Stack spacing={2}>
            <Typography level="h2" component="h1">
              Tabvana Options
            </Typography>
            <Divider />

            <Typography level="h4">Visual Settings</Typography>

            <FormControl>
              <FormLabel>Page Similarity Scaler: {similarityScaler}</FormLabel>
              <Slider
                min={0}
                max={10}
                step={0.1}
                value={similarityScaler ?? 5}
                onChange={(e, val) => setSimilarityScaler(val as number)}
              />
            </FormControl>
            <FormControl>
              <FormLabel>Tag Similarity Scaler: {tagSimilarityScaler}</FormLabel>
              <Slider
                min={1}
                max={202}
                step={2}
                value={tagSimilarityScaler ?? 20}
                onChange={(e, val) => setTagSimilarityScaler(val as number)}
              />
            </FormControl>

            <Divider />

            <Typography level="h4">Backup Settings</Typography>

            <FormControl>
              <FormLabel>Backup Interval (seconds)</FormLabel>
              <Input
                type="number"
                value={backupEveryNSeconds || 0}
                onChange={(event) => {
                  setBackupEveryNSeconds(toNumber(event.target.value));
                }}
              />
            </FormControl>

            <Box>
              <Typography level="body-sm" sx={{ mb: 1, color: 'text.secondary' }}>
                Current Backup Directory:{' '}
                {dbBackupPath ? (
                  <Box
                    component="a"
                    href={`file://${dbBackupPath}`}
                    target="_blank"
                    sx={{
                      color: 'primary.main',
                      textDecoration: 'underline',
                      wordBreak: 'break-all',
                    }}
                  >
                    {dbBackupPath}
                  </Box>
                ) : (
                  backupDirHandle?.name || 'Not set'
                )}
              </Typography>
              {latestBackup && (
                <Typography level="body-xs" sx={{ color: 'text.tertiary' }}>
                  Last backup: {new Date(latestBackup).toLocaleString()} ({formatBytes(latestBackupSize || 0)})
                </Typography>
              )}
            </Box>

            <Stack direction="row" spacing={2}>
              <Button
                color={backupStatus === BackupStatus.DirectoryNotAccessible ? 'danger' : 'primary'}
                startDecorator={<FontAwesomeIcon icon={faFolderOpen} />}
                onClick={async () => {
                   if (backupStatus === BackupStatus.DirectoryNotAccessible) {
                     if (await requestBackupPermission()) {
                       await maybeBackup(true);
                       return;
                     }
                   }
                   await setBackupDir();
                   await maybeBackup(true);
                }}
                fullWidth
              >
                {backupStatus === BackupStatus.DirectoryNotAccessible ? "Grant Backup Directory Permission" : "Set Backup Directory"}
              </Button>
              <Button
                variant="outlined"
                startDecorator={<FontAwesomeIcon icon={faDownload} />}
                onClick={() => maybeBackup(true)}
                fullWidth
              >
                Backup Now
              </Button>
              <Button
                variant="outlined"
                color="warning"
                startDecorator={<FontAwesomeIcon icon={faUpload} />}
                onClick={() => setIsRestoreModalOpen(true)}
                fullWidth
              >
                Restore Backup
              </Button>
            </Stack>

            <Modal open={isRestoreModalOpen} onClose={() => !isRestoring && setIsRestoreModalOpen(false)}>
              <ModalDialog variant="outlined" role="alertdialog">
                <DialogTitle>
                  <FontAwesomeIcon icon={faTriangleExclamation} />
                  Confirmation
                </DialogTitle>
                <Divider />
                <DialogContent>
                  Are you sure you want to restore a backup? This will completely replace your current history and settings, and cannot be undone.
                </DialogContent>
                <DialogActions>
                  <Button variant="solid" color="danger" onClick={handleRestore} loading={isRestoring}>
                    Yes, restore
                  </Button>
                  <Button variant="plain" color="neutral" onClick={() => setIsRestoreModalOpen(false)} disabled={isRestoring}>
                    Cancel
                  </Button>
                </DialogActions>
              </ModalDialog>
            </Modal>

            <Divider />

            <Typography level="h4">Configuration</Typography>

            <Box>
              <Typography level="title-sm" sx={{ mb: 1 }}>Ignored URLs (Exact substring match)</Typography>
              <Stack spacing={1}>
                {localIgnoredUrls.map((url, index) => (
                  <Stack key={index} direction="row" spacing={1}>
                    <Input
                      fullWidth
                      value={url}
                      onChange={(e) => updateIgnoredUrl(index, e.target.value)}
                      placeholder="e.g. chrome://"
                    />
                    <IconButton
                      color="danger"
                      variant="outlined"
                      onClick={() => removeIgnoredUrl(index)}
                    >
                      <FontAwesomeIcon icon={faTrash} />
                    </IconButton>
                  </Stack>
                ))}
                <Button
                  startDecorator={<FontAwesomeIcon icon={faPlus} />}
                  onClick={addIgnoredUrl}
                  variant="outlined"
                  color="neutral"
                >
                  Add Ignored URL
                </Button>
              </Stack>
            </Box>

            <Divider />

            <Box>
              <Typography level="title-sm" sx={{ mb: 1 }}>Normalization Rules (Regex Capture {`\u2192`} Replacement)</Typography>
              <Stack spacing={1}>
                {localNormalizeRules.map((rule, index) => (
                  <Stack key={index} direction="row" spacing={1} alignItems="center">
                    <Input
                      sx={{ flex: 2 }}
                      value={rule[0]}
                      onChange={(e) => updateNormalizeRule(index, 0, e.target.value)}
                      placeholder="Regex Pattern (e.g. (.*)\?.*)"
                    />
                    <Box component="span" sx={{ color: 'text.secondary' }}>{`\u2192`}</Box>
                    <Input
                      sx={{ flex: 1 }}
                      value={rule[1]}
                      onChange={(e) => updateNormalizeRule(index, 1, e.target.value)}
                      placeholder="Replacement (e.g. $1)"
                    />
                    <IconButton
                      color="danger"
                      variant="outlined"
                      onClick={() => removeNormalizeRule(index)}
                    >
                      <FontAwesomeIcon icon={faTrash} />
                    </IconButton>
                  </Stack>
                ))}
                <Button
                  startDecorator={<FontAwesomeIcon icon={faPlus} />}
                  onClick={addNormalizeRule}
                  variant="outlined"
                  color="neutral"
                >
                  Add Normalization Rule
                </Button>
              </Stack>
            </Box>

            <Stack sx={{ my: 2 }}>
                <Button
                color="success"
                size="lg"
                startDecorator={<FontAwesomeIcon icon={faSave} />}
                onClick={handleSave}
                >
                Save Configuration
                </Button>
                {saveStatus && (
                <Alert color={saveStatus.startsWith('Error') ? 'danger' : 'success'} size="sm" sx={{ mt: 1 }}>
                    {saveStatus}
                </Alert>
                )}
            </Stack>


            
            <Divider />



            <Typography level="h4">Advanced Maintenance</Typography>

            <Alert
              startDecorator={<FontAwesomeIcon icon={faTriangleExclamation} />}
              variant="soft"
              color="warning"
              size="sm"
            >
              Renormalizing the database rebuilds search indices. This operation can be slow for large
              histories.
            </Alert>

            <Button 
                color="warning" 
                variant="solid" 
                onClick={async () => {
                   await enqueueOperation('renormalize', {}); // {batchSize: 100, limit: 1000}
                   await enqueueOperation('generateEmbedding', {});
                   await enqueueOperation('fetchMissingTitle', {limit: 10000});
                   await enqueueOperation('cleanupGraphRefs', {});
                   setSaveStatus('Renormalization and embedding generation started in background');
                   setTimeout(() => setSaveStatus(''), 3000);
                }}
            >
              Renormalize Database
            </Button>
            
            <Divider />
            <Typography level="h4" color="danger">Diagnostics</Typography>
            <Typography level="body-sm" sx={{ mb: 1, color: 'text.secondary' }}>
                If you experience crashes, download the internal error logs to debug the cause.
            </Typography>
            <Stack direction="row" spacing={2}>
              <Button
                  color="neutral"
                  variant="outlined"
                  startDecorator={<FontAwesomeIcon icon={faBug} />}
                  onClick={handleDownloadLogs}
              >
                Download Debug Logs
              </Button>
              <Button
                  color="danger"
                  variant="outlined"
                  startDecorator={<FontAwesomeIcon icon={faTrash} />}
                  onClick={handleClearLogs}
              >
                Clear Debug Logs
              </Button>
            </Stack>
          </Stack>
        </Sheet>
      </Sheet>
    </CssVarsProvider>
  );
};

const formatBytes = (bytes: number, decimals: number = 2) => {
  if (bytes === 0) return '0 Bytes';
  const k = 1024;
  const dm = decimals < 0 ? 0 : decimals;
  const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
  const i = Math.floor(Math.log(bytes) / Math.log(k));
  return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
};

export default Options;
