// --- WALL-E Listening Skill: Meeting Intelligence ---
// LLM-powered meeting notes, action items, and email drafts.
// Stores outputs as Wall-E memories for the think/reflect pipeline.

const brain = require('../brain');

const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
function _validateId(id) {
  if (!id || !UUID_RE.test(id)) throw new Error('Invalid recording ID');
}

/**
 * Generate structured meeting notes from a recording's transcript.
 * @param {string} recordingId
 * @returns {Promise<{notes: string, summary: string}>}
 */
async function generateMeetingNotes(recordingId) {
  _validateId(recordingId);
  const { transcript, segments } = _getTranscriptData(recordingId);

  const formatted = _formatTranscript(transcript, segments);
  const provider = _getProvider();
  const model = _getModel();

  const response = await provider.chat({
    model,
    maxTokens: 4096,
    messages: [{ role: 'user', content:
      'Generate structured meeting notes from this transcript.\n\n' +
      'Format your response as markdown with these sections:\n' +
      '## Summary\nA 2-3 sentence summary of the meeting.\n\n' +
      '## Key Discussion Points\nBulleted list of main topics discussed.\n\n' +
      '## Decisions Made\nBulleted list of decisions or agreements reached.\n\n' +
      '## Open Questions\nAny unresolved items or follow-ups mentioned.\n\n' +
      'TRANSCRIPT:\n' + formatted
    }]
  });

  const notes = response.content || '';
  const summaryMatch = notes.match(/## Summary\n([\s\S]*?)(?=\n## |$)/);
  const summary = summaryMatch ? summaryMatch[1].trim() : notes.slice(0, 200);

  return { notes, summary };
}

/**
 * Extract action items from transcript.
 * @param {string} recordingId
 * @returns {Promise<Array<{text: string, assignee: string|null, deadline: string|null, priority: string}>>}
 */
async function extractActionItems(recordingId) {
  _validateId(recordingId);
  const { transcript, segments } = _getTranscriptData(recordingId);

  const formatted = _formatTranscript(transcript, segments);
  const provider = _getProvider();
  const model = _getModel();

  const response = await provider.chat({
    model,
    maxTokens: 2048,
    messages: [{ role: 'user', content:
      'Extract action items from this meeting transcript.\n\n' +
      'Return a JSON array where each item has:\n' +
      '- "text": description of the action item\n' +
      '- "assignee": person responsible (from speaker names, or null)\n' +
      '- "deadline": any mentioned deadline (or null)\n' +
      '- "priority": "high", "medium", or "low"\n\n' +
      'Return ONLY the JSON array, no other text.\n\n' +
      'TRANSCRIPT:\n' + formatted
    }]
  });

  try {
    const text = response.content || '[]';
    const jsonMatch = text.match(/\[[\s\S]*\]/);
    return jsonMatch ? JSON.parse(jsonMatch[0]) : [];
  } catch {
    return [];
  }
}

/**
 * Generate email draft summarizing the meeting.
 * @param {string} recordingId
 * @param {object} [opts] - {recipients, tone, includeActionItems}
 * @returns {Promise<{subject: string, body: string}>}
 */
async function generateEmailDraft(recordingId, opts) {
  _validateId(recordingId);
  opts = opts || {};
  const { transcript, segments } = _getTranscriptData(recordingId);
  const rec = brain.getDb().prepare('SELECT title, mode FROM recordings WHERE id = ?').get(recordingId);

  const formatted = _formatTranscript(transcript, segments);
  const provider = _getProvider();
  const model = _getModel();

  let prompt = 'Generate a professional email summarizing this meeting.\n\n';
  if (opts.recipients) prompt += 'Recipients: ' + opts.recipients + '\n';
  if (opts.tone) prompt += 'Tone: ' + opts.tone + '\n';
  if (opts.includeActionItems) prompt += 'Include action items in the email.\n';
  prompt += '\nFormat as:\nSUBJECT: <subject line>\n\nBODY:\n<email body>\n\n';
  prompt += 'Meeting title: ' + (rec ? rec.title : 'Meeting') + '\n\n';
  prompt += 'TRANSCRIPT:\n' + formatted;

  const response = await provider.chat({
    model,
    maxTokens: 2048,
    messages: [{ role: 'user', content: prompt }]
  });

  const text = response.content || '';
  const subjectMatch = text.match(/SUBJECT:\s*(.+)/i);
  const bodyMatch = text.match(/BODY:\s*([\s\S]+)/i);

  return {
    subject: subjectMatch ? subjectMatch[1].trim() : 'Meeting Summary',
    body: bodyMatch ? bodyMatch[1].trim() : text
  };
}

/**
 * Run the full meeting intelligence pipeline and store as memories.
 * @param {string} recordingId
 * @returns {Promise<{notes: string, summary: string, actionItems: Array, emailDraft: object, memoryIds: string[]}>}
 */
async function processRecording(recordingId) {
  _validateId(recordingId);
  const rec = brain.getDb().prepare('SELECT title, stopped_at FROM recordings WHERE id = ?').get(recordingId);
  if (!rec) throw new Error('Recording not found');

  // Run generators (notes + action items in parallel, email after)
  const [notesResult, actionItems] = await Promise.all([
    generateMeetingNotes(recordingId),
    extractActionItems(recordingId)
  ]);

  const emailDraft = await generateEmailDraft(recordingId, { includeActionItems: true });

  // Store as memories
  const memoryIds = storeAsMemories(recordingId, {
    notes: notesResult.notes,
    summary: notesResult.summary,
    actionItems,
    emailDraft,
    title: rec.title,
    timestamp: rec.stopped_at || new Date().toISOString()
  });

  return {
    notes: notesResult.notes,
    summary: notesResult.summary,
    actionItems,
    emailDraft,
    memoryIds
  };
}

/**
 * Store meeting intelligence outputs as Wall-E memories.
 * @param {string} recordingId
 * @param {object} outputs
 * @returns {string[]} - Array of created memory IDs
 */
function storeAsMemories(recordingId, outputs) {
  const ids = [];
  const ts = outputs.timestamp || new Date().toISOString();
  const title = outputs.title || 'Meeting';

  // Meeting notes
  if (outputs.notes) {
    const result = brain.insertMemory({
      source: 'listening',
      source_id: 'recording:' + recordingId + ':meeting_notes',
      memory_type: 'meeting_notes',
      subject: 'Meeting Notes: ' + title,
      content: outputs.notes,
      importance: 0.8,
      timestamp: ts
    });
    if (result) ids.push(result.id);
  }

  // Action items (one memory per item)
  if (outputs.actionItems && outputs.actionItems.length > 0) {
    const itemsText = outputs.actionItems.map((item, i) =>
      (i + 1) + '. ' + item.text +
      (item.assignee ? ' [Assignee: ' + item.assignee + ']' : '') +
      (item.deadline ? ' [Due: ' + item.deadline + ']' : '')
    ).join('\n');

    const result = brain.insertMemory({
      source: 'listening',
      source_id: 'recording:' + recordingId + ':action_items',
      memory_type: 'action_item',
      subject: 'Action Items: ' + title,
      content: itemsText,
      importance: 0.85,
      timestamp: ts
    });
    if (result) ids.push(result.id);
  }

  // Email draft
  if (outputs.emailDraft && outputs.emailDraft.body) {
    const result = brain.insertMemory({
      source: 'listening',
      source_id: 'recording:' + recordingId + ':email_draft',
      memory_type: 'email_draft',
      subject: 'Email Draft: ' + (outputs.emailDraft.subject || title),
      content: 'Subject: ' + (outputs.emailDraft.subject || '') + '\n\n' + outputs.emailDraft.body,
      importance: 0.6,
      timestamp: ts
    });
    if (result) ids.push(result.id);
  }

  return ids;
}

/**
 * Get stored meeting intelligence outputs for a recording.
 * @param {string} recordingId
 * @returns {{notes: object|null, actionItems: object|null, emailDraft: object|null}}
 */
function getOutputs(recordingId) {
  _validateId(recordingId);
  const db = brain.getDb();
  const notes = db.prepare(
    "SELECT * FROM memories WHERE source = 'listening' AND source_id = ?"
  ).get('recording:' + recordingId + ':meeting_notes') || null;
  const actionItems = db.prepare(
    "SELECT * FROM memories WHERE source = 'listening' AND source_id = ?"
  ).get('recording:' + recordingId + ':action_items') || null;
  const emailDraft = db.prepare(
    "SELECT * FROM memories WHERE source = 'listening' AND source_id = ?"
  ).get('recording:' + recordingId + ':email_draft') || null;

  return { notes, actionItems, emailDraft };
}

// ── Helpers ──────────────────────────────────────────────────────────

function _getTranscriptData(recordingId) {
  const db = brain.getDb();
  const transcript = db.prepare(
    'SELECT * FROM transcripts WHERE recording_id = ? AND status = ? ORDER BY created_at DESC LIMIT 1'
  ).get(recordingId, 'done');
  if (!transcript) throw new Error('No completed transcript for recording: ' + recordingId);

  const segments = db.prepare(
    'SELECT * FROM transcript_segments WHERE transcript_id = ? ORDER BY seq_order'
  ).all(transcript.id);

  return { transcript, segments };
}

function _formatTranscript(transcript, segments) {
  if (segments.length > 0) {
    return segments.map(seg => {
      const time = _fmtMs(seg.start_ms);
      const label = seg.speaker_label || 'Speaker';
      return '[' + time + '] ' + label + ': ' + seg.text;
    }).join('\n');
  }
  return transcript.full_text || '';
}

function _fmtMs(ms) {
  const sec = Math.floor(ms / 1000);
  const min = Math.floor(sec / 60);
  return min + ':' + String(sec % 60).padStart(2, '0');
}

function _getProvider() {
  const { getDefaultClient } = require('../llm/client');
  return getDefaultClient();
}

function _getModel() {
  const { getDefaultModel } = require('../llm/client');
  return getDefaultModel();
}

module.exports = {
  generateMeetingNotes,
  extractActionItems,
  generateEmailDraft,
  processRecording,
  storeAsMemories,
  getOutputs
};
