import dotenv from 'dotenv';
dotenv.config({ path: __dirname + '/../../.env' });

import * as fs from 'fs';
import * as path from 'path';
import pool from '../db';
import { parseSessionsIndex, parseJSONLFile } from './parser';
import {
  upsertContact,
  upsertSession,
  importMessages,
  getSyncState,
  updateSyncState,
  importUserMemory,
  linkScUsers,
} from './importer';

const AGENTS_DIR = '/home/administrator/.openclaw/agents';
const AGENTS = ['main', 'sportsclaw', 'cryptoclaw'];

async function syncAgent(agentName: string): Promise<{ sessions: number; messages: number; contacts: number }> {
  const agentDir = path.join(AGENTS_DIR, agentName);
  const sessionsDir = path.join(agentDir, 'sessions');
  const indexFile = path.join(sessionsDir, 'sessions.json');

  if (!fs.existsSync(indexFile)) {
    console.log(`  No sessions.json for ${agentName}, skipping`);
    return { sessions: 0, messages: 0, contacts: 0 };
  }

  const sessionIndex = parseSessionsIndex(indexFile);
  let totalMessages = 0;
  let totalSessions = 0;
  let totalContacts = 0;

  for (const [sessionKey, sessionInfo] of sessionIndex) {
    const sourceFile = sessionInfo.sessionFile;
    if (!sourceFile || !fs.existsSync(sourceFile)) {
      console.log(`  Skipping missing file: ${sourceFile}`);
      continue;
    }

    // Skip deleted files
    if (sourceFile.includes('.deleted.')) continue;

    console.log(`  Processing ${path.basename(sourceFile)}...`);

    // Parse origin to find contact
    const origin = sessionInfo.origin;
    let contactId: string | null = null;
    let groupId: bigint | null = null;
    let groupName: string | null = null;

    // Extract IDs from origin
    const label = origin.label || '';
    const idMatch = label.match(/id:(-?\d+)/);
    const usernameMatch = label.match(/@(\w+)/);
    const fromMatch = origin.from?.match(/telegram:(\d+)/);

    if (sessionInfo.chatType === 'group' && idMatch) {
      groupId = BigInt(idMatch[1]);
      groupName = label.replace(/\s*id:-?\d+/, '').trim() || null;
    }

    // Try to get a contact from the origin
    if (sessionInfo.chatType === 'direct') {
      let telegramId: bigint | null = null;
      let username: string | null = null;
      let displayName: string | null = null;

      if (fromMatch) {
        telegramId = BigInt(fromMatch[1]);
      } else if (idMatch) {
        const id = BigInt(idMatch[1]);
        if (id > 0) telegramId = id;
      }

      if (usernameMatch) username = usernameMatch[1];
      const nameMatch = label.match(/^([^(@]+)/);
      if (nameMatch) displayName = nameMatch[1].trim().replace(/\s*id:\d+/, '').trim() || null;

      if (telegramId) {
        try {
          contactId = await upsertContact(
            telegramId, username, displayName,
            new Date(sessionInfo.updatedAt)
          );
          totalContacts++;
        } catch (err: any) {
          console.error(`  Failed to upsert contact for ${telegramId}:`, err.message);
        }
      }
    }

    // Check sync state
    const syncState = await getSyncState(sourceFile);
    const startByte = syncState?.lastByte || 0;

    // Parse JSONL from last position
    const { messages, header, bytesRead, linesRead } = await parseJSONLFile(sourceFile, startByte);

    if (messages.length === 0 && startByte > 0) {
      console.log(`  No new messages in ${path.basename(sourceFile)}`);
      continue;
    }

    // Determine session timestamps
    const timestamps = messages
      .filter(m => m.timestamp)
      .map(m => new Date(m.timestamp).getTime());
    const headerTs = header ? new Date(header.timestamp).getTime() : Date.now();
    const startedAt = new Date(timestamps.length > 0 ? Math.min(...timestamps, headerTs) : headerTs);
    const lastActivity = new Date(timestamps.length > 0 ? Math.max(...timestamps) : sessionInfo.updatedAt);

    // Count only user/assistant text messages for message_count
    const textMessages = messages.filter(m => m.contentType === 'text');

    // Upsert session
    try {
      await upsertSession(
        sessionInfo.sessionId,
        agentName,
        contactId,
        sessionInfo,
        sourceFile,
        textMessages.length + (syncState?.lastLine || 0),
        startedAt,
        lastActivity,
        groupId,
        groupName
      );
      totalSessions++;
    } catch (err: any) {
      console.error(`  Failed to upsert session ${sessionInfo.sessionId}:`, err.message);
      continue;
    }

    // Import messages
    const imported = await importMessages(messages, sessionInfo.sessionId, contactId);
    totalMessages += imported;

    // Update sync state
    await updateSyncState(
      sourceFile,
      agentName,
      (syncState?.lastLine || 0) + linesRead,
      bytesRead
    );

    console.log(`  Imported ${imported} messages (${linesRead} lines) from ${path.basename(sourceFile)}`);
  }

  // Import user memory files
  const memoryDir = path.join(agentDir, 'workspace', 'memory', 'users');
  await importUserMemory(agentName, memoryDir);

  return { sessions: totalSessions, messages: totalMessages, contacts: totalContacts };
}

async function runSync(): Promise<void> {
  console.log(`\n[${new Date().toISOString()}] Starting CRM sync...`);
  const startTime = Date.now();

  for (const agent of AGENTS) {
    console.log(`\nSyncing agent: ${agent}`);
    try {
      const stats = await syncAgent(agent);
      console.log(`  Done: ${stats.sessions} sessions, ${stats.messages} messages, ${stats.contacts} contacts`);
    } catch (err: any) {
      console.error(`  Error syncing ${agent}:`, err.message);
    }
  }

  // Link sc_users
  try {
    const linked = await linkScUsers();
    if (linked > 0) console.log(`\nLinked ${linked} contacts to sc_users`);
  } catch (err: any) {
    console.error('Failed to link sc_users:', err.message);
  }

  const elapsed = ((Date.now() - startTime) / 1000).toFixed(1);
  console.log(`\nSync completed in ${elapsed}s`);
}

// Main: run once then every 5 minutes
async function main() {
  console.log('CRM Sync Worker starting...');

  // Run immediately
  await runSync();

  // Then every 5 minutes
  setInterval(async () => {
    try {
      await runSync();
    } catch (err) {
      console.error('Sync cycle failed:', err);
    }
  }, 5 * 60 * 1000);
}

main().catch((err) => {
  console.error('Worker fatal error:', err);
  process.exit(1);
});
