'use strict';

const fs = require('node:fs');
const path = require('node:path');
const crypto = require('node:crypto');

const { applyTransforms } = require('./transforms');

function readJsonlFile(filePath) {
  const events = [];
  const diagnostics = [];
  let raw = '';
  try {
    raw = fs.readFileSync(filePath, 'utf8');
  } catch (err) {
    return { events, diagnostics: [{ level: 'error', message: err.message, filePath }] };
  }
  const lines = raw.split('\n');
  for (let i = 0; i < lines.length; i++) {
    const line = lines[i];
    if (!line.trim()) continue;
    try {
      events.push({ line: i + 1, event: JSON.parse(line) });
    } catch (err) {
      diagnostics.push({ level: 'warn', message: `Malformed JSONL line ${i + 1}: ${err.message}`, filePath, line: i + 1 });
    }
  }
  return { events, diagnostics };
}

function fileVersion(filePath) {
  try {
    const stat = fs.statSync(filePath);
    return `mtime:${Math.round(stat.mtimeMs)}:size:${stat.size}`;
  } catch {
    return 'missing';
  }
}

function sourceIdFromFile(prefix, filePath) {
  const base = path.basename(filePath || 'unknown', '.jsonl');
  const hash = crypto.createHash('sha1').update(path.resolve(filePath || base)).digest('hex').slice(0, 12);
  return `${prefix}:${base}:${hash}`;
}

function extractMessageText(value) {
  if (value == null) return '';
  if (typeof value === 'string') return value;
  if (Array.isArray(value)) return extractBlocksText(value);
  if (typeof value !== 'object') return String(value);
  if (value.message?.content != null) return extractMessageText(value.message.content);
  if (value.content != null) return extractMessageText(value.content);
  if (value.text != null) return String(value.text);
  if (value.parts != null) return extractMessageText(value.parts);
  return '';
}

function extractBlocksText(blocks) {
  const parts = [];
  for (const block of blocks || []) {
    if (!block) continue;
    const type = block.type || '';
    if (type === 'text' || type === 'input_text' || type === 'output_text') {
      if (typeof block.text === 'string') parts.push(block.text);
      continue;
    }
    if (type === 'tool_use' || type === 'function_call') {
      const name = block.name || block.function?.name || 'tool';
      parts.push(`[Tool: ${name}] ${summarizeToolInput(name, block.input ?? block.arguments)}`);
      continue;
    }
    if (type === 'tool_result' || type === 'function_call_output') {
      const content = block.content ?? block.output ?? '';
      const text = extractMessageText(content);
      if (text) parts.push(`[Tool result] ${text}`);
      continue;
    }
    if (block.text) parts.push(String(block.text));
  }
  return parts.join('\n').trim();
}

function extractToolCalls(value) {
  const calls = [];
  const blocks = Array.isArray(value?.content) ? value.content
    : Array.isArray(value) ? value
      : [];
  for (const block of blocks) {
    if (!block || typeof block !== 'object') continue;
    if (block.type === 'tool_use' || block.type === 'function_call') {
      calls.push({
        name: block.name || block.function?.name || 'tool',
        input: parseToolInput(block.input ?? block.arguments ?? {}),
      });
    }
  }
  return calls;
}

function parseToolInput(value) {
  if (value == null) return {};
  if (typeof value === 'object') return value;
  if (typeof value !== 'string') return { value: String(value) };
  try {
    const parsed = JSON.parse(value);
    return parsed && typeof parsed === 'object' ? parsed : { value: parsed };
  } catch {
    return { raw: value };
  }
}

function summarizeToolInput(toolName, input) {
  const parsed = parseToolInput(input);
  if (!parsed || typeof parsed !== 'object') return '';
  const direct = parsed.file_path || parsed.filePath || parsed.path || parsed.command || parsed.cmd || parsed.pattern || parsed.query || parsed.raw;
  if (direct) return truncate(String(direct), 160);
  const keys = Object.keys(parsed);
  if (keys.length === 0) return '';
  return truncate(JSON.stringify(parsed), 160);
}

function cleanUserText(text) {
  if (!text) return '';
  return String(text)
    .replace(/\r\n/g, '\n')
    .replace(/^\s*<environment_context>[\s\S]*?<\/environment_context>\s*/gi, '')
    .replace(/^\s*<permissions instructions>[\s\S]*?<\/permissions instructions>\s*/gi, '')
    .replace(/^\s*<skills_instructions>[\s\S]*?<\/skills_instructions>\s*/gi, '')
    .replace(/^\s*<plugins_instructions>[\s\S]*?<\/plugins_instructions>\s*/gi, '')
    .replace(/^\s*<skill>[\s\S]*?<\/skill>\s*/gi, '')
    .trim();
}

function looksLikeToolResult(text) {
  const trimmed = String(text || '').trim();
  if (!trimmed) return true;
  const head = trimmed.slice(0, 600);
  return [
    /^\[Request interrupted/i,
    /^<tool_use_error>/i,
    /^<system-reminder>/i,
    /^<local-command-/i,
    /^<command-/i,
    /^<task-notification>/i,
    /^<environment_context>/i,
    /^<permissions instructions>/i,
    /^<skills_instructions>/i,
    /^<plugins_instructions>/i,
    /^Base directory for this skill:/i,
  ].some((re) => re.test(head));
}

function cleanedContent(raw, transforms, context = {}) {
  return applyTransforms(raw, transforms, context);
}

function truncate(value, max) {
  const text = String(value || '');
  return text.length > max ? `${text.slice(0, max - 3)}...` : text;
}

function toIso(value) {
  if (!value) return new Date().toISOString();
  const date = new Date(value);
  return Number.isNaN(date.getTime()) ? new Date().toISOString() : date.toISOString();
}

module.exports = {
  cleanUserText,
  cleanedContent,
  extractMessageText,
  extractToolCalls,
  fileVersion,
  looksLikeToolResult,
  parseToolInput,
  readJsonlFile,
  sourceIdFromFile,
  summarizeToolInput,
  toIso,
  truncate,
};
