import { env } from '../config/env';
import { logger } from '../utils/logger';

// --- Types ---

export interface TermuxSmsResult {
  success: boolean;
  error?: string;
  timestamp?: string;
  phone?: string;
  message_length?: number;
  total_sent?: number;
}

export interface TermuxInboxMessage {
  number: string;
  body: string;
  date: string;
  type: string; // "inbox" | "sent"
  read: boolean;
  threadId?: number;
}

export interface TermuxContact {
  name: string;
  number: string;
}

export interface TermuxBatteryStatus {
  health: string;
  percentage: number;
  plugged: string;
  status: string;
  temperature: number;
}

export interface TermuxHealthResponse {
  status: string;
  uptime: number;
  messages_sent: number;
  device_ip?: string;
}

// --- Client ---

class TermuxClient {
  // DB-sourced config (overrides env vars when non-empty)
  private dbUrl = '';
  private dbKey = '';
  private dbEnabled: boolean | null = null;

  private get baseUrl(): string {
    return this.dbUrl || env.TERMUX_API_URL;
  }

  private get apiKey(): string {
    return this.dbKey || env.TERMUX_API_KEY;
  }

  get enabled(): boolean {
    const featureOn = this.dbEnabled === true || env.ENABLE_SMS === 'true';
    return featureOn && !!this.baseUrl && !!this.apiKey;
  }

  /** Whether the active config came from the database (vs env vars). */
  get configSource(): 'database' | 'env' {
    return (this.dbUrl || this.dbKey) ? 'database' : 'env';
  }

  /** Load config from DB settings (called at startup + after save-config). */
  async configureFromDb(): Promise<void> {
    try {
      const { siteSettingsService } = await import('../modules/settings/settings.service');
      const settings = await siteSettingsService.get();
      this.dbUrl = settings.smsTermuxApiUrl || '';
      this.dbKey = settings.smsTermuxApiKey || '';
      this.dbEnabled = settings.enableSms;
    } catch (err) {
      logger.warn(`Failed to load SMS config from DB: ${err instanceof Error ? err.message : err}`);
    }
  }

  /**
   * Make an authenticated request to the Termux API server.
   * Auth via X-API-Key header (matches Termux server's HMAC auth).
   */
  private async request<T>(
    method: string,
    path: string,
    body?: Record<string, unknown>,
    timeoutMs = 15000,
  ): Promise<T> {
    const url = `${this.baseUrl}${path}`;
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), timeoutMs);

    const headers: Record<string, string> = {
      'X-API-Key': this.apiKey,
    };

    let fetchBody: string | undefined;
    if (body) {
      headers['Content-Type'] = 'application/json';
      fetchBody = JSON.stringify(body);
    }

    try {
      const res = await fetch(url, {
        method,
        headers,
        body: fetchBody,
        signal: controller.signal,
      });

      if (!res.ok) {
        const text = await res.text().catch(() => '');
        throw new Error(`Termux API ${method} ${path} returned ${res.status}: ${text}`);
      }

      const contentType = res.headers.get('content-type') || '';
      if (contentType.includes('application/json')) {
        return await res.json() as T;
      }
      return {} as T;
    } finally {
      clearTimeout(timeout);
    }
  }

  // --- Health ---

  async isAvailable(): Promise<boolean> {
    try {
      const controller = new AbortController();
      const timeout = setTimeout(() => controller.abort(), 5000);
      try {
        const res = await fetch(`${this.baseUrl}/health`, {
          headers: { 'X-API-Key': this.apiKey },
          signal: controller.signal,
        });
        return res.ok;
      } finally {
        clearTimeout(timeout);
      }
    } catch {
      return false;
    }
  }

  async getHealth(): Promise<TermuxHealthResponse | null> {
    if (!this.enabled) return null;
    try {
      return await this.request<TermuxHealthResponse>('GET', '/health');
    } catch (err) {
      logger.warn(`Termux getHealth failed: ${err instanceof Error ? err.message : err}`);
      return null;
    }
  }

  // --- SMS ---

  async sendSms(phone: string, message: string): Promise<TermuxSmsResult> {
    if (!this.enabled) {
      return { success: false, error: 'SMS feature not enabled' };
    }

    try {
      return await this.request<TermuxSmsResult>('POST', '/api/sms/send', {
        phone,
        message,
      }, 30000); // 30s timeout for SMS send
    } catch (err) {
      const errorMsg = err instanceof Error ? err.message : 'Unknown error';
      logger.warn(`Termux sendSms to ${phone} failed: ${errorMsg}`);
      return { success: false, error: errorMsg };
    }
  }

  /**
   * Get inbox messages from the phone.
   * @param since - ISO date string; only return messages newer than this
   * @param limit - Max messages to return (default 100)
   */
  async getInbox(since?: string, limit = 100): Promise<TermuxInboxMessage[]> {
    if (!this.enabled) return [];

    try {
      const params = new URLSearchParams({ limit: String(limit), type: 'inbox' });
      if (since) params.set('since', since);
      const data = await this.request<{ messages?: TermuxInboxMessage[]; success?: boolean }>(
        'GET',
        `/api/sms/inbox?${params.toString()}`,
      );
      return data.messages || [];
    } catch (err) {
      logger.warn(`Termux getInbox failed: ${err instanceof Error ? err.message : err}`);
      return [];
    }
  }

  /**
   * Get contacts from the phone's address book
   */
  async getContacts(): Promise<TermuxContact[]> {
    if (!this.enabled) return [];

    try {
      const data = await this.request<{ contacts?: TermuxContact[]; success?: boolean }>(
        'GET',
        '/api/contacts/list',
      );
      return data.contacts || [];
    } catch (err) {
      logger.warn(`Termux getContacts failed: ${err instanceof Error ? err.message : err}`);
      return [];
    }
  }

  // --- Device ---

  async getBattery(): Promise<TermuxBatteryStatus | null> {
    if (!this.enabled) return null;

    try {
      // Flask endpoint returns { battery: { percentage, status, ... }, success: true }
      const data = await this.request<{ battery?: TermuxBatteryStatus } & Partial<TermuxBatteryStatus>>(
        'GET', '/api/device/battery',
      );
      return data.battery || data as unknown as TermuxBatteryStatus;
    } catch (err) {
      logger.warn(`Termux getBattery failed: ${err instanceof Error ? err.message : err}`);
      return null;
    }
  }

  async getDeviceInfo(): Promise<Record<string, unknown> | null> {
    if (!this.enabled) return null;

    try {
      return await this.request<Record<string, unknown>>('GET', '/api/device/info');
    } catch (err) {
      logger.warn(`Termux getDeviceInfo failed: ${err instanceof Error ? err.message : err}`);
      return null;
    }
  }

  // --- Logs ---

  /**
   * Get last N lines from the phone's SMS API log file.
   */
  async getLogs(lines = 100): Promise<{ lines: string[]; totalLines: number; fileSize: number } | null> {
    if (!this.enabled) return null;

    try {
      const data = await this.request<{ lines?: string[]; total_lines?: number; file_size?: number; success?: boolean }>(
        'GET',
        `/api/logs/tail?lines=${Math.min(500, Math.max(1, lines))}`,
        undefined,
        15000,
      );
      return {
        lines: data.lines || [],
        totalLines: data.total_lines || 0,
        fileSize: data.file_size || 0,
      };
    } catch (err) {
      logger.warn(`Termux getLogs failed: ${err instanceof Error ? err.message : err}`);
      return null;
    }
  }

  // --- Test (static, for setup wizard) ---

  /**
   * Test connectivity to an arbitrary Termux API server.
   * Does NOT use or modify the singleton client state.
   */
  static async testConnection(url: string, apiKey: string): Promise<{ success: boolean; health?: TermuxHealthResponse; error?: string }> {
    const controller = new AbortController();
    const timeout = setTimeout(() => controller.abort(), 10000);

    try {
      const res = await fetch(`${url}/health`, {
        headers: { 'X-API-Key': apiKey },
        signal: controller.signal,
      });

      if (!res.ok) {
        const text = await res.text().catch(() => '');
        return { success: false, error: `HTTP ${res.status}: ${text}` };
      }

      const contentType = res.headers.get('content-type') || '';
      if (contentType.includes('application/json')) {
        const health = await res.json() as TermuxHealthResponse;
        return { success: true, health };
      }

      return { success: true };
    } catch (err) {
      const errorMsg = err instanceof Error ? err.message : 'Connection failed';
      return { success: false, error: errorMsg };
    } finally {
      clearTimeout(timeout);
    }
  }
}

export { TermuxClient };
export const termuxClient = new TermuxClient();
