import { useState } from 'react';
import { useTranslation } from 'react-i18next';
import { useQueries, useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { FileTree, type SubdirId, type FileEntry, FILE_SUBDIRS } from './FileTree';
import { MonacoFileEditor } from './MonacoFileEditor';
import { SaveAsScriptDialog } from './SaveAsScriptDialog';
import { ScriptDiffReview } from './ScriptDiffReview';
import { BrowserSessionsPanel } from './BrowserSessionsPanel';
import { McpPanel } from './McpPanel';
import { AgentsMdPanel } from './AgentsMdPanel';
import { NewFileForm } from './NewFileForm';
import { PetsPanel } from './PetsPanel';
import { SshConnectionsPanel } from './SshConnectionsPanel';
import { NotesPanel } from './NotesPanel';
import { SubscriptionsPanel } from './SubscriptionsPanel';
import { SkillsPanel } from './SkillsPanel';
import { MemoryPanel } from './MemoryPanel';
/** All subdirs shown in the tree — both real file-based and virtual. */
const ALL_SUBDIRS: SubdirId[] = ['agents-md', 'browser-macros', 'recordings', 'notes', 'subscribed-notes', 'pets', 'browser-sessions', 'mcp', 'skills', 'ssh-connections', 'trash', 'memory'];

// title/desc/agency are i18n keys under root.subdirs.<key>, translated at render.
const SUBDIR_INFO: { id: SubdirId; icon: string; key: string }[] = [
  { id: 'agents-md', icon: '📖', key: 'agentsMd' },
  { id: 'browser-macros', icon: '🤖', key: 'browserMacros' },
  { id: 'recordings', icon: '🎬', key: 'recordings' },
  { id: 'pets', icon: '◉', key: 'pets' },
  { id: 'browser-sessions', icon: '🌐', key: 'browserSessions' },
  { id: 'trash', icon: '🗑', key: 'trash' },
  { id: 'memory', icon: '🧠', key: 'memory' },
  { id: 'mcp', icon: '🔌', key: 'mcp' },
  { id: 'skills', icon: '📚', key: 'skills' },
  { id: 'ssh-connections', icon: '🔐', key: 'sshConnections' },
  { id: 'notes', icon: '📝', key: 'notes' },
  { id: 'subscribed-notes', icon: '🔔', key: 'subscribedNotes' },
];

interface FolderListResponse {
  files: FileEntry[];
}

async function apiFolderList(subdir: SubdirId): Promise<FileEntry[]> {
  const res = await fetch(`/api/users/me/folder/list?subdir=${subdir}`, {
    credentials: 'include',
  });
  if (!res.ok) throw new Error(`List failed: ${res.status}`);
  const data: FolderListResponse = await res.json();
  return data.files ?? [];
}

interface NoteDiscoverRow {
  folder: string;
  file_name: string;
  updated_at: number;
  content_size: number;
}

/** Fetch all own notes via the discover API (unlimited depth, returns folder/file pairs). */
async function apiNotesList(): Promise<FileEntry[]> {
  const res = await fetch('/api/notes/discover?owner_id=me&limit=200', {
    credentials: 'include',
  });
  if (!res.ok) throw new Error(`Notes list failed: ${res.status}`);
  const data: { rows: NoteDiscoverRow[] } = await res.json();
  return (data.rows ?? []).map((r) => ({
    // Use "folder/file.md" as the virtual file name so FileTree shows the full path
    name: `${r.folder}/${r.file_name}`,
    size: r.content_size,
    mtime: new Date(r.updated_at).toISOString(),
  }));
}

async function apiFolderGet(subdir: SubdirId, path: string): Promise<string> {
  const res = await fetch(
    `/api/users/me/folder/file?subdir=${subdir}&path=${encodeURIComponent(path)}`,
    { credentials: 'include' },
  );
  if (!res.ok) throw new Error(`Fetch failed: ${res.status}`);
  return res.text();
}

async function apiFolderPut(subdir: SubdirId, path: string, body: string): Promise<void> {
  const res = await fetch(
    `/api/users/me/folder/file?subdir=${subdir}&path=${encodeURIComponent(path)}`,
    {
      method: 'PUT',
      credentials: 'include',
      headers: { 'Content-Type': 'text/plain; charset=utf-8' },
      body,
    },
  );
  if (!res.ok) throw new Error(`Save failed: ${res.status}`);
}

async function apiFolderDelete(subdir: SubdirId, path: string): Promise<void> {
  const res = await fetch(
    `/api/users/me/folder/file?subdir=${subdir}&path=${encodeURIComponent(path)}`,
    { method: 'DELETE', credentials: 'include' },
  );
  if (!res.ok) throw new Error(`Delete failed: ${res.status}`);
}

/** Virtual subdirs don't have real files on disk */
const VIRTUAL_SUBDIRS = new Set<SubdirId>(['agents-md', 'browser-sessions', 'mcp', 'skills', 'pets', 'ssh-connections', 'subscribed-notes', 'memory']);

/** Subdirs where users can create new files from the UI */
const WRITABLE_USER_SUBDIRS = new Set<SubdirId>(['browser-macros']);

type ShowToast = (message: string, variant?: 'success' | 'error') => void;

interface UserFolderTabProps {
  showToast?: ShowToast;
}

export function UserFolderTab({ showToast }: UserFolderTabProps = {}) {
  const { t } = useTranslation('userfolder');
  const [selectedSubdir, setSelectedSubdir] = useState<SubdirId | null>('browser-macros');
  const [selectedFile, setSelectedFile] = useState<string | null>(null);
  const [editorDirty, setEditorDirty] = useState(false);
  const [saveAsDialogOpen, setSaveAsDialogOpen] = useState(false);
  const qc = useQueryClient();

  // Fetch the current user (needed for SubscriptionsPanel)
  const meQuery = useQuery<{ id: string }>({
    queryKey: ['auth', 'me'],
    queryFn: async () => {
      const res = await fetch('/api/auth/me');
      if (!res.ok) throw new Error(`${res.status}`);
      return res.json();
    },
    staleTime: 60_000,
  });
  const currentUserId = meQuery.data?.id ?? '';

  // Only file-based subdirs are fetched; notes uses a separate discover endpoint
  // because notes live at depth 2 (notes/<folder>/<file>.md) and folder/list only shows depth 1.
  const fileSubdirs = FILE_SUBDIRS.filter((s) => s !== 'notes');
  const subdirResults = useQueries({
    queries: fileSubdirs.map(subdir => ({
      queryKey: ['userfolder', 'list', subdir],
      queryFn: () => apiFolderList(subdir),
      staleTime: 10_000,
    })),
  });

  // Separate query for notes that uses the discover API instead of the folder-list API
  const notesListQuery = useQuery<FileEntry[]>({
    queryKey: ['userfolder', 'list', 'notes'],
    queryFn: () => apiNotesList(),
    staleTime: 10_000,
  });

  const subdirFilesMap: Partial<Record<SubdirId, { subdir: SubdirId; files: FileEntry[]; loading: boolean }>> = Object.fromEntries(
    fileSubdirs.map((subdir, i) => [
      subdir,
      {
        subdir,
        files: subdirResults[i]!.data ?? [],
        loading: subdirResults[i]!.isLoading,
      },
    ])
  );
  // Inject notes separately using the discover-based listing (depth-2 aware)
  subdirFilesMap['notes'] = {
    subdir: 'notes',
    files: notesListQuery.data ?? [],
    loading: notesListQuery.isLoading,
  };

  // Build the tree data: real subdirs get files, virtual ones get empty placeholders
  const SUBDIRS = ALL_SUBDIRS;
  const subdirQueries = SUBDIRS.map(subdir => {
    if (VIRTUAL_SUBDIRS.has(subdir)) {
      return { subdir, files: [], loading: false };
    }
    return subdirFilesMap[subdir] ?? { subdir, files: [], loading: false };
  });

  // File content query — only when a file is selected (and not virtual subdir)
  const fileQuery = useQuery<string>({
    queryKey: ['userfolder', 'file', selectedSubdir, selectedFile],
    queryFn: () => apiFolderGet(selectedSubdir!, selectedFile!),
    enabled: !!(selectedSubdir && selectedFile && !VIRTUAL_SUBDIRS.has(selectedSubdir)),
    staleTime: 30_000,
    refetchOnWindowFocus: false,
  });

  const deleteMutation = useMutation({
    mutationFn: ({ subdir, file }: { subdir: SubdirId; file: string }) =>
      apiFolderDelete(subdir, file),
    onSuccess: (_data, { subdir, file }) => {
      qc.invalidateQueries({ queryKey: ['userfolder', 'list', subdir] });
      if (selectedSubdir === subdir && selectedFile === file) {
        setSelectedFile(null);
      }
    },
    onError: (err, { subdir, file }) => {
      const msg = err instanceof Error ? err.message : 'Unknown error';
      const label = t('delete.failed', { path: `${subdir}/${file}` });
      if (showToast) showToast(`${label}: ${msg}`, 'error');
      else console.error(`${label}: ${msg}`);
    },
  });

  const selectedSubdirData = subdirQueries.find(q => q.subdir === selectedSubdir);
  const selectedFileMeta = selectedSubdirData?.files.find(f => f.name === selectedFile);

  const handleSave = async (content: string) => {
    if (!selectedSubdir || !selectedFile) return;
    await apiFolderPut(selectedSubdir, selectedFile, content);
    qc.invalidateQueries({ queryKey: ['userfolder', 'list', selectedSubdir] });
    qc.setQueryData(
      ['userfolder', 'file', selectedSubdir, selectedFile],
      content,
    );
  };

  const handleDelete = (subdir: SubdirId, file: string) => {
    if (!window.confirm(`Delete ${subdir}/${file}?`)) return;
    deleteMutation.mutate({ subdir, file });
  };

  function handleSelectSubdir(subdir: SubdirId) {
    if (editorDirty && !window.confirm('You have unsaved changes. Discard them?')) return;
    if (selectedSubdir === subdir) {
      setSelectedSubdir(null);
      setSelectedFile(null);
    } else {
      setSelectedSubdir(subdir);
      setSelectedFile(null);
    }
  }

  function handleSelectFile(subdir: SubdirId, file: string) {
    if (editorDirty && !window.confirm('You have unsaved changes. Discard them?')) return;
    setSelectedSubdir(subdir);
    setSelectedFile(file);
  }

  // Determine right-pane content
  const isVirtualSelected = selectedSubdir !== null && VIRTUAL_SUBDIRS.has(selectedSubdir);

  return (
    <div className="flex h-full gap-2 p-2 overflow-hidden">
      {/* Left: file tree */}
      <div
        className="bg-canvas border border-hairline rounded-md overflow-hidden flex flex-col"
        style={{ width: 'clamp(200px, 22vw, 280px)', flexShrink: 0 }}
      >
        <div className="flex-shrink-0 px-3 py-2.5 border-b border-hairline">
          <span className="text-2xs font-semibold text-slate-500 uppercase tracking-wide">
            User Folder
          </span>
        </div>
        <div className="flex-1 min-h-0 overflow-y-auto">
          <FileTree
            subdirData={subdirQueries}
            selectedSubdir={selectedSubdir}
            selectedFile={selectedFile}
            onSelectSubdir={handleSelectSubdir}
            onSelectFile={handleSelectFile}
            onDeleteFile={handleDelete}
          />
        </div>
      </div>

      {/* Right: editor / virtual panel */}
      <div className="flex-1 min-w-0 bg-canvas border border-hairline rounded-md overflow-hidden flex flex-col">
        {/* agents-md virtual pane */}
        {isVirtualSelected && selectedSubdir === 'agents-md' && (
          <AgentsMdPanel onDirtyChange={setEditorDirty} />
        )}

        {/* browser-sessions virtual pane */}
        {isVirtualSelected && selectedSubdir === 'browser-sessions' && (
          <BrowserSessionsPanel />
        )}

        {/* mcp virtual pane */}
        {isVirtualSelected && selectedSubdir === 'mcp' && (
          <McpPanel showToast={showToast} />
        )}

        {/* skills virtual pane */}
        {isVirtualSelected && selectedSubdir === 'skills' && (
          <SkillsPanel />
        )}

        {/* memory virtual pane */}
        {isVirtualSelected && selectedSubdir === 'memory' && (
          <MemoryPanel />
        )}

        {/* pets virtual pane */}
        {isVirtualSelected && selectedSubdir === 'pets' && (
          <PetsPanel showToast={showToast} />
        )}

        {/* ssh-connections virtual pane */}
        {isVirtualSelected && selectedSubdir === 'ssh-connections' && (
          <SshConnectionsPanel showToast={showToast} />
        )}

        {/* subscribed-notes virtual pane */}
        {isVirtualSelected && selectedSubdir === 'subscribed-notes' && (
          <SubscriptionsPanel currentUserId={currentUserId} />
        )}

        {/* notes/ pane — uses discover API for listing (depth 2) + NotesPanel editor */}
        {selectedSubdir === 'notes' && (
          <NotesPanel
            filePath={selectedFile}
            onSaved={() => {
              qc.invalidateQueries({ queryKey: ['userfolder', 'list', 'notes'] });
            }}
            onSelectFile={(path) => {
              setSelectedFile(path);
            }}
          />
        )}

        {/* File-based content (non-notes subdirs) */}
        {!isVirtualSelected && selectedSubdir !== 'notes' && (
          <>
            {/* Save as Script toolbar — shown only in recordings/ when a .json file is selected */}
            {selectedSubdir === 'recordings' && selectedFile?.endsWith('.json') && (
              <div className="flex-shrink-0 flex items-center gap-2 px-4 py-2 border-b border-hairline bg-surface-2/50">
                <span className="text-2xs text-slate-500 flex-1">
                  Recording: <span className="font-mono">{selectedFile}</span>
                </span>
                <button
                  type="button"
                  onClick={() => setSaveAsDialogOpen(true)}
                  className="px-3 py-1 rounded-md text-2xs font-semibold bg-accent text-accent-fg hover:bg-accent-deep transition-colors"
                >
                  Save as Script
                </button>
              </div>
            )}

            <div className="flex-1 min-h-0 overflow-hidden">
              {selectedSubdir && selectedFile ? (
                /* If a .next.js patch file is selected in browser-macros/, show the diff review pane */
                selectedSubdir === 'browser-macros' && selectedFile.endsWith('.next.js') ? (
                  <ScriptDiffReview
                    scriptName={selectedFile.slice(0, -'.next.js'.length)}
                    showToast={showToast}
                    onClose={(acceptedScript) => {
                      if (acceptedScript) {
                        setSelectedFile(acceptedScript);
                      } else {
                        setSelectedFile(null);
                      }
                    }}
                  />
                ) : fileQuery.isLoading ? (
                  <div className="h-full flex items-center justify-center text-[13px] text-slate-400">
                    Loading…
                  </div>
                ) : fileQuery.isError ? (
                  <div className="h-full flex items-center justify-center text-[13px] text-red-500">
                    Failed to load file.
                  </div>
                ) : (
                  <MonacoFileEditor
                    subdir={selectedSubdir}
                    filename={selectedFile}
                    content={fileQuery.data ?? ''}
                    mtime={selectedFileMeta?.mtime ?? ''}
                    size={selectedFileMeta?.size ?? 0}
                    onSave={handleSave}
                    onDirtyChange={setEditorDirty}
                  />
                )
              ) : (
                <div className="h-full overflow-y-auto">
                  <div className="max-w-2xl mx-auto px-6 py-8">
                    {selectedSubdir && WRITABLE_USER_SUBDIRS.has(selectedSubdir) ? (
                      /* Focused view for a selected writable subdir: info + new-file form */
                      (() => {
                        const info = SUBDIR_INFO.find(i => i.id === selectedSubdir);
                        if (!info) return null;
                        const files = selectedSubdirData?.files ?? [];
                        return (
                          <>
                            <div className="mb-6 flex gap-3">
                              <span className="text-2xl leading-none mt-0.5 select-none" aria-hidden>
                                {info.icon}
                              </span>
                              <div className="flex-1 min-w-0">
                                <h2 className="text-base font-semibold text-slate-900">{t(`root.subdirs.${info.key}.title`)}</h2>
                                <p className="text-[13px] text-slate-500 mt-1 leading-relaxed">{t(`root.subdirs.${info.key}.desc`)}</p>
                                <p className="text-2xs text-slate-400 mt-1 uppercase tracking-wide">{t(`root.subdirs.${info.key}.agency`)}</p>
                              </div>
                            </div>
                            {files.length > 0 && (
                              <div className="mb-4 text-xs text-slate-500">
                                {t('root.fileCount', { count: files.length })}
                              </div>
                            )}
                            <NewFileForm
                              subdir={selectedSubdir as 'browser-macros'}
                              existingFilenames={files.map(f => f.name)}
                              onCreate={async (filename, skeleton) => {
                                await apiFolderPut(selectedSubdir, filename, skeleton);
                                qc.invalidateQueries({ queryKey: ['userfolder', 'list', selectedSubdir] });
                                setSelectedFile(filename);
                              }}
                            />
                          </>
                        );
                      })()
                    ) : (
                      /* Full overview when no subdir is selected (or non-writable subdir selected without a file) */
                      <>
                        <div className="mb-6">
                          <h2 className="text-base font-semibold text-slate-900 mb-1">{t('root.title')}</h2>
                          <p className="text-[13px] text-slate-500 leading-relaxed">
                            {t('root.intro')}
                          </p>
                        </div>
                        <ul className="space-y-5">
                          {SUBDIR_INFO.map(({ id, icon, key }) => (
                            <li key={id} className="flex gap-3">
                              <span className="text-xl leading-none mt-0.5 select-none" aria-hidden>
                                {icon}
                              </span>
                              <div className="flex-1 min-w-0">
                                <div className="text-[13px] font-semibold text-slate-900">{t(`root.subdirs.${key}.title`)}</div>
                                <p className="text-[13px] text-slate-600 mt-1 leading-relaxed">{t(`root.subdirs.${key}.desc`)}</p>
                                <p className="text-2xs text-slate-400 mt-1 uppercase tracking-wide">{t(`root.subdirs.${key}.agency`)}</p>
                              </div>
                            </li>
                          ))}
                        </ul>
                      </>
                    )}
                  </div>
                </div>
              )}
            </div>
          </>
        )}
      </div>

      {/* Save as Script dialog — navigates to browser-macros on success */}
      {saveAsDialogOpen && selectedFile?.endsWith('.json') && (
        <SaveAsScriptDialog
          recordingName={selectedFile.endsWith('.json') ? selectedFile.slice(0, -5) : selectedFile}
          onClose={() => setSaveAsDialogOpen(false)}
          onSuccess={(scriptName) => {
            setSaveAsDialogOpen(false);
            // Navigate to the new macro in browser-macros/
            setSelectedSubdir('browser-macros');
            setSelectedFile(scriptName);
          }}
        />
      )}
    </div>
  );
}
