import { pickDirectory, supportsNativeFs } from '@learnitive/native-fs';
import { Button, Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, toast } from '@learnitive/ui-components';
import { AlertTriangle, Check, FolderOpen, HardDrive, LoaderCircle, RefreshCw, Unplug } from 'lucide-react';
import React from 'react';
import { useCoreServices } from '@learnitive/context';
import { requestPageConfirm } from '../components/page-modal-service';
import {
  PROJECTS_FOLDER_STATE_EVENT,
  ProjectMirrorService,
  ProjectsFolderNeedsConfirmationError,
  type ProjectsFolderSnapshot,
} from './project-mirror-service';

function relativeSyncTime(value?: number): string {
  if (!value) return 'Not synced yet';
  const seconds = Math.max(0, Math.round((Date.now() - value) / 1000));
  if (seconds < 15) return 'Synced just now';
  if (seconds < 60) return `Synced ${seconds}s ago`;
  const minutes = Math.round(seconds / 60);
  if (minutes < 60) return `Synced ${minutes}m ago`;
  const hours = Math.round(minutes / 60);
  return `Synced ${hours}h ago`;
}

function announceFolderState() {
  window.dispatchEvent(new CustomEvent(PROJECTS_FOLDER_STATE_EVENT));
}

export function ProjectStorageDialog({
  open,
  onOpenChange,
}: {
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  const core = useCoreServices();
  const [service, setService] = React.useState<ProjectMirrorService>();
  const [snapshot, setSnapshot] = React.useState<ProjectsFolderSnapshot>();
  const [busy, setBusy] = React.useState<'connect' | 'sync' | 'permission' | 'disconnect' | 'conflict' | ''>('');
  const [error, setError] = React.useState('');

  const refresh = React.useCallback(async (target = service) => {
    if (!target) return;
    const next = await target.getSnapshot();
    setSnapshot(next);
  }, [service]);

  React.useEffect(() => {
    let cancelled = false;
    const next = new ProjectMirrorService({
      workspaceOps: core.workspaceOps,
      projectStorage: core.projectStorage,
      workspaceState: core.workspaceState,
    });
    setService(next);
    if (open) void next.getSnapshot().then((value) => { if (!cancelled) setSnapshot(value); });
    return () => { cancelled = true; };
  }, [core.projectStorage, core.workspaceOps, core.workspaceState, open]);

  React.useEffect(() => {
    if (open) void refresh();
  }, [open, refresh]);

  const connect = async () => {
    if (!service || !supportsNativeFs()) return;
    setBusy('connect');
    setError('');
    try {
      const handle = await pickDirectory({ id: 'learnitive-projects-folder' });
      const inspection = await service.inspectRoot(handle);
      let allowInitializeNonEmpty = false;
      if (inspection.kind === 'unmanaged-nonempty') {
        allowInitializeNonEmpty = await requestPageConfirm({
          title: 'Use this folder for Learnitive projects?',
          description: 'This folder already contains unrelated files. Learnitive will add its own .learnitive metadata and project subfolders, but it will not remove or overwrite unrelated files.',
          confirmText: 'Use this folder',
        });
        if (!allowInitializeNonEmpty) return;
      }
      const next = await service.connectRoot(handle, { allowInitializeNonEmpty });
      setSnapshot(next);
      announceFolderState();
      toast.success(`Projects Folder connected${next.projectCount ? ` · ${next.projectCount} project${next.projectCount === 1 ? '' : 's'} synchronized` : ''}.`);
    } catch (reason) {
      if (reason instanceof DOMException && reason.name === 'AbortError') return;
      if (reason instanceof ProjectsFolderNeedsConfirmationError) return;
      const message = reason instanceof Error ? reason.message : String(reason);
      setError(message);
      toast.error(message);
    } finally { setBusy(''); }
  };

  const syncNow = async () => {
    if (!service) return;
    setBusy('sync'); setError('');
    try {
      const next = await service.syncAll();
      setSnapshot(next);
      announceFolderState();
      if (next.conflicts.length) toast.error(`${next.conflicts.length} project file conflict${next.conflicts.length === 1 ? '' : 's'} need review.`);
      else toast.success('Projects Folder synchronized.');
    } catch (reason) {
      const message = reason instanceof Error ? reason.message : String(reason);
      setError(message); toast.error(message);
    } finally { setBusy(''); }
  };

  const grantPermission = async () => {
    if (!service) return;
    setBusy('permission'); setError('');
    try {
      const next = await service.requestRootPermission();
      setSnapshot(next); announceFolderState(); toast.success('Projects Folder access restored.');
    } catch (reason) {
      const message = reason instanceof Error ? reason.message : String(reason);
      setError(message); toast.error(message);
    } finally { setBusy(''); }
  };

  const disconnect = async () => {
    if (!service || !snapshot?.connected) return;
    const confirmed = await requestPageConfirm({
      title: 'Disconnect Projects Folder?',
      description: `Stop synchronizing with “${snapshot.handleName ?? 'Projects Folder'}”? Browser projects and files already written to that folder will remain unchanged.`,
      confirmText: 'Disconnect',
      tone: 'destructive',
    });
    if (!confirmed) return;
    setBusy('disconnect'); setError('');
    try {
      await service.disconnectRoot();
      setSnapshot(await service.getSnapshot());
      announceFolderState();
      toast.success('Projects Folder disconnected. Files on disk were left unchanged.');
    } catch (reason) {
      const message = reason instanceof Error ? reason.message : String(reason);
      setError(message); toast.error(message);
    } finally { setBusy(''); }
  };

  const resolveConflict = async (id: string, resolution: 'browser' | 'local' | 'both') => {
    if (!service) return;
    setBusy('conflict'); setError('');
    try {
      await service.resolveConflict(id, resolution);
      setSnapshot(await service.getSnapshot());
      announceFolderState();
    } catch (reason) {
      const message = reason instanceof Error ? reason.message : String(reason);
      setError(message); toast.error(message);
    } finally { setBusy(''); }
  };

  const connected = Boolean(snapshot?.connected);
  const unsupported = !supportsNativeFs();
  return (
    <Dialog open={open} onOpenChange={(value) => !busy && onOpenChange(value)}>
      <DialogContent className="sm:max-w-2xl" aria-describedby="interactive-project-storage-description">
        <DialogHeader>
          <DialogTitle>Project Storage</DialogTitle>
          <DialogDescription id="interactive-project-storage-description">
            Projects are always saved in this browser. Connect one Projects Folder to synchronize all projects with normal files on this device and recover them after browser data is cleared.
          </DialogDescription>
        </DialogHeader>

        <div className="grid gap-3 md:grid-cols-2">
          <section className="interactive-storage-card is-active">
            <div className="interactive-storage-card-icon"><HardDrive className="size-5" /></div>
            <div><div className="interactive-storage-card-title"><strong>Browser</strong><span className="interactive-badge"><Check className="size-3" /> Enabled for Browser</span></div>
            <p>Fast browser-local working storage for every project.</p>
            <small>Clearing this site's browser data can remove the Browser copy. Connect a Projects Folder for durable filesystem recovery.</small></div>
          </section>

          <section className={`interactive-storage-card ${connected ? 'is-active' : ''}`}>
            <div className="interactive-storage-card-icon"><FolderOpen className="size-5" /></div>
            <div className="min-w-0"><div className="interactive-storage-card-title"><strong>Local Folder</strong>{connected ? <span className="interactive-badge interactive-badge-success"><Check className="size-3" /> Connected</span> : null}</div>
            {unsupported ? <p>Not supported by this browser.</p> : connected ? <><p className="truncate"><strong>{snapshot?.handleName}</strong></p><small>{snapshot?.status === 'permission-required' ? 'Access required' : relativeSyncTime(snapshot?.lastSuccessfulSyncAt)} · {snapshot?.projectCount ?? 0} project{snapshot?.projectCount === 1 ? '' : 's'}</small></> : <><p>Keep every Browser project synchronized to one folder on this device.</p></>}
            <small>Local Folder synchronization works in Chrome and Edge desktop browsers.</small>
            </div>
          </section>
        </div>

        {error || snapshot?.error ? <div className="interactive-storage-alert is-error" role="alert"><AlertTriangle className="size-4" /><span>{error || snapshot?.error}</span></div> : null}
        {snapshot?.status === 'permission-required' ? <div className="interactive-storage-alert"><AlertTriangle className="size-4" /><span>Projects remain safe in Browser storage. Grant folder access to resume synchronization.</span></div> : null}
        {snapshot?.pendingCount ? <div className="interactive-storage-alert"><RefreshCw className="size-4" /><span>{snapshot.pendingCount} project sync operation{snapshot.pendingCount === 1 ? '' : 's'} pending.</span></div> : null}

        {snapshot?.conflicts.length ? <section className="interactive-storage-conflicts"><h3>Conflicts to review</h3>{snapshot.conflicts.map((conflict) => <article key={conflict.id}><div><strong>{conflict.projectSlug}</strong><code>{conflict.path}</code></div><div className="flex flex-wrap gap-2"><Button size="sm" variant="outline" disabled={Boolean(busy)} onClick={() => void resolveConflict(conflict.id, 'browser')}>Keep Browser</Button><Button size="sm" variant="outline" disabled={Boolean(busy)} onClick={() => void resolveConflict(conflict.id, 'local')}>Keep Local</Button><Button size="sm" variant="outline" disabled={Boolean(busy)} onClick={() => void resolveConflict(conflict.id, 'both')}>Keep both</Button></div></article>)}</section> : null}

        {snapshot?.issues.length ? <section className="interactive-storage-issues"><h3>Storage issues</h3><ul>{snapshot.issues.map((issue) => <li key={issue.id}>{issue.message}</li>)}</ul></section> : null}

        <div className="flex flex-wrap justify-end gap-2">
          {!connected ? <Button disabled={unsupported || Boolean(busy) || !service} onClick={() => void connect()}>{busy === 'connect' ? <LoaderCircle className="size-4 animate-spin" /> : <FolderOpen className="size-4" />} Connect Local Folder</Button> : <>
            {snapshot?.status === 'permission-required' ? <Button disabled={Boolean(busy)} onClick={() => void grantPermission()}>{busy === 'permission' ? <LoaderCircle className="size-4 animate-spin" /> : <FolderOpen className="size-4" />} Grant access</Button> : <Button variant="outline" disabled={Boolean(busy)} onClick={() => void syncNow()}>{busy === 'sync' ? <LoaderCircle className="size-4 animate-spin" /> : <RefreshCw className="size-4" />} Sync now</Button>}
            <Button variant="outline" disabled={Boolean(busy)} onClick={() => void connect()}><FolderOpen className="size-4" /> Change folder</Button>
            <Button variant="destructive" disabled={Boolean(busy)} onClick={() => void disconnect()}><Unplug className="size-4" /> Disconnect</Button>
          </>}
        </div>
      </DialogContent>
    </Dialog>
  );
}
