'use strict';

const fs = require('fs');
const path = require('path');
const os = require('os');
const crypto = require('crypto');

const VERSION = 'walle-jsonl-v1';
const WALLE_PROJECT_ENTRY = '__walle__';

function defaultSessionsDir(env = process.env) {
  if (env.WALLE_SESSIONS_DIR) return env.WALLE_SESSIONS_DIR;
  if (env.WALL_E_SESSIONS_DIR) return env.WALL_E_SESSIONS_DIR;
  if (env.WALLE_DEV_DIR) return path.join(env.WALLE_DEV_DIR, 'sessions');
  if (env.WALL_E_DATA_DIR && env.WALL_E_DATA_DIR !== path.join(os.homedir(), '.walle', 'data')) {
    return path.join(env.WALL_E_DATA_DIR, 'sessions');
  }
  // CTM_DATA_DIR belongs to the task manager; Wall-E transcripts stay in Wall-E-owned storage.
  return path.join(os.homedir(), '.walle', 'sessions');
}

function sessionPath(rootDir, sessionId) {
  return path.join(rootDir, `${sessionId}.jsonl`);
}

function ensureDirForFile(filePath) {
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
}

function newUuid() {
  return crypto.randomUUID ? crypto.randomUUID() : crypto.randomBytes(16).toString('hex');
}

function nowIso() {
  return new Date().toISOString();
}

function textBlocks(text) {
  return [{ type: 'text', text: String(text || '') }];
}

function attachmentMetadata(attachments) {
  if (!Array.isArray(attachments) || attachments.length === 0) return [];
  return attachments
    .filter((attachment) => attachment && typeof attachment === 'object')
    .map((attachment, index) => {
      const out = {
        type: attachment.type || 'image',
        label: attachment.label || `[Image #${index + 1}]`,
      };
      const copy = [
        'name',
        'filename',
        'mediaType',
        'mimeType',
        'url',
        'path',
        'file_path',
        'id',
        'imageWidth',
        'imageHeight',
        'originalWidth',
        'originalHeight',
        'resizedForProvider',
        'byteSize',
      ];
      for (const key of copy) {
        if (attachment[key] !== undefined && attachment[key] !== null && attachment[key] !== '') {
          out[key] = attachment[key];
        }
      }
      return out;
    });
}

function appendRecord(filePath, record) {
  ensureDirForFile(filePath);
  const offset = fs.existsSync(filePath) ? fs.statSync(filePath).size : 0;
  const line = JSON.stringify(record) + '\n';
  fs.appendFileSync(filePath, line, 'utf8');
  return {
    filePath,
    offset,
    bytes: Buffer.byteLength(line),
    record,
    uuid: record.uuid || '',
  };
}

function createSession(filePath, opts = {}) {
  ensureDirForFile(filePath);
  if (opts.reset !== false || !fs.existsSync(filePath)) {
    fs.writeFileSync(filePath, '', 'utf8');
  }
  if (opts.reset === false && fs.existsSync(filePath) && fs.statSync(filePath).size > 0) {
    return null;
  }

  const record = {
    type: 'session_meta',
    provider: 'walle',
    version: VERSION,
    sessionId: opts.sessionId || '',
    chatSessionId: opts.chatSessionId || '',
    cwd: opts.cwd || '',
    label: opts.label || '',
    modelId: opts.modelId || '',
    modelProvider: opts.modelProvider || '',
    agentMode: opts.agentMode || '',
    agentKind: opts.agentKind || '',
    taskType: opts.taskType || '',
    gitBranch: opts.gitBranch || '',
    hostname: os.hostname(),
    uuid: newUuid(),
    timestamp: opts.timestamp || nowIso(),
  };
  return appendRecord(filePath, record);
}

function baseMessage(opts, type, role) {
  return {
    type,
    provider: 'walle',
    version: VERSION,
    sessionId: opts.sessionId || '',
    chatSessionId: opts.chatSessionId || '',
    uuid: opts.uuid || newUuid(),
    parentUuid: opts.parentUuid || null,
    cwd: opts.cwd || '',
    timestamp: opts.timestamp || nowIso(),
    message: {
      role,
      content: textBlocks(opts.text),
    },
  };
}

function appendUserMessage(filePath, opts = {}) {
  const record = baseMessage(opts, 'user', 'user');
  if (opts.modelId) record.modelId = opts.modelId;
  if (opts.modelProvider) record.modelProvider = opts.modelProvider;
  const attachments = attachmentMetadata(opts.attachments);
  if (attachments.length) record.attachments = attachments;
  return appendRecord(filePath, record);
}

function appendAssistantMessage(filePath, opts = {}) {
  const record = baseMessage(opts, 'assistant', 'assistant');
  record.message.model = opts.model || opts.modelId || '';
  record.modelId = opts.model || opts.modelId || '';
  record.modelProvider = opts.modelProvider || opts.provider || '';
  record.latencyMs = Number.isFinite(opts.latencyMs) ? opts.latencyMs : null;
  record.tokens = opts.tokens || {};
  record.cost = Number.isFinite(opts.cost) ? opts.cost : null;
  if (Array.isArray(opts.toolCalls)) {
    record.toolCalls = JSON.parse(JSON.stringify(opts.toolCalls));
  }
  return appendRecord(filePath, record);
}

function appendPart(filePath, opts = {}) {
  const record = {
    type: 'walle_part',
    provider: 'walle',
    version: VERSION,
    sessionId: opts.sessionId || '',
    chatSessionId: opts.chatSessionId || '',
    uuid: opts.uuid || newUuid(),
    parentUuid: opts.parentUuid || null,
    cwd: opts.cwd || '',
    timestamp: opts.timestamp || nowIso(),
    partType: opts.partType || 'event',
    data: opts.data && typeof opts.data === 'object' ? opts.data : {},
  };
  return appendRecord(filePath, record);
}

function extractText(content) {
  if (!content) return '';
  if (typeof content === 'string') return content;
  if (Array.isArray(content)) {
    return content
      .filter((block) => block && block.type === 'text' && block.text)
      .map((block) => block.text)
      .join('\n');
  }
  if (typeof content.text === 'string') return content.text;
  return '';
}

function readLastUuid(filePath) {
  let raw;
  try { raw = fs.readFileSync(filePath, 'utf8'); } catch { return ''; }
  const lines = raw.trim().split('\n').filter(Boolean);
  for (let i = lines.length - 1; i >= 0; i--) {
    try {
      const row = JSON.parse(lines[i]);
      if (row && row.uuid) return row.uuid;
    } catch { /* skip malformed tail rows */ }
  }
  return '';
}

function readSessionMeta(filePath) {
  let raw;
  try { raw = fs.readFileSync(filePath, 'utf8'); } catch { return null; }
  const lines = raw.split('\n').filter(Boolean);
  for (const line of lines) {
    try {
      const row = JSON.parse(line);
      if (row && row.type === 'session_meta') return row;
    } catch {
      // Ignore malformed tail rows; the transcript writer is append-only.
    }
  }
  return null;
}

module.exports = {
  VERSION,
  WALLE_PROJECT_ENTRY,
  defaultSessionsDir,
  sessionPath,
  createSession,
  appendRecord,
  appendUserMessage,
  appendAssistantMessage,
  appendPart,
  extractText,
  readLastUuid,
  readSessionMeta,
};
