/**
 * TextMe Channel Plugin - Configuration
 * 
 * Multi-account configuration handling for TextMe messaging integration.
 */

import * as fs from 'fs/promises';
import * as path from 'path';
import { existsSync } from 'fs';

// ============================================================================
// Types
// ============================================================================

export interface AccountCredentials {
  email: string;
  password: string;
}

export interface StoredTokens {
  accessToken: string;
  refreshToken: string;
  expiresAt?: number;
  userId?: number;
}

export interface AccountConfig {
  /** Unique identifier for this account */
  id: string;
  /** Display name for the account */
  name: string;
  /** Login credentials (email/password) */
  credentials: AccountCredentials;
  /** Phone numbers associated with this account */
  phoneNumbers: string[];
  /** Whether this account is enabled */
  enabled: boolean;
  /** Whether this is the default account for outbound */
  isDefault?: boolean;
}

export interface PluginConfig {
  /** List of TextMe accounts */
  accounts: AccountConfig[];
  /** Clawdbot gateway URL for forwarding messages */
  clawdbotGatewayUrl: string;
  /** Clawdbot gateway token */
  clawdbotGatewayToken?: string;
  /** Webhook port for receiving Clawdbot outbound requests */
  webhookPort: number;
  /** Webhook path prefix */
  webhookPath: string;
  /** Enable debug logging */
  debug: boolean;
  /** Path to token storage file */
  tokenStoragePath: string;
  /** Auto-reconnect on disconnect */
  autoReconnect: boolean;
  /** Max reconnection attempts */
  maxReconnectAttempts: number;
  /** Reconnect delay (ms) */
  reconnectDelay: number;
}

export interface TokenStorage {
  accounts: Record<string, StoredTokens>;
  lastUpdated: string;
}

// ============================================================================
// Defaults
// ============================================================================

const DEFAULT_CONFIG: Partial<PluginConfig> = {
  clawdbotGatewayUrl: 'http://localhost:3000',
  webhookPort: 3100,
  webhookPath: '/textme',
  debug: false,
  tokenStoragePath: '.textme-tokens.json',
  autoReconnect: true,
  maxReconnectAttempts: 10,
  reconnectDelay: 5000,
};

// ============================================================================
// Configuration Manager
// ============================================================================

export class ConfigManager {
  private config: PluginConfig;
  private configPath: string;
  private tokenStorage: TokenStorage = { accounts: {}, lastUpdated: '' };

  constructor(configPath?: string) {
    this.configPath = configPath || path.join(process.cwd(), 'textme-channel.json');
    this.config = DEFAULT_CONFIG as PluginConfig;
  }

  /**
   * Load configuration from file or environment
   */
  async load(): Promise<PluginConfig> {
    // Try loading from file first
    if (existsSync(this.configPath)) {
      try {
        const content = await fs.readFile(this.configPath, 'utf-8');
        const fileConfig = JSON.parse(content) as Partial<PluginConfig>;
        this.config = { ...DEFAULT_CONFIG, ...fileConfig } as PluginConfig;
      } catch (err) {
        console.error(`[TextMeConfig] Failed to load config from ${this.configPath}:`, err);
      }
    }

    // Override with environment variables
    this.loadFromEnv();

    // Validate required fields
    this.validate();

    // Load token storage
    await this.loadTokenStorage();

    return this.config;
  }

  /**
   * Load configuration from environment variables
   */
  private loadFromEnv(): void {
    // Single account from env (for simple setups)
    const email = process.env.TEXTME_EMAIL;
    const password = process.env.TEXTME_PASSWORD;
    const phoneNumbers = process.env.TEXTME_PHONE_NUMBERS?.split(',').map(p => p.trim());

    if (email && password) {
      const envAccount: AccountConfig = {
        id: 'env-account',
        name: 'Environment Account',
        credentials: { email, password },
        phoneNumbers: phoneNumbers || [],
        enabled: true,
        isDefault: true,
      };

      // Add to accounts or replace if exists
      if (!this.config.accounts) {
        this.config.accounts = [];
      }
      const existingIdx = this.config.accounts.findIndex(a => a.id === 'env-account');
      if (existingIdx >= 0) {
        this.config.accounts[existingIdx] = envAccount;
      } else {
        this.config.accounts.unshift(envAccount);
      }
    }

    // Other env overrides
    if (process.env.CLAWDBOT_GATEWAY_URL) {
      this.config.clawdbotGatewayUrl = process.env.CLAWDBOT_GATEWAY_URL;
    }
    if (process.env.CLAWDBOT_GATEWAY_TOKEN) {
      this.config.clawdbotGatewayToken = process.env.CLAWDBOT_GATEWAY_TOKEN;
    }
    if (process.env.TEXTME_WEBHOOK_PORT) {
      this.config.webhookPort = parseInt(process.env.TEXTME_WEBHOOK_PORT, 10);
    }
    if (process.env.TEXTME_WEBHOOK_PATH) {
      this.config.webhookPath = process.env.TEXTME_WEBHOOK_PATH;
    }
    if (process.env.TEXTME_DEBUG === 'true') {
      this.config.debug = true;
    }
    if (process.env.TEXTME_TOKEN_STORAGE_PATH) {
      this.config.tokenStoragePath = process.env.TEXTME_TOKEN_STORAGE_PATH;
    }
  }

  /**
   * Validate configuration
   */
  private validate(): void {
    if (!this.config.accounts || this.config.accounts.length === 0) {
      throw new Error('[TextMeConfig] No accounts configured. Set TEXTME_EMAIL and TEXTME_PASSWORD or provide config file.');
    }

    for (const account of this.config.accounts) {
      if (!account.id) {
        throw new Error('[TextMeConfig] Account missing id');
      }
      if (!account.credentials?.email || !account.credentials?.password) {
        throw new Error(`[TextMeConfig] Account ${account.id} missing email or password`);
      }
    }
  }

  /**
   * Get the current configuration
   */
  getConfig(): PluginConfig {
    return this.config;
  }

  /**
   * Get enabled accounts
   */
  getEnabledAccounts(): AccountConfig[] {
    return this.config.accounts.filter(a => a.enabled !== false);
  }

  /**
   * Get default account for outbound messages
   */
  getDefaultAccount(): AccountConfig | undefined {
    return this.config.accounts.find(a => a.isDefault && a.enabled !== false)
      || this.config.accounts.find(a => a.enabled !== false);
  }

  /**
   * Get account by phone number
   */
  getAccountByPhoneNumber(phoneNumber: string): AccountConfig | undefined {
    const normalized = this.normalizePhoneNumber(phoneNumber);
    return this.config.accounts.find(a => 
      a.enabled !== false && 
      a.phoneNumbers.some(p => this.normalizePhoneNumber(p) === normalized)
    );
  }

  /**
   * Get account by ID
   */
  getAccountById(id: string): AccountConfig | undefined {
    return this.config.accounts.find(a => a.id === id);
  }

  /**
   * Normalize phone number for comparison
   */
  private normalizePhoneNumber(phone: string): string {
    return phone.replace(/[^\d+]/g, '');
  }

  // ==========================================================================
  // Token Storage
  // ==========================================================================

  /**
   * Load token storage from file
   */
  async loadTokenStorage(): Promise<TokenStorage> {
    const storagePath = path.resolve(this.config.tokenStoragePath);
    
    if (existsSync(storagePath)) {
      try {
        const content = await fs.readFile(storagePath, 'utf-8');
        this.tokenStorage = JSON.parse(content);
      } catch (err) {
        console.error('[TextMeConfig] Failed to load token storage:', err);
        this.tokenStorage = { accounts: {}, lastUpdated: '' };
      }
    }

    return this.tokenStorage;
  }

  /**
   * Save token storage to file
   */
  async saveTokenStorage(): Promise<void> {
    const storagePath = path.resolve(this.config.tokenStoragePath);
    this.tokenStorage.lastUpdated = new Date().toISOString();

    try {
      await fs.writeFile(storagePath, JSON.stringify(this.tokenStorage, null, 2), 'utf-8');
      // Set restrictive permissions (owner read/write only)
      await fs.chmod(storagePath, 0o600);
    } catch (err) {
      console.error('[TextMeConfig] Failed to save token storage:', err);
      throw err;
    }
  }

  /**
   * Get stored tokens for an account
   */
  getStoredTokens(accountId: string): StoredTokens | undefined {
    return this.tokenStorage.accounts[accountId];
  }

  /**
   * Store tokens for an account
   */
  async storeTokens(accountId: string, tokens: StoredTokens): Promise<void> {
    this.tokenStorage.accounts[accountId] = tokens;
    await this.saveTokenStorage();
  }

  /**
   * Remove stored tokens for an account
   */
  async removeTokens(accountId: string): Promise<void> {
    delete this.tokenStorage.accounts[accountId];
    await this.saveTokenStorage();
  }

  /**
   * Check if tokens are expired (with buffer)
   */
  isTokenExpired(accountId: string, bufferMs: number = 5 * 60 * 1000): boolean {
    const tokens = this.tokenStorage.accounts[accountId];
    if (!tokens?.expiresAt) {
      return true;
    }
    return Date.now() >= (tokens.expiresAt - bufferMs);
  }
}

// ============================================================================
// Singleton Export
// ============================================================================

let configManagerInstance: ConfigManager | null = null;

export function getConfigManager(configPath?: string): ConfigManager {
  if (!configManagerInstance) {
    configManagerInstance = new ConfigManager(configPath);
  }
  return configManagerInstance;
}

export function resetConfigManager(): void {
  configManagerInstance = null;
}
