'use strict';
const brain = require('../brain');
const slackMcp = require('../tools/slack-mcp');

/**
 * Slack pull using WALL-E's own OAuth connection.
 *
 * Two modes:
 * - pullNewSlackMessages(): Incremental — only after latest timestamp
 * - pullAllSlackMessages(): Full backfill — month by month from 2022 to now
 *
 * Dedup: source_id = "slack-{timestamp}-{sender_prefix}" — brain.insertMemory skips dupes.
 */

const PAUSE_MS = 2000; // Rate limit between API calls

function ensureDb() {
  try { brain.getDb(); } catch { brain.initDb(); }
  return brain.getDb();
}

// ── Incremental: only new messages ──

async function pullNewSlackMessages({ onProgress } = {}) {
  const log = onProgress || ((msg) => console.log('[slack-pull]', msg));
  if (!(await slackMcp.isAuthenticated())) throw new Error('Slack not connected.');

  const db = ensureDb();
  const latest = db.prepare("SELECT max(timestamp) as ts FROM memories WHERE source = 'slack'").get();
  const rawTs = latest?.ts || '';
  const latestDate = /^\d{4}-\d{2}-\d{2}/.test(rawTs) ? rawTs.slice(0, 10) : '2020-01-01';
  const totalBefore = db.prepare("SELECT count(*) as c FROM memories WHERE source = 'slack'").get().c;

  log(`Incremental pull from ${latestDate}. Currently have ${totalBefore} messages.`);

  const queries = [`from:me after:${latestDate}`, `to:me after:${latestDate}`];
  const stats = { new_messages: 0, skipped: 0 };

  for (const query of queries) {
    const result = await searchAndIngest(query, log, db, stats);
    if (result.authError) throw new Error('Slack auth expired');
  }

  const totalAfter = db.prepare("SELECT count(*) as c FROM memories WHERE source = 'slack'").get().c;
  log(`Done. New: ${stats.new_messages}, Skipped: ${stats.skipped}, Total: ${totalBefore} → ${totalAfter}`);
  return { ...stats, total_before: totalBefore, total_after: totalAfter };
}

// ── Full backfill: month by month ──

async function pullAllSlackMessages({ onProgress } = {}) {
  const log = onProgress || ((msg) => console.log('[slack-pull]', msg));
  if (!(await slackMcp.isAuthenticated())) throw new Error('Slack not connected.');

  const db = ensureDb();
  const totalBefore = db.prepare("SELECT count(*) as c FROM memories WHERE source = 'slack'").get().c;

  // Generate month ranges from 2022-01 to current month
  const months = generateMonthRanges(2022, 1);
  log(`Full backfill: ${months.length} months to scan. Currently have ${totalBefore} messages.`);

  // Check existing coverage to prioritize sparse months
  const coverage = db.prepare(
    "SELECT substr(timestamp,1,7) as month, count(*) as c FROM memories WHERE source='slack' GROUP BY month"
  ).all();
  const coverageMap = {};
  coverage.forEach(r => { coverageMap[r.month] = r.c; });

  const stats = { new_messages: 0, skipped: 0, months_scanned: 0 };

  for (const month of months) {
    const existing = coverageMap[month] || 0;
    const [year, mo] = month.split('-');
    const afterDate = `${year}-${mo}-01`;
    const nextMonth = mo === '12' ? `${parseInt(year) + 1}-01-01` : `${year}-${String(parseInt(mo) + 1).padStart(2, '0')}-01`;

    log(`[${month}] ${existing} existing messages — scanning...`);

    // Search both directions for this month
    const queries = [
      `from:me after:${afterDate} before:${nextMonth}`,
      `to:me after:${afterDate} before:${nextMonth}`,
    ];

    for (const query of queries) {
      const result = await searchAndIngest(query, log, db, stats);
      if (result.authError) throw new Error('Slack auth expired');
      await sleep(PAUSE_MS);
    }

    stats.months_scanned++;
    const nowCount = db.prepare(
      "SELECT count(*) as c FROM memories WHERE source='slack' AND substr(timestamp,1,7)=?"
    ).get(month).c;
    log(`[${month}] Now have ${nowCount} messages (+${nowCount - existing})`);
  }

  const totalAfter = db.prepare("SELECT count(*) as c FROM memories WHERE source = 'slack'").get().c;
  log(`Full backfill done. New: ${stats.new_messages}, Skipped: ${stats.skipped}, Total: ${totalBefore} → ${totalAfter}`);
  return { ...stats, total_before: totalBefore, total_after: totalAfter };
}

// ── Shared search + ingest logic ──

async function searchAndIngest(query, log, db, stats) {
  try {
    log(`  Searching: "${query}"`);
    const result = await slackMcp.callSlackMcp('slack_search_public_and_private', { query });
    const texts = (result?.content || []).filter(c => c.type === 'text').map(c => c.text);
    const combined = texts.join('\n');

    if (!combined || combined.length < 10) {
      log(`  No results`);
      return { ok: true };
    }

    const messages = parseSlackSearchResults(combined);
    log(`  Found ${messages.length} messages`);

    let newCount = 0;
    for (const msg of messages) {
      const sourceId = `slack-${msg.timestamp}-${(msg.sender || '').slice(0, 5)}`;
      const exists = db.prepare("SELECT 1 FROM memories WHERE source = 'slack' AND source_id = ?").get(sourceId);
      if (exists) { stats.skipped++; continue; }

      brain.insertMemory({
        source: 'slack',
        source_id: sourceId,
        memory_type: msg.isOwner ? 'message_sent' : 'message_received',
        direction: msg.isOwner ? 'outbound' : 'inbound',
        content: msg.content,
        source_channel: msg.channel,
        participants: msg.sender,
        timestamp: msg.timestamp,
        metadata: JSON.stringify({ channel: msg.channel, sender: msg.sender }),
      });
      newCount++;
      stats.new_messages++;
    }

    log(`  Ingested ${newCount} new, ${messages.length - newCount} dupes`);
    return { ok: true };
  } catch (err) {
    if (err.message.includes('401') || err.message.includes('expired') || err.message.includes('invalid_auth')) {
      return { authError: true };
    }
    log(`  Error: ${err.message}`);
    return { ok: false };
  }
}

// ── Helpers ──

function generateMonthRanges(startYear, startMonth) {
  const months = [];
  const now = new Date();
  const endYear = now.getFullYear();
  const endMonth = now.getMonth() + 1;
  let y = startYear, m = startMonth;
  while (y < endYear || (y === endYear && m <= endMonth)) {
    months.push(`${y}-${String(m).padStart(2, '0')}`);
    m++;
    if (m > 12) { m = 1; y++; }
  }
  return months;
}

function parseSlackSearchResults(text) {
  const messages = [];
  const lines = text.split('\n');
  let current = null;

  for (const line of lines) {
    // Pattern: "N. #channel-name - Sender: message" or "N. Group DM with ... - Sender: message"
    const numberedMatch = line.match(/^\d+\.\s+(?:#(\S+)|Group DM with (.+?)|DM)\s+-\s+(.+?):\s+(.+?)\s+(\d{4,}-\d{2}-\d{2})$/);
    if (numberedMatch) {
      if (current && current.content) messages.push(current);
      const channelName = numberedMatch[1] || _dmChannelName(numberedMatch[2]) || 'DM';
      const sender = numberedMatch[3].trim();
      current = {
        sender,
        channel: channelName,
        timestamp: parseTimestamp(numberedMatch[5]) || new Date().toISOString(),
        content: numberedMatch[4].trim(),
        isOwner: isOwnerMessage(sender),
      };
      continue;
    }

    // Pattern: "**Sender Name** in #channel (timestamp)"
    const headerMatch = line.match(/^\*\*(.+?)\*\*\s+(?:in\s+)?(?:#?(\S+))?\s*(?:\((.+?)\))?/);
    if (headerMatch) {
      if (current && current.content) messages.push(current);
      const sender = headerMatch[1].trim();
      current = {
        sender,
        channel: headerMatch[2] || 'unknown',
        timestamp: parseTimestamp(headerMatch[3]) || new Date().toISOString(),
        content: '',
        isOwner: isOwnerMessage(sender),
      };
      continue;
    }

    // Pattern: "From: Sender | Channel: #channel | Date: ..."
    const metaMatch = line.match(/From:\s*(.+?)\s*\|\s*(?:Channel|In):\s*#?(.+?)\s*\|\s*(?:Date|Time):\s*(.+)/i);
    if (metaMatch) {
      if (current && current.content) messages.push(current);
      const sender = metaMatch[1].trim();
      current = {
        sender,
        channel: metaMatch[2].trim(),
        timestamp: parseTimestamp(metaMatch[3]) || new Date().toISOString(),
        content: '',
        isOwner: isOwnerMessage(sender),
      };
      continue;
    }

    // Content line
    if (current && line.trim() && !line.startsWith('---')) {
      current.content += (current.content ? '\n' : '') + line.trim();
    }
  }
  if (current && current.content) messages.push(current);

  if (messages.length === 0 && text.trim().length > 20) {
    messages.push({
      sender: 'unknown', channel: 'unknown',
      timestamp: new Date().toISOString(),
      content: text.slice(0, 2000), isOwner: false,
    });
  }
  return messages;
}

// Convert "Owner Name, Alice Smith, Bob Jones" → "DM with Alice, Bob"
function _dmChannelName(participantStr) {
  if (!participantStr) return null;
  const ownerFirst = (process.env.WALLE_OWNER_NAME || '').split(' ')[0].toLowerCase();
  const others = participantStr.split(',')
    .map(p => p.trim())
    .filter(p => p && (!ownerFirst || !p.toLowerCase().startsWith(ownerFirst)))
    .map(p => p.split(' ')[0])
    .slice(0, 3);
  return others.length > 0 ? `DM with ${others.join(', ')}` : 'DM';
}

function isOwnerMessage(sender) {
  if (!sender) return false;
  const s = sender.toLowerCase();
  const ownerName = (process.env.WALLE_OWNER_NAME || '').toLowerCase();
  const ownerParts = ownerName.split(/\s+/).filter(Boolean);
  return ownerParts.some(p => p.length > 1 && s.includes(p)) || s === 'you' || s === 'me';
}

function parseTimestamp(ts) {
  if (!ts) return null;
  try {
    const d = new Date(ts);
    if (!isNaN(d.getTime())) return d.toISOString();
  } catch {}
  return null;
}

function sleep(ms) {
  return new Promise(r => setTimeout(r, ms));
}

module.exports = { pullNewSlackMessages, pullAllSlackMessages, parseSlackSearchResults };
