#!/usr/bin/env node
/**
 * TextMe CLI
 * Command-line interface for TextMe messaging service
 */

import { Command } from 'commander';
import { homedir } from 'os';
import { mkdir, readFile, writeFile } from 'fs/promises';
import { existsSync } from 'fs';
import { join } from 'path';
import qrcode from 'qrcode-terminal';

import { TextMeAuth, TextMeAPI, TextMeRealtime, createTextMeClient } from '../src/index.js';
import type { Conversation, Message, UserInfo, IncomingMessage } from '../src/index.js';

// ============================================================================
// Credentials Management
// ============================================================================

interface StoredCredentials {
  token: string;
  expiresAt?: number;
  savedAt: number;
}

const CREDENTIALS_DIR = join(homedir(), '.textme');
const CREDENTIALS_FILE = join(CREDENTIALS_DIR, 'credentials.json');
const CAPTURED_TOKEN_FILE = join(CREDENTIALS_DIR, 'captured-token.json');

async function ensureCredentialsDir(): Promise<void> {
  if (!existsSync(CREDENTIALS_DIR)) {
    await mkdir(CREDENTIALS_DIR, { recursive: true, mode: 0o700 });
  }
}

async function saveCredentials(token: string, expiresAt?: number): Promise<void> {
  await ensureCredentialsDir();
  const credentials: StoredCredentials = {
    token,
    expiresAt,
    savedAt: Date.now(),
  };
  await writeFile(CREDENTIALS_FILE, JSON.stringify(credentials, null, 2), { mode: 0o600 });
}

async function loadCredentials(): Promise<StoredCredentials | null> {
  // First try the main credentials file
  try {
    const data = await readFile(CREDENTIALS_FILE, 'utf-8');
    return JSON.parse(data) as StoredCredentials;
  } catch {
    // Fall through to check captured token
  }

  // Check for captured token from mitmproxy
  try {
    const data = await readFile(CAPTURED_TOKEN_FILE, 'utf-8');
    const captured = JSON.parse(data) as { token: string; captured_at?: string };
    if (captured.token) {
      console.log('📱 Using token captured from mobile app');
      return {
        token: captured.token,
        savedAt: Date.now(),
      };
    }
  } catch {
    // No captured token either
  }

  return null;
}

async function importCapturedToken(): Promise<boolean> {
  try {
    const data = await readFile(CAPTURED_TOKEN_FILE, 'utf-8');
    const captured = JSON.parse(data) as { token: string; captured_at?: string };
    if (captured.token) {
      await saveCredentials(captured.token);
      return true;
    }
  } catch {
    return false;
  }
  return false;
}

async function getAuthenticatedClient(): Promise<TextMeAPI> {
  const credentials = await loadCredentials();
  if (!credentials) {
    console.error('❌ Not authenticated. Run `textme auth` first.');
    process.exit(1);
  }

  // Check if token might be expired
  if (credentials.expiresAt && Date.now() > credentials.expiresAt) {
    console.warn('⚠️  Token may be expired. Run `textme auth` to re-authenticate.');
  }

  return createTextMeClient(credentials.token);
}

async function getToken(): Promise<string> {
  const credentials = await loadCredentials();
  if (!credentials) {
    console.error('❌ Not authenticated. Run `textme auth` first.');
    process.exit(1);
  }
  return credentials.token;
}

// ============================================================================
// CLI Helpers
// ============================================================================

function formatTimestamp(dateStr: string): string {
  const date = new Date(dateStr);
  const now = new Date();
  const isToday = date.toDateString() === now.toDateString();
  
  if (isToday) {
    return date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
  }
  return date.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }) + ' ' + 
         date.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
}

function truncate(text: string, maxLength: number): string {
  if (text.length <= maxLength) return text;
  return text.slice(0, maxLength - 1) + '…';
}

function getConversationName(conv: Conversation): string {
  if (conv.name) return conv.name;
  if (conv.participants.length === 0) return 'Unknown';
  if (conv.participants.length === 1) {
    return conv.participants[0].name || conv.participants[0].phone_number;
  }
  return conv.participants.map(p => p.name || p.phone_number).join(', ');
}

// ============================================================================
// Commands
// ============================================================================

const program = new Command();

program
  .name('textme')
  .description('TextMe CLI - Send and receive messages from the terminal')
  .version('0.1.0');

// Auth command
program
  .command('auth')
  .description('Authenticate via QR code scan')
  .action(async () => {
    console.log('🔐 TextMe Authentication\n');
    
    // Check if there's already a captured token
    const existing = await loadCredentials();
    if (existing) {
      console.log('ℹ️  Found existing token. Use `textme whoami` to verify or continue to re-authenticate.\n');
    }
    
    const auth = new TextMeAuth();
    const sessionId = auth.generateQRSession();
    
    // Generate QR code URL for mobile app to scan
    const qrData = `textme://auth/${sessionId}`;
    
    console.log('Scan this QR code with the TextMe mobile app:\n');
    qrcode.generate(qrData, { small: true });
    
    console.log('\nWaiting for scan... (timeout: 2 minutes)');
    console.log('⚠️  If this fails, use `textme import` with mitmproxy capture instead.\n');
    
    try {
      const token = await auth.waitForQRAuth(sessionId);
      
      // Get token expiry (default 24h from now if not available)
      const expiresAt = Date.now() + (24 * 60 * 60 * 1000);
      
      await saveCredentials(token, expiresAt);
      
      console.log('✅ Authentication successful!');
      console.log(`   Token saved to ${CREDENTIALS_FILE}`);
      
      // Try to get user info to confirm
      try {
        const client = createTextMeClient(token);
        const user = await client.getUserInfo();
        console.log(`\n👤 Logged in as: ${user.first_name || user.username || user.email}`);
        if (user.phone_number) {
          console.log(`   Phone: ${user.phone_number}`);
        }
      } catch {
        // Ignore - user info is optional
      }
    } catch (err) {
      if (err instanceof Error) {
        if (err.message.includes('timeout')) {
          console.error('\n⏰ QR scan timed out. Please try again.');
        } else if (err.message.includes('expired')) {
          console.error('\n⏰ Session expired. Please try again.');
        } else {
          console.error(`\n❌ Authentication failed: ${err.message}`);
        }
      } else {
        console.error('\n❌ Authentication failed');
      }
      process.exit(1);
    }
  });

// Import command - import captured token from mitmproxy
program
  .command('import')
  .description('Import token captured from mobile app via mitmproxy')
  .option('-t, --token <token>', 'Manually provide a JWT token')
  .action(async (options) => {
    console.log('📱 Token Import\n');
    
    if (options.token) {
      // Manual token provided
      await saveCredentials(options.token);
      console.log('✅ Token saved!');
      console.log('   Run `textme whoami` to verify.\n');
      return;
    }
    
    // Try to import from captured-token.json
    const imported = await importCapturedToken();
    
    if (imported) {
      console.log('✅ Imported token from captured-token.json');
      console.log(`   Saved to ${CREDENTIALS_FILE}`);
      console.log('\n   Run `textme whoami` to verify.\n');
    } else {
      console.log('❌ No captured token found.');
      console.log('\nTo capture a token from the mobile app:');
      console.log('  1. Run: mitmproxy -s scripts/capture-token.py');
      console.log('  2. Configure your phone to use the proxy');
      console.log('  3. Open TextMe app on your phone');
      console.log('  4. Token will be saved to ~/.textme/captured-token.json');
      console.log('\nOr manually provide a token:');
      console.log('  textme import --token YOUR_JWT_TOKEN\n');
      process.exit(1);
    }
  });

// Whoami command
program
  .command('whoami')
  .description('Display current user information')
  .action(async () => {
    try {
      const client = await getAuthenticatedClient();
      const user: UserInfo = await client.getUserInfo();
      
      console.log('\n👤 User Information\n');
      console.log(`   ID:       ${user.id}`);
      console.log(`   Email:    ${user.email}`);
      if (user.username) console.log(`   Username: ${user.username}`);
      if (user.first_name || user.last_name) {
        console.log(`   Name:     ${[user.first_name, user.last_name].filter(Boolean).join(' ')}`);
      }
      if (user.phone_number) console.log(`   Phone:    ${user.phone_number}`);
      console.log(`   Verified: ${user.is_verified ? '✓' : '✗'}`);
      console.log(`   Created:  ${formatTimestamp(user.created_at)}`);
      console.log();
    } catch (err) {
      if (err instanceof Error) {
        console.error(`❌ Failed to get user info: ${err.message}`);
      } else {
        console.error('❌ Failed to get user info');
      }
      process.exit(1);
    }
  });

// List command
program
  .command('list')
  .description('List conversations')
  .option('-a, --archived', 'Show archived conversations')
  .option('-l, --limit <n>', 'Number of conversations to show', '20')
  .action(async (options) => {
    try {
      const client = await getAuthenticatedClient();
      const response = await client.listConversations({
        limit: parseInt(options.limit, 10),
        archived: options.archived,
      });
      
      if (response.conversations.length === 0) {
        console.log('\n📭 No conversations found.\n');
        return;
      }
      
      console.log('\n📬 Conversations\n');
      console.log('   ID       Unread  Name / Participants');
      console.log('   ──────   ──────  ────────────────────────────────────────');
      
      for (const conv of response.conversations) {
        const name = getConversationName(conv);
        const unreadBadge = conv.unread_count > 0 ? `(${conv.unread_count})`.padEnd(6) : '      ';
        const archiveBadge = conv.is_archived ? ' [archived]' : '';
        const muteBadge = conv.is_muted ? ' [muted]' : '';
        
        console.log(`   ${String(conv.id).padEnd(7)} ${unreadBadge} ${truncate(name, 40)}${archiveBadge}${muteBadge}`);
        
        if (conv.last_message) {
          const preview = truncate(conv.last_message.body, 50).replace(/\n/g, ' ');
          const time = formatTimestamp(conv.last_message.created_at);
          console.log(`                     └─ ${preview}  (${time})`);
        }
      }
      
      console.log(`\n   Showing ${response.conversations.length} of ${response.count} conversations\n`);
    } catch (err) {
      if (err instanceof Error) {
        console.error(`❌ Failed to list conversations: ${err.message}`);
      } else {
        console.error('❌ Failed to list conversations');
      }
      process.exit(1);
    }
  });

// Read command
program
  .command('read <conversation-id>')
  .description('Read messages from a conversation')
  .option('-n, --limit <n>', 'Number of messages to show', '20')
  .action(async (conversationId: string, options) => {
    try {
      const client = await getAuthenticatedClient();
      const convId = parseInt(conversationId, 10);
      
      if (isNaN(convId)) {
        console.error('❌ Invalid conversation ID. Must be a number.');
        process.exit(1);
      }
      
      // Get conversation details
      const conv = await client.getConversation(convId);
      const messages = await client.getMessages(convId, {
        limit: parseInt(options.limit, 10),
      });
      
      console.log(`\n💬 ${getConversationName(conv)}\n`);
      
      if (messages.messages.length === 0) {
        console.log('   No messages yet.\n');
        return;
      }
      
      // Display messages in chronological order (oldest first)
      const sortedMessages = [...messages.messages].reverse();
      
      for (const msg of sortedMessages) {
        const sender = conv.participants.find(p => p.id === msg.sender_id);
        const senderName = sender?.name || sender?.phone_number || 'You';
        const time = formatTimestamp(msg.created_at);
        const statusIcon = getStatusIcon(msg.status);
        
        console.log(`   [${time}] ${senderName} ${statusIcon}`);
        
        // Handle multi-line messages
        const lines = msg.body.split('\n');
        for (const line of lines) {
          console.log(`   ${line}`);
        }
        
        // Show attachments
        if (msg.attachments && msg.attachments.length > 0) {
          for (const att of msg.attachments) {
            console.log(`   📎 ${att.filename} (${att.content_type})`);
          }
        }
        
        console.log();
      }
      
      console.log(`   Showing ${messages.messages.length} messages\n`);
    } catch (err) {
      if (err instanceof Error) {
        console.error(`❌ Failed to read messages: ${err.message}`);
      } else {
        console.error('❌ Failed to read messages');
      }
      process.exit(1);
    }
  });

function getStatusIcon(status: Message['status']): string {
  switch (status) {
    case 'pending': return '⏳';
    case 'sent': return '✓';
    case 'delivered': return '✓✓';
    case 'read': return '✓✓';
    case 'failed': return '❌';
    default: return '';
  }
}

// Send command
program
  .command('send <conversation-id> <message>')
  .description('Send a message to a conversation')
  .action(async (conversationId: string, message: string) => {
    try {
      const client = await getAuthenticatedClient();
      const convId = parseInt(conversationId, 10);
      
      if (isNaN(convId)) {
        console.error('❌ Invalid conversation ID. Must be a number.');
        process.exit(1);
      }
      
      const response = await client.sendMessage(convId, {
        body: message,
        type: 'text',
      });
      
      console.log(`\n✅ Message sent!`);
      console.log(`   ID: ${response.message.id}`);
      console.log(`   Status: ${response.message.status}`);
      console.log();
    } catch (err) {
      if (err instanceof Error) {
        console.error(`❌ Failed to send message: ${err.message}`);
      } else {
        console.error('❌ Failed to send message');
      }
      process.exit(1);
    }
  });

// Watch command
program
  .command('watch')
  .description('Watch for incoming messages in real-time')
  .option('-c, --conversation <ids>', 'Comma-separated conversation IDs to watch')
  .action(async (options) => {
    try {
      const token = await getToken();
      const realtime = new TextMeRealtime();
      
      console.log('\n👁️  Watching for messages... (Ctrl+C to stop)\n');
      
      // Set up event handlers
      realtime.on('connected', () => {
        console.log('🟢 Connected to TextMe realtime\n');
      });
      
      realtime.on('disconnected', ({ reason }) => {
        console.log(`🔴 Disconnected: ${reason}`);
      });
      
      realtime.on('message', (msg: IncomingMessage) => {
        const time = msg.timestamp.toLocaleTimeString('en-US', { 
          hour: 'numeric', 
          minute: '2-digit',
          second: '2-digit'
        });
        
        console.log(`[${time}] 💬 ${msg.senderName || msg.senderId}`);
        console.log(`   Conversation: ${msg.conversationId}`);
        console.log(`   ${msg.text}`);
        
        if (msg.mediaUrl) {
          console.log(`   📎 ${msg.mediaType}: ${msg.mediaUrl}`);
        }
        console.log();
      });
      
      realtime.on('typing', (event) => {
        const indicator = event.isTyping ? '✏️  typing...' : '   stopped typing';
        console.log(`   [${event.conversationId}] ${event.userId} ${indicator}`);
      });
      
      realtime.on('call', (event) => {
        const callType = event.type === 'incoming' ? '📞 Incoming call' :
                        event.type === 'ended' ? '📴 Call ended' :
                        event.type === 'missed' ? '📵 Missed call' : '📱 Call';
        console.log(`   ${callType} from ${event.callerName || event.callerId}`);
        if (event.duration) {
          console.log(`   Duration: ${event.duration}s`);
        }
        console.log();
      });
      
      realtime.on('error', (err) => {
        console.error(`❌ Error: ${err.message}`);
      });
      
      // Connect
      await realtime.connect(token);
      
      // Subscribe to specific conversations if provided
      if (options.conversation) {
        const ids = options.conversation.split(',').map((s: string) => s.trim());
        for (const id of ids) {
          realtime.subscribe(id);
          console.log(`   Subscribed to conversation ${id}`);
        }
        console.log();
      }
      
      // Handle graceful shutdown
      const shutdown = () => {
        console.log('\n\n👋 Disconnecting...');
        realtime.disconnect();
        process.exit(0);
      };
      
      process.on('SIGINT', shutdown);
      process.on('SIGTERM', shutdown);
      
      // Keep process alive
      await new Promise(() => {});
    } catch (err) {
      if (err instanceof Error) {
        console.error(`❌ Failed to connect: ${err.message}`);
      } else {
        console.error('❌ Failed to connect');
      }
      process.exit(1);
    }
  });

// Parse and run
program.parse();
