const fs = require('fs');
const path = require('path');
const AdapterBase = require('./adapter-base');
const claudeDesktopSessions = require('../../claude-task-manager/lib/claude-desktop-sessions');

const MAX_FILE_SIZE = 50 * 1024 * 1024; // 50MB — skip files larger than this

class CtmAdapter extends AdapterBase {
  constructor({ projectsDir, desktopAppSupportDirs, includeClaudeDesktop } = {}) {
    super('ctm');
    const hasCustomProjectsDir = !!projectsDir;
    this.projectsDir = projectsDir || path.join(process.env.HOME, '.claude', 'projects');
    this.desktopAppSupportDirs = desktopAppSupportDirs || null;
    this.includeClaudeDesktop = includeClaudeDesktop !== undefined
      ? includeClaudeDesktop
      : (!hasCustomProjectsDir || !!desktopAppSupportDirs);
    this._processedFiles = new Map(); // filePath -> { size, mtime }
  }

  async poll(since) {
    const sessionFiles = this._getAllSessionFiles();
    const memories = [];

    for (const { filePath, projectPath, projectEntry } of sessionFiles) {
      let stat;
      try { stat = fs.statSync(claudeDesktopSessions.sourcePathForStat(filePath)); } catch { continue; }

      // Skip files that are too large to avoid OOM
      if (stat.size > MAX_FILE_SIZE) continue;

      const prev = this._processedFiles.get(filePath);
      if (prev && prev.size === stat.size && prev.mtimeMs === stat.mtimeMs) {
        continue;
      }
      this._processedFiles.set(filePath, { size: stat.size, mtimeMs: stat.mtimeMs });

      const entries = this._parseSessionFile(filePath);
      for (const entry of entries) {
        entry._projectPath = projectPath;
        entry._projectEntry = projectEntry;
        entry._filePath = filePath;
        const normalized = this.normalize(entry);
        if (!normalized) continue;
        if (since && normalized.timestamp && normalized.timestamp <= since) {
          continue;
        }
        memories.push(normalized);
      }
    }

    return memories;
  }

  normalize(rawData) {
    if (!rawData || !rawData.message) return null;
    const msg = rawData.message;
    const role = msg.role || 'unknown';
    const isUser = role === 'user';
    const direction = isUser ? 'outbound' : 'observed';
    const memoryType = isUser ? 'message_sent' : 'assistant_response';

    // Extract text content
    let content = '';
    let toolUses = [];

    if (typeof msg.content === 'string') {
      content = msg.content;
    } else if (Array.isArray(msg.content)) {
      const textParts = [];
      for (const block of msg.content) {
        if (block.type === 'text') {
          textParts.push(block.text);
        } else if (block.type === 'tool_use') {
          const summary = this._summarizeInput(block.name, block.input);
          toolUses.push({
            name: block.name,
            input_summary: summary,
          });
          textParts.push(`[Tool: ${block.name}] ${summary}`);
        }
      }
      content = textParts.join('\n');
    }

    if (!content || !content.trim()) return null;

    const sourceId = rawData.uuid || rawData._filePath || `ctm-${Date.now()}`;
    const timestamp = rawData.timestamp || new Date().toISOString();

    const source = rawData._source === claudeDesktopSessions.DESKTOP_AGENT ? claudeDesktopSessions.DESKTOP_AGENT : 'ctm';
    const result = {
      source,
      source_id: sourceId,
      memory_type: memoryType,
      direction,
      content,
      timestamp,
    };

    if (rawData.cwd) {
      result.source_channel = rawData.cwd;
    }
    if (rawData._projectPath) {
      result.subject = rawData._projectPath;
    }

    const metadata = {};
    if (toolUses.length > 0) {
      metadata.tool_uses = toolUses;
    }
    if (Object.keys(metadata).length > 0) {
      result.metadata = JSON.stringify(metadata);
    }

    return result;
  }

  _getAllSessionFiles() {
    const results = [];

    if (fs.existsSync(this.projectsDir)) {
      let entries;
      try { entries = fs.readdirSync(this.projectsDir); } catch { entries = []; }

      for (const projectEntry of entries) {
        const projectDir = path.join(this.projectsDir, projectEntry);
        let stat;
        try { stat = fs.statSync(projectDir); } catch { continue; }
        if (!stat.isDirectory()) continue;

        const projectPath = this._decodeProjectEntry(projectEntry);
        let files;
        try { files = fs.readdirSync(projectDir); } catch { continue; }

        for (const file of files) {
          if (!file.endsWith('.jsonl') && !file.endsWith('.jsonl.bak')) continue;
          const filePath = path.join(projectDir, file);
          results.push({ filePath, projectPath, projectEntry });
        }
      }
    }

    if (this.includeClaudeDesktop) {
      for (const entry of claudeDesktopSessions.listSessionFileEntries({
        appSupportDirs: this.desktopAppSupportDirs || undefined,
      })) {
        results.push(entry);
      }
    }
    return results;
  }

  _parseSessionFile(filePath) {
    const desktopSession = claudeDesktopSessions.getSessionFromVirtualPath(filePath, {
      appSupportDirs: this.desktopAppSupportDirs || undefined,
    });
    if (desktopSession) return claudeDesktopSessions.toClaudeCodeEntries(desktopSession);

    let data;
    try { data = fs.readFileSync(filePath, 'utf8'); } catch { return []; }

    const lines = data.split('\n').filter(Boolean);
    const entries = [];

    for (const line of lines) {
      try {
        const entry = JSON.parse(line);
        if (entry && entry.type && entry.message) {
          entries.push(entry);
        }
      } catch {
        // skip malformed lines
      }
    }
    return entries;
  }

  _decodeProjectEntry(entry) {
    try {
      const sessionUtils = require(path.join(
        process.env.HOME, 'ws', 'tools', 'claude-task-manager', 'session-utils.js'
      ));
      if (typeof sessionUtils.decodeProjectEntry === 'function') {
        return sessionUtils.decodeProjectEntry(entry);
      }
    } catch {
      // fall back to local decode
    }
    // Local fallback: leading dash -> /, remaining dashes -> /
    if (!entry.startsWith('-')) return entry;
    return '/' + entry.slice(1).replace(/-/g, '/');
  }

  _summarizeInput(toolName, input) {
    if (!input) return '';
    switch (toolName) {
      case 'Read':
      case 'Write':
      case 'Edit':
        return input.file_path || '';
      case 'Bash': {
        const cmd = input.command || '';
        return cmd.length > 80 ? cmd.slice(0, 77) + '...' : cmd;
      }
      case 'Glob':
      case 'Grep':
        return input.pattern || '';
      default:
        return '';
    }
  }
}

module.exports = CtmAdapter;
