import {
  Button,
  buttonVariants,
  cn,
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  Input,
  Label,
} from '@learnitive/base-ui';
import type { WorkspaceStorageType } from '@learnitive/types';
import { Check, FolderOpen } from 'lucide-react';
import React, { useEffect, useId, useMemo, useRef, useState } from 'react';

const DEFAULT_STORAGE = 'browser';

export interface StorageTypeConfig {
  type: WorkspaceStorageType;
  requiresDirectory?: boolean;
  disabled?: boolean;
  defaultSelected?: boolean;
  title: string;
  description: string;
}

export interface ErrorInfo {
  title?: string;
  message: string;
}

export type DirectoryPickResult =
  | { type: 'success'; dirHandle: FileSystemDirectoryHandle }
  | { type: 'error'; errorInfo: ErrorInfo; error: Error };

export interface WorkspaceValidation {
  isValid: boolean;
  message?: string;
}

export interface WorkspaceConfig {
  name: string;
  type: WorkspaceStorageType;
  dirHandle?: FileSystemDirectoryHandle;
}

export interface CreateWorkspaceDialogProps {
  open: boolean;
  onOpenChange: (open: boolean) => void;
  onDone: (config: WorkspaceConfig) => void | Promise<void>;
  storageTypes: StorageTypeConfig[];
  onDirectoryPick?: () => Promise<DirectoryPickResult>;
  validateWorkspace: (config: WorkspaceConfig) => WorkspaceValidation;
}

/**
 * A single-screen functional project creator. Storage selection, browser project
 * name, and native-folder selection stay in one dialog; this is deliberately
 * not a first-run/onboarding wizard.
 */
export function CreateWorkspaceDialog({
  open,
  onOpenChange,
  onDone,
  storageTypes,
  onDirectoryPick,
  validateWorkspace,
}: CreateWorkspaceDialogProps) {
  const defaultStorage =
    storageTypes.find((item) => item.defaultSelected && !item.disabled)?.type ||
    storageTypes.find((item) => !item.disabled)?.type ||
    storageTypes[0]?.type ||
    DEFAULT_STORAGE;
  const [selected, setSelected] = useState<WorkspaceStorageType>(defaultStorage);
  const [name, setName] = useState('');
  const [dirHandle, setDirHandle] = useState<FileSystemDirectoryHandle>();
  const [error, setError] = useState<ErrorInfo>();
  const [isSubmitting, setIsSubmitting] = useState(false);
  const submittingRef = useRef(false);
  const nameRef = useRef<HTMLInputElement>(null);
  const workspaceNameId = useId();

  const selectedConfig = useMemo(
    () => storageTypes.find((item) => item.type === selected),
    [selected, storageTypes],
  );
  const requiresDirectory = Boolean(selectedConfig?.requiresDirectory);

  useEffect(() => {
    if (!open) return;
    setSelected(defaultStorage);
    setName('');
    setDirHandle(undefined);
    setError(undefined);
    setIsSubmitting(false);
    submittingRef.current = false;
  }, [defaultStorage, open]);

  useEffect(() => {
    if (open && !requiresDirectory) nameRef.current?.focus();
  }, [open, requiresDirectory]);

  const changeStorage = (storage: WorkspaceStorageType) => {
    const candidate = storageTypes.find((item) => item.type === storage);
    if (!candidate || candidate.disabled || isSubmitting) return;
    setSelected(storage);
    setDirHandle(undefined);
    setError(undefined);
  };

  const pickNativeDirectory = async () => {
    if (!onDirectoryPick || isSubmitting) {
      setError({ message: t.app.dialogs.createWorkspace.directoryPickingUnsupported });
      return;
    }
    const result = await onDirectoryPick();
    if (result.type === 'error') {
      setError(result.errorInfo);
      return;
    }
    setDirHandle(result.dirHandle);
    setError(undefined);
  };

  const submit = async () => {
    if (submittingRef.current || !selectedConfig || selectedConfig.disabled) return;
    const projectName = requiresDirectory ? dirHandle?.name || '' : name.trim();
    const config: WorkspaceConfig = {
      type: selected,
      name: projectName,
      ...(requiresDirectory && dirHandle ? { dirHandle } : {}),
    };
    const validation = validateWorkspace(config);
    if (!validation.isValid) {
      setError({
        message:
          validation.message ||
          (requiresDirectory
            ? t.app.dialogs.createWorkspace.invalidDirectoryDefault
            : t.app.dialogs.createWorkspace.invalidNameDefault),
      });
      return;
    }
    if (requiresDirectory && !dirHandle) {
      setError({ message: t.app.dialogs.createWorkspace.invalidDirectoryDefault });
      return;
    }

    submittingRef.current = true;
    setIsSubmitting(true);
    setError(undefined);
    try {
      await onDone(config);
    } catch (submissionError) {
      submittingRef.current = false;
      setIsSubmitting(false);
      setError(toSubmissionErrorInfo(submissionError));
    }
  };

  const handleOpenChange = (nextOpen: boolean) => {
    if (!isSubmitting) onOpenChange(nextOpen);
  };

  if (storageTypes.length === 0) {
    return (
      <Dialog open={open} onOpenChange={handleOpenChange}>
        <DialogContent>
          <DialogHeader>
            <DialogTitle>{t.app.dialogs.createWorkspace.errorTitle}</DialogTitle>
            <DialogDescription>{t.app.dialogs.createWorkspace.noStorageTypes}</DialogDescription>
          </DialogHeader>
          <ErrorMessage error={{ message: t.app.dialogs.createWorkspace.noStorageTypes }} />
          <DialogFooter>
            <Button onClick={() => onOpenChange(false)}>{t.app.common.closeButton}</Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    );
  }

  return (
    <Dialog open={open} onOpenChange={handleOpenChange}>
      <DialogContent className="sm:max-w-xl">
        <DialogHeader>
          <DialogTitle>{t.app.dialogs.createWorkspace.selectTypeTitle}</DialogTitle>
          <DialogDescription>
            Choose where this project should live. Project contents stay on this device.
          </DialogDescription>
        </DialogHeader>

        <div
          className="space-y-3"
          role="radiogroup"
          aria-label={t.app.dialogs.createWorkspace.selectTypeTitle}
        >
          {storageTypes.map((config) => (
            <StorageChoice
              key={config.type}
              title={config.title}
              description={config.description}
              isSelected={selected === config.type}
              onClick={() => changeStorage(config.type)}
              disabled={Boolean(config.disabled)}
            />
          ))}
        </div>

        <div className="grid gap-3 py-2">
          {requiresDirectory ? (
            <div className="grid gap-2">
              <Label>{t.app.dialogs.createWorkspace.selectDirectoryTitle}</Label>
              <div className="flex flex-wrap items-center gap-2">
                <Button
                  type="button"
                  variant={dirHandle ? 'outline' : 'default'}
                  onClick={() => void pickNativeDirectory()}
                  disabled={!onDirectoryPick || isSubmitting}
                >
                  <FolderOpen />
                  <span>{dirHandle?.name || t.app.dialogs.createWorkspace.pickDirectoryButton}</span>
                </Button>
                {dirHandle ? (
                  <Button
                    type="button"
                    variant="ghost"
                    onClick={() => setDirHandle(undefined)}
                    disabled={isSubmitting}
                  >
                    {t.app.common.clearButton}
                  </Button>
                ) : null}
              </div>
            </div>
          ) : (
            <div className="grid grid-cols-4 items-center gap-4">
              <Label htmlFor={workspaceNameId} className="text-right">
                {t.app.dialogs.createWorkspace.nameLabel}
              </Label>
              <Input
                id={workspaceNameId}
                ref={nameRef}
                value={name}
                onChange={(event) => { setName(event.currentTarget.value); setError(undefined); }}
                onKeyDown={(event) => {
                  if (event.key === 'Enter') {
                    event.preventDefault();
                    void submit();
                  }
                }}
                className="col-span-3"
                disabled={isSubmitting}
              />
            </div>
          )}
          <ErrorMessage error={error} />
        </div>

        <DialogFooter className="gap-2 sm:items-center sm:justify-between sm:space-x-0">
          <a
            className={cn(
              buttonVariants({ variant: 'ghost' }),
              'justify-start px-0 text-foreground text-sm hover:text-foreground/80 hover:underline',
            )}
            href="/privacy"
            target="_blank"
            rel="noreferrer"
          >
            {t.app.dialogs.createWorkspace.dataPrivacyLink}
          </a>
          <div className="flex flex-col-reverse gap-2 sm:flex-row sm:justify-end">
            <Button variant="outline" onClick={() => onOpenChange(false)} disabled={isSubmitting}>
              {t.app.common.cancelButton}
            </Button>
            <Button
              onClick={() => void submit()}
              disabled={isSubmitting || (requiresDirectory ? !dirHandle : !name.trim())}
            >
              {isSubmitting ? 'Creating…' : t.app.common.createButton}
            </Button>
          </div>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

function StorageChoice({
  title,
  description,
  isSelected,
  onClick,
  disabled,
}: {
  title: string;
  description: string;
  isSelected: boolean;
  onClick: () => void;
  disabled: boolean;
}) {
  return (
    <label
      className={cn(
        'relative block w-full cursor-pointer select-none space-y-1 rounded-md p-3 text-left leading-none transition-colors duration-200 ease-in-out focus-within:bg-muted focus-within:ring-2 focus-within:ring-ring focus-within:ring-offset-2 hover:bg-muted',
        isSelected && 'bg-muted',
        disabled && 'cursor-not-allowed opacity-50',
      )}
    >
      <input
        type="radio"
        name="workspace-storage-type"
        checked={isSelected}
        onChange={() => { if (!disabled) onClick(); }}
        disabled={disabled}
        className="absolute inset-0 h-full w-full cursor-pointer opacity-0 disabled:cursor-not-allowed"
      />
      <div className="flex items-center justify-between space-x-1">
        <div>
          <span className="mb-1 block font-semibold text-sm leading-none tracking-tight">{title}</span>
          <span className="block text-foreground/80 text-sm">{description}</span>
        </div>
        <Check className={cn('h-4 w-4 transition-opacity duration-200', isSelected ? 'opacity-100' : 'opacity-0')} />
      </div>
    </label>
  );
}

function ErrorMessage({ error }: { error?: ErrorInfo }) {
  if (!error) return null;
  return (
    <div className="rounded-sm bg-destructive p-2 text-destructive-foreground text-sm" role="alert">
      {error.title ? <strong>{error.title}: </strong> : null}
      {error.message}
    </div>
  );
}

function toSubmissionErrorInfo(error: unknown): ErrorInfo {
  return {
    message:
      error instanceof Error && error.message
        ? error.message
        : t.app.dialogs.createWorkspace.createFailed,
  };
}
