import { feature } from 'bun:bundle';
import chalk from 'chalk';
import { mkdir } from 'fs/promises';
import { join } from 'path';
import * as React from 'react';
import { use, useEffect, useState } from 'react';
import { getOriginalCwd } from '../../bootstrap/state.js';
import { useExitOnCtrlCDWithKeybindings } from '../../hooks/useExitOnCtrlCDWithKeybindings.js';
import { Box, Text, ListItem } from '@anthropic/ink';
import { useKeybinding } from '../../keybindings/useKeybinding.js';
import { getAutoMemPath, isAutoMemoryEnabled } from '../../memdir/paths.js';
import { logEvent } from '../../services/analytics/index.js';
import { isAutoDreamEnabled } from '../../services/autoDream/config.js';
import { readLastConsolidatedAt } from '../../services/autoDream/consolidationLock.js';
import { useAppState } from '../../state/AppState.js';
import { getAgentMemoryDir } from '@claude-code-best/builtin-tools/tools/AgentTool/agentMemory.js';
import { openPath } from '../../utils/browser.js';
import { getMemoryFiles, type MemoryFileInfo } from '../../utils/claudemd.js';
import { getClaudeConfigHomeDir } from '../../utils/envUtils.js';
import { getDisplayPath } from '../../utils/file.js';
import { formatRelativeTimeAgo } from '../../utils/format.js';
import { projectIsInGitRepo } from '../../utils/memory/versions.js';
import { updateSettingsForSource } from '../../utils/settings/settings.js';
import { Select } from '../CustomSelect/index.js';

/* eslint-disable @typescript-eslint/no-require-imports */
const teamMemPaths = feature('TEAMMEM')
  ? (require('../../memdir/teamMemPaths.js') as typeof import('../../memdir/teamMemPaths.js'))
  : null;
/* eslint-enable @typescript-eslint/no-require-imports */

interface ExtendedMemoryFileInfo extends MemoryFileInfo {
  isNested?: boolean;
  exists: boolean;
}

// Remember last selected path
let lastSelectedPath: string | undefined;

const OPEN_FOLDER_PREFIX = '__open_folder__';

type Props = {
  onSelect: (path: string) => void;
  onCancel: () => void;
};

export function MemoryFileSelector({ onSelect, onCancel }: Props): React.ReactNode {
  const existingMemoryFiles = use(getMemoryFiles()) as MemoryFileInfo[];

  // Create entries for User and Project CLAUDE.md even if they don't exist
  const userMemoryPath = join(getClaudeConfigHomeDir(), 'CLAUDE.md');
  const projectMemoryPath = join(getOriginalCwd(), 'CLAUDE.md');

  // Check if these are already in the existing files
  const hasUserMemory = existingMemoryFiles.some(f => f.path === userMemoryPath);
  const hasProjectMemory = existingMemoryFiles.some(f => f.path === projectMemoryPath);

  // Filter out AutoMem/TeamMem entrypoints: these are MEMORY.md files, and
  // /memory already surfaces "Open auto-memory folder" / "Open team memory
  // folder" options below. Listing the entrypoint file separately is redundant.
  const allMemoryFiles: ExtendedMemoryFileInfo[] = [
    ...existingMemoryFiles.filter(f => f.type !== 'AutoMem' && f.type !== 'TeamMem').map(f => ({ ...f, exists: true })),
    // Add User memory if it doesn't exist
    ...(hasUserMemory
      ? []
      : [
          {
            path: userMemoryPath,
            type: 'User' as const,
            content: '',
            exists: false,
          },
        ]),
    // Add Project memory if it doesn't exist
    ...(hasProjectMemory
      ? []
      : [
          {
            path: projectMemoryPath,
            type: 'Project' as const,
            content: '',
            exists: false,
          },
        ]),
  ];

  const depths = new Map<string, number>();

  // Create options for the select component
  const memoryOptions = allMemoryFiles.map(file => {
    const displayPath = getDisplayPath(file.path);
    const existsLabel = file.exists ? '' : ' (new)';

    // Calculate depth based on parent
    const depth = file.parent ? (depths.get(file.parent) ?? 0) + 1 : 0;
    depths.set(file.path, depth);
    const indent = depth > 0 ? '  '.repeat(depth - 1) : '';

    // Format label based on type
    let label: string;
    if (file.type === 'User' && !file.isNested && file.path === userMemoryPath) {
      label = `User memory`;
    } else if (file.type === 'Project' && !file.isNested && file.path === projectMemoryPath) {
      label = `Project memory`;
    } else if (depth > 0) {
      // For child nodes (imported files), show indented with L
      label = `${indent}L ${displayPath}${existsLabel}`;
    } else {
      // For other memory files, just show the path
      label = `${displayPath}`;
    }

    // Create description based on type - keep the original descriptions for built-in types
    let description: string;
    const isGit = projectIsInGitRepo(getOriginalCwd());

    if (file.type === 'User' && !file.isNested) {
      description = 'Saved in ~/.claude/CLAUDE.md';
    } else if (file.type === 'Project' && !file.isNested && file.path === projectMemoryPath) {
      description = `${isGit ? 'Checked in at' : 'Saved in'} ./CLAUDE.md`;
    } else if (file.parent) {
      // For imported files (with @-import)
      description = '@-imported';
    } else if (file.isNested) {
      // For nested files (dynamically loaded)
      description = 'dynamically loaded';
    } else {
      description = '';
    }

    return {
      label,
      value: file.path,
      description,
    };
  });

  // Add "Open folder" options for auto-memory and agent memory directories
  const folderOptions: Array<{
    label: string;
    value: string;
    description: string;
  }> = [];

  const agentDefinitions = useAppState(s => s.agentDefinitions);
  if (isAutoMemoryEnabled()) {
    // Always show auto-memory folder option
    folderOptions.push({
      label: 'Open auto-memory folder',
      value: `${OPEN_FOLDER_PREFIX}${getAutoMemPath()}`,
      description: '',
    });

    // Team memory directly below auto-memory (team dir is a subdir of auto dir)
    if (feature('TEAMMEM') && teamMemPaths!.isTeamMemoryEnabled()) {
      folderOptions.push({
        label: 'Open team memory folder',
        value: `${OPEN_FOLDER_PREFIX}${teamMemPaths!.getTeamMemPath()}`,
        description: '',
      });
    }

    // Add agent memory folders for agents that have memory configured
    for (const agent of agentDefinitions.activeAgents) {
      if (agent.memory) {
        const agentDir = getAgentMemoryDir(agent.agentType, agent.memory);
        folderOptions.push({
          label: `Open ${chalk.bold(agent.agentType)} agent memory`,
          value: `${OPEN_FOLDER_PREFIX}${agentDir}`,
          description: `${agent.memory} scope`,
        });
      }
    }
  }

  memoryOptions.push(...folderOptions);

  // Initialize with last selected path if it's still in the options, otherwise use first option
  const initialPath =
    lastSelectedPath && memoryOptions.some(opt => opt.value === lastSelectedPath)
      ? lastSelectedPath
      : memoryOptions[0]?.value || '';

  // Toggle state (local copy of settings so the UI updates immediately)
  const [autoMemoryOn, setAutoMemoryOn] = useState(isAutoMemoryEnabled);
  const [autoDreamOn, setAutoDreamOn] = useState(isAutoDreamEnabled);

  // Dream row is only meaningful when auto-memory is on (dream consolidates
  // that dir). Snapshot at mount so the row doesn't vanish mid-navigation
  // if the user toggles auto-memory off.
  const [showDreamRow] = useState(isAutoMemoryEnabled);

  // Dream status: prefer live task state (this session fired it), fall back
  // to the cross-process lock mtime.
  const isDreamRunning = useAppState(s =>
    Object.values(s.tasks).some(t => t.type === 'dream' && t.status === 'running'),
  );
  const [lastDreamAt, setLastDreamAt] = useState<number | null>(null);
  useEffect(() => {
    if (!showDreamRow) return;
    void readLastConsolidatedAt().then(setLastDreamAt);
  }, [showDreamRow, isDreamRunning]);

  const dreamStatus = isDreamRunning
    ? 'running'
    : lastDreamAt === null
      ? '' // stat in flight
      : lastDreamAt === 0
        ? 'never'
        : `last ran ${formatRelativeTimeAgo(new Date(lastDreamAt))}`;

  // null = Select has focus, 0 = auto-memory, 1 = auto-dream (if showDreamRow)
  const [focusedToggle, setFocusedToggle] = useState<number | null>(null);
  const toggleFocused = focusedToggle !== null;
  const lastToggleIndex = showDreamRow ? 1 : 0;

  function handleToggleAutoMemory(): void {
    const newValue = !autoMemoryOn;
    updateSettingsForSource('userSettings', { autoMemoryEnabled: newValue });
    setAutoMemoryOn(newValue);
    logEvent('tengu_auto_memory_toggled', { enabled: newValue });
  }

  function handleToggleAutoDream(): void {
    const newValue = !autoDreamOn;
    updateSettingsForSource('userSettings', { autoDreamEnabled: newValue });
    setAutoDreamOn(newValue);
    logEvent('tengu_auto_dream_toggled', { enabled: newValue });
  }

  useExitOnCtrlCDWithKeybindings();

  useKeybinding('confirm:no', onCancel, { context: 'Confirmation' });

  useKeybinding(
    'confirm:yes',
    () => {
      if (focusedToggle === 0) handleToggleAutoMemory();
      else if (focusedToggle === 1) handleToggleAutoDream();
    },
    { context: 'Confirmation', isActive: toggleFocused },
  );
  useKeybinding(
    'select:next',
    () => {
      setFocusedToggle(prev => (prev !== null && prev < lastToggleIndex ? prev + 1 : null));
    },
    { context: 'Select', isActive: toggleFocused },
  );
  useKeybinding(
    'select:previous',
    () => {
      setFocusedToggle(prev => (prev !== null && prev > 0 ? prev - 1 : prev));
    },
    { context: 'Select', isActive: toggleFocused },
  );

  return (
    <Box flexDirection="column" width="100%">
      <Box flexDirection="column" marginBottom={1}>
        <ListItem isFocused={focusedToggle === 0}>
          <Text>Auto-memory: {autoMemoryOn ? 'on' : 'off'}</Text>
        </ListItem>
        {showDreamRow && (
          <ListItem isFocused={focusedToggle === 1} styled={false}>
            <Text color={focusedToggle === 1 ? 'suggestion' : undefined}>
              Auto-dream: {autoDreamOn ? 'on' : 'off'}
              {dreamStatus && <Text dimColor> · {dreamStatus}</Text>}
              {!isDreamRunning && autoDreamOn && <Text dimColor> · /dream to run</Text>}
            </Text>
          </ListItem>
        )}
      </Box>

      <Select
        defaultFocusValue={initialPath}
        options={memoryOptions}
        isDisabled={toggleFocused}
        onChange={value => {
          if (value.startsWith(OPEN_FOLDER_PREFIX)) {
            const folderPath = value.slice(OPEN_FOLDER_PREFIX.length);
            // Ensure folder exists before opening (idempotent; swallow
            // permission errors to match previous behavior)
            void mkdir(folderPath, { recursive: true })
              .catch(() => {})
              .then(() => openPath(folderPath));
            return;
          }
          lastSelectedPath = value; // Remember the selection
          onSelect(value);
        }}
        onCancel={onCancel}
        onUpFromFirstItem={() => setFocusedToggle(lastToggleIndex)}
      />
    </Box>
  );
}
