import { WORKSPACE_STORAGE_TYPE } from '@learnitive/constants';
import { useCoreServices } from '@learnitive/context';
import {
  fetchProjectTemplateFiles,
  loadProjectTemplateManifest,
  normalizeProjectSlug,
  requestPersistentBrowserStorage,
  supportsOpfsProjectStorage,
  writePreparedProjectTemplateFiles,
  type ProjectTemplateDefinition,
} from '@learnitive/project-storage';
import {
  Button,
  Dialog,
  DialogContent,
  DialogDescription,
  DialogFooter,
  DialogHeader,
  DialogTitle,
  Input,
  Label,
  toast,
} from '@learnitive/ui-components';
import { WsPath } from '@learnitive/ws-path';
import { LoaderCircle } from 'lucide-react';
import React from 'react';
import { finalizeCreatedBrowserProject } from '../saas/project-mirror-service';



function cloneTemplateConfig(template: ProjectTemplateDefinition) {
  return {
    ...template.config,
    assets: { ...template.config.assets },
  };
}

export function ProjectTemplateDialog({
  open,
  onOpenChange,
}: {
  open: boolean;
  onOpenChange: (open: boolean) => void;
}) {
  const core = useCoreServices();
  const {
    fileSystem,
    navigation,
    projectStorage,
    workspaceOps,
  } = core;
  const [templates, setTemplates] = React.useState<readonly ProjectTemplateDefinition[]>([]);
  const [selectedId, setSelectedId] = React.useState('blank-article');
  const [catalogLoading, setCatalogLoading] = React.useState(false);
  const [workspaceName, setWorkspaceName] = React.useState('');
  const [templateQuery, setTemplateQuery] = React.useState('');
  const [busy, setBusy] = React.useState(false);
  const [error, setError] = React.useState<string>();

  React.useEffect(() => {
    if (!open) return;
    const controller = new AbortController();
    setSelectedId('blank-article');
    setWorkspaceName('');
    setTemplateQuery('');
    setError(undefined);
    setCatalogLoading(true);
    void loadProjectTemplateManifest(controller.signal)
      .then((manifest) => {
        setTemplates(manifest.templates);
        const defaultTemplate =
          manifest.templates.find((template) => template.id === 'blank-article') ??
          manifest.templates[0];
        if (defaultTemplate) setSelectedId(defaultTemplate.id);
      })
      .catch((reason) => {
        if (controller.signal.aborted) return;
        setTemplates([]);
        setError(reason instanceof Error ? reason.message : String(reason));
      })
      .finally(() => {
        if (!controller.signal.aborted) setCatalogLoading(false);
      });
    return () => controller.abort();
  }, [open]);

  const selected =
    templates.find((template) => template.id === selectedId) ?? templates[0];

  const templateNeedle = templateQuery.trim().toLowerCase();
  const matchingTemplates = templates.filter((template) => !templateNeedle || `${template.name} ${template.description} ${template.category} ${template.tags.join(' ')}`.toLowerCase().includes(templateNeedle));
  const firstTemplates = matchingTemplates.slice(0, 12);
  const visibleTemplates = !templateNeedle && selected && !firstTemplates.some((template) => template.id === selected.id)
    ? [selected, ...firstTemplates.slice(0, 11)]
    : firstTemplates;

  const create = async () => {
    if (!selected) return;

    const displayName = workspaceName.trim() || selected.name;
    const name = normalizeProjectSlug(displayName, { fallback: selected.slug || 'project' });
    const validName = WsPath.safeFromParts(name, '');
    if (!validName.ok) {
      setError(validName.validationError?.reason ?? t.app.templates.invalidWorkspaceName);
      return;
    }
    setBusy(true);
    setError(undefined);
    let created = false;
    try {
      const preparedFiles = await fetchProjectTemplateFiles(selected);
      const type = supportsOpfsProjectStorage()
        ? WORKSPACE_STORAGE_TYPE.BrowserOPFS
        : WORKSPACE_STORAGE_TYPE.Browser;
      await workspaceOps.createWorkspaceInfo({
        name,
        type,
        metadata: {
          project: cloneTemplateConfig(selected),
          product: 'learnitive',
          projectDisplayName: displayName,
        },
      });
      created = true;
      await requestPersistentBrowserStorage();

      const provider = await projectStorage.getProvider(name);
      await writePreparedProjectTemplateFiles(provider, selected, preparedFiles);
      await finalizeCreatedBrowserProject(name, core);
      fileSystem.refreshFileTree();
      toast.success(t.app.templates.created({ template: selected.name }));
      onOpenChange(false);
      navigation.goWsFile(WsPath.fromParts(name, selected.config.rootFile).wsPath);
    } catch (reason) {
      if (created) {
        await workspaceOps.deleteWorkspaceInfo(name).catch(() => undefined);
      }
      const message = reason instanceof Error ? reason.message : String(reason);
      setError(message);
      toast.error(t.app.templates.createFailed);
    } finally {
      setBusy(false);
    }
  };

  return (
    <>
      <Dialog open={open} onOpenChange={(value) => !busy && onOpenChange(value)}>
        <DialogContent className="max-h-[85vh] overflow-y-auto sm:max-w-3xl" data-testid="interactive-template-dialog">
          <DialogHeader>
            <DialogTitle>{t.app.templates.title}</DialogTitle>
            <DialogDescription>{t.app.templates.description}</DialogDescription>
          </DialogHeader>

          {error && (
            <div role="alert" className="rounded-md border border-destructive/40 bg-destructive/5 px-3 py-2 text-sm text-destructive">
              {error}
            </div>
          )}

          {catalogLoading && (
            <div className="flex items-center gap-2 rounded-lg border p-3 text-sm text-muted-foreground">
              <LoaderCircle className="size-4 animate-spin" /> Loading templates…
            </div>
          )}

          <div className="space-y-2">
            <Label htmlFor="interactive-template-search">Find a template</Label>
            <Input id="interactive-template-search" value={templateQuery} onChange={(event) => setTemplateQuery(event.currentTarget.value)} placeholder="Search by name or category…" />
            <div className="flex items-center justify-between gap-3 text-muted-foreground text-xs"><span>{matchingTemplates.length ? `Showing ${Math.min(visibleTemplates.length, matchingTemplates.length)} of ${matchingTemplates.length} matching templates` : 'No matching templates'}</span><a className="underline" href="/templates">Browse full template library</a></div>
          </div>

          <div className="grid gap-3 sm:grid-cols-2">
            {visibleTemplates.map((template) => {
              return (
                <button
                  key={template.id}
                  type="button"
                  aria-pressed={selectedId === template.id}
                  className={`rounded-lg border p-3 text-left transition-colors ${
                    selectedId === template.id
                      ? 'border-primary bg-primary/5'
                      : 'hover:bg-muted/40'
                  }`}
                  onClick={() => {
                    setSelectedId(template.id);
                    setError(undefined);
                  }}
                >
                  <div className="flex items-start justify-between gap-3">
                    <div>
                      <div className="font-medium">{template.name}</div>
                      <div className="mt-1 text-muted-foreground text-xs">{template.description}</div>
                    </div>
                  </div>
                  <div className="mt-2 text-[11px] uppercase tracking-wide text-muted-foreground">
                    {template.category}{template.updatedAt ? ` · ${t.app.templates.updated} ${template.updatedAt}` : ''}
                  </div>
                </button>
              );
            })}
          </div>

          <div className="rounded-lg border p-4">
            <div className="space-y-1.5">
              <Label htmlFor="interactive-template-name">{t.app.templates.workspaceName}</Label>
              <Input
                id="interactive-template-name"
                value={workspaceName}
                autoComplete="off"
                placeholder={t.app.templates.workspaceNamePlaceholder}
                onChange={(event) => setWorkspaceName(event.currentTarget.value)}
              />
              <p className="text-xs text-muted-foreground">Projects are created in Browser storage and automatically mirror to your Projects Folder when one is connected.</p>
            </div>
          </div>

          <DialogFooter>
            <Button variant="outline" disabled={busy} onClick={() => onOpenChange(false)}>
              {t.app.common.cancelButton}
            </Button>
            <Button disabled={busy || catalogLoading || !selected || !workspaceName.trim()} onClick={() => void create()}>
              {busy && <LoaderCircle className="mr-2 size-4 animate-spin" />}
              {t.app.templates.createProject}
            </Button>
          </DialogFooter>
        </DialogContent>
      </Dialog>
    </>
  );
}
