/**
 * Telegram Client
 *
 * HTTP client for interacting with Telegram Bot API
 * Uses axios for HTTP requests with retry logic and error handling
 */

import axios, { AxiosInstance, AxiosError } from 'axios';
import { logger } from '../../../shared/utils/logger';
import {
  telegramConfig,
  TelegramConfig,
  getTelegramApiEndpoint,
  isTelegramEnabled,
} from '../../../config/telegram';

/**
 * Telegram API Response Interface
 */
export interface TelegramApiResponse<T = any> {
  ok: boolean;
  result?: T;
  description?: string;
  error_code?: number;
  parameters?: {
    migrate_to_chat_id?: number;
    retry_after?: number;
  };
}

/**
 * Telegram Message Interface
 */
export interface TelegramMessage {
  chat_id: string;
  text: string;
  parse_mode?: 'Markdown' | 'MarkdownV2' | 'HTML';
  disable_web_page_preview?: boolean;
  disable_notification?: boolean;
  reply_to_message_id?: number | undefined;
  reply_markup?: any;
}

/**
 * Telegram Send Photo Interface
 */
export interface TelegramPhotoMessage {
  chat_id: string;
  photo: string | Buffer;
  caption?: string | undefined;
  parse_mode?: 'Markdown' | 'MarkdownV2' | 'HTML';
  disable_notification?: boolean;
}

/**
 * Telegram Send Document Interface
 */
export interface TelegramDocumentMessage {
  chat_id: string;
  document: string | Buffer;
  caption?: string | undefined;
  parse_mode?: 'Markdown' | 'MarkdownV2' | 'HTML';
  disable_notification?: boolean;
}

/**
 * Telegram Chat Interface
 */
export interface TelegramChat {
  id: number;
  type: 'private' | 'group' | 'supergroup' | 'channel';
  title?: string;
  username?: string;
  first_name?: string;
  last_name?: string;
}

/**
 * Telegram User Interface
 */
export interface TelegramUser {
  id: number;
  is_bot: boolean;
  first_name: string;
  last_name?: string;
  username?: string;
  language_code?: string;
}

/**
 * Telegram Webhook Info Interface
 */
export interface TelegramWebhookInfo {
  url: string;
  has_custom_certificate: boolean;
  pending_update_count: number;
  last_error_date?: number;
  last_error_message?: string;
  max_connections?: number;
  allowed_updates?: string[];
}

/**
 * Telegram Bot Info Interface
 */
export interface TelegramBotInfo {
  id: number;
  is_bot: true;
  first_name: string;
  username: string;
  can_join_groups: boolean;
  can_read_all_group_messages: boolean;
  supports_inline_queries: boolean;
}

/**
 * Custom Error Class for Telegram API Errors
 */
export class TelegramApiError extends Error {
  constructor(
    message: string,
    public errorCode?: number,
    public description?: string,
    public retryAfter?: number
  ) {
    super(message);
    this.name = 'TelegramApiError';
  }
}

/**
 * Telegram Client Class
 */
export class TelegramClient {
  private axiosInstance: AxiosInstance;
  private config: TelegramConfig;
  private botInfo: TelegramBotInfo | null = null;

  constructor() {
    this.config = telegramConfig.getConfig();

    // Create axios instance with default configuration
    this.axiosInstance = axios.create({
      timeout: this.config.timeout,
      headers: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
      },
    });

    // Add request interceptor for logging
    this.axiosInstance.interceptors.request.use(
      (config) => {
        logger.debug({
          method: config.method?.toUpperCase(),
          url: config.url,
        }, 'Telegram API request');
        return config;
      },
      (error) => {
        logger.error({ error }, 'Telegram API request interceptor error');
        return Promise.reject(error);
      }
    );

    // Add response interceptor for logging and error handling
    this.axiosInstance.interceptors.response.use(
      (response) => {
        logger.debug({
          status: response.status,
          url: response.config.url,
        }, 'Telegram API response');
        return response;
      },
      (error) => {
        if (error.response) {
          logger.warn({
            status: error.response.status,
            data: error.response.data,
            url: error.config.url,
          }, 'Telegram API error response');
        }
        return Promise.reject(this.handleApiError(error));
      }
    );
  }

  /**
   * Handle Telegram API errors
   */
  private handleApiError(error: AxiosError<any>): TelegramApiError {
    if (error.response) {
      const { error_code, description, parameters } = error.response.data;

      // Rate limiting (429)
      if (error.response.status === 429 || error_code === 429) {
        const retryAfter = parameters?.retry_after || 30;
        logger.warn({
          retryAfter,
          description,
        }, 'Telegram API rate limit exceeded');
        return new TelegramApiError(
          'Rate limit exceeded',
          429,
          description,
          retryAfter
        );
      }

      // Bad request (400)
      if (error.response.status === 400 || error_code === 400) {
        logger.warn({
          description,
        }, 'Telegram API bad request');
        return new TelegramApiError(
          'Bad request: ' + (description || 'Invalid parameters'),
          400,
          description
        );
      }

      // Unauthorized (401)
      if (error.response.status === 401 || error_code === 401) {
        logger.error({
          description,
        }, 'Telegram API unauthorized - invalid bot token');
        return new TelegramApiError(
          'Unauthorized: Invalid bot token',
          401,
          description
        );
      }

      // Forbidden (403)
      if (error.response.status === 403 || error_code === 403) {
        logger.warn({
          description,
        }, 'Telegram API forbidden - bot was blocked or chat not found');
        return new TelegramApiError(
          'Forbidden: Bot was blocked or chat not found',
          403,
          description
        );
      }

      // Not Found (404)
      if (error.response.status === 404 || error_code === 404) {
        return new TelegramApiError(
          'Not Found: Resource does not exist',
          404,
          description
        );
      }

      // Conflict (409) - Server issues
      if (error.response.status === 409 || error_code === 409) {
        logger.warn({
          description,
        }, 'Telegram API conflict');
        return new TelegramApiError(
          'Conflict: ' + (description || 'Server error'),
          409,
          description
        );
      }

      // Generic error response
      return new TelegramApiError(
        description || 'Unknown API error',
        error_code,
        description
      );
    }

    // Network error
    if (error.request) {
      logger.error({
        message: error.message,
        code: error.code,
      }, 'Telegram API network error');
      return new TelegramApiError(
        'Network error: Unable to reach Telegram API',
        undefined,
        error.message
      );
    }

    // Request setup error
    return new TelegramApiError(
      'Request setup error: ' + error.message,
      undefined,
      error.message
    );
  }

  /**
   * Retry logic with exponential backoff
   */
  private async retryWithBackoff<T>(
    fn: () => Promise<T>,
    maxRetries: number = this.config.maxRetries,
    baseDelay: number = this.config.retryDelay
  ): Promise<T> {
    let lastError: TelegramApiError | undefined;

    for (let attempt = 0; attempt <= maxRetries; attempt++) {
      try {
        return await fn();
      } catch (error) {
        lastError = error as TelegramApiError;

        // Don't retry on client errors (4xx) except 429
        if (lastError.errorCode && lastError.errorCode >= 400 && lastError.errorCode < 500) {
          if (lastError.errorCode !== 429) {
            throw lastError;
          }
        }

        // If we have retries left, wait before retrying
        if (attempt < maxRetries) {
          // Use retry_after if present (rate limiting), otherwise exponential backoff
          const delay = lastError.retryAfter
            ? lastError.retryAfter * 1000
            : baseDelay * Math.pow(2, attempt);

          logger.info({
            attempt: attempt + 1,
            maxRetries,
            delay,
            errorCode: lastError.errorCode,
          }, 'Retrying Telegram API request');

          await this.sleep(delay);
        }
      }
    }

    throw lastError;
  }

  /**
   * Sleep utility
   */
  private sleep(ms: number): Promise<void> {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

  /**
   * Make a POST request to Telegram API
   */
  private async post<T>(method: string, data: any): Promise<T> {
    if (!isTelegramEnabled()) {
      throw new TelegramApiError('Telegram notifications are disabled');
    }

    const url = getTelegramApiEndpoint(method);

    return await this.retryWithBackoff(async () => {
      const response = await this.axiosInstance.post<TelegramApiResponse<T>>(url, data);

      if (!response.data.ok) {
        throw this.handleApiError({
          response: {
            status: response.status,
            data: response.data,
          },
        } as AxiosError);
      }

      return response.data.result as T;
    });
  }

  /**
   * Make a GET request to Telegram API
   */
  private async get<T>(method: string, params?: any): Promise<T> {
    if (!isTelegramEnabled()) {
      throw new TelegramApiError('Telegram notifications are disabled');
    }

    const url = getTelegramApiEndpoint(method);

    return await this.retryWithBackoff(async () => {
      const response = await this.axiosInstance.get<TelegramApiResponse<T>>(url, {
        params,
      });

      if (!response.data.ok) {
        throw this.handleApiError({
          response: {
            status: response.status,
            data: response.data,
          },
        } as AxiosError);
      }

      return response.data.result as T;
    });
  }

  /**
   * Send a text message
   */
  public async sendMessage(
    chatId: string,
    text: string,
    options?: {
      parseMode?: 'Markdown' | 'MarkdownV2' | 'HTML';
      disableWebPagePreview?: boolean;
      disableNotification?: boolean;
      replyToMessageId?: number;
    }
  ): Promise<{ message_id: number; date: number; chat: TelegramChat; from: TelegramUser }> {
    const message: TelegramMessage = {
      chat_id: chatId,
      text,
      parse_mode: options?.parseMode || this.config.parseMode,
      disable_web_page_preview: options?.disableWebPagePreview ?? this.config.disableWebPagePreview,
      disable_notification: options?.disableNotification ?? this.config.disableNotification,
      reply_to_message_id: options?.replyToMessageId,
    };

    const result = await this.post('sendMessage', message);

    logger.info({
      chatId,
      messageId: (result as any)['message_id'],
      textLength: text.length,
    }, 'Telegram message sent successfully');

    return result as any;
  }

  /**
   * Send a photo
   */
  public async sendPhoto(
    chatId: string,
    photo: string | Buffer,
    caption?: string,
    options?: {
      parseMode?: 'Markdown' | 'MarkdownV2' | 'HTML';
      disableNotification?: boolean;
    }
  ): Promise<{ message_id: number; date: number; chat: TelegramChat }> {
    const photoMessage: TelegramPhotoMessage = {
      chat_id: chatId,
      photo,
      caption,
      parse_mode: options?.parseMode || this.config.parseMode,
      disable_notification: options?.disableNotification ?? this.config.disableNotification,
    };

    const result = await this.post('sendPhoto', photoMessage);

    logger.info({
      chatId,
      messageId: (result as any)['message_id'],
      hasCaption: !!caption,
    }, 'Telegram photo sent successfully');

    return result as any;
  }

  /**
   * Send a document
   */
  public async sendDocument(
    chatId: string,
    document: string | Buffer,
    caption?: string,
    options?: {
      parseMode?: 'Markdown' | 'MarkdownV2' | 'HTML';
      disableNotification?: boolean;
    }
  ): Promise<{ message_id: number; date: number; chat: TelegramChat }> {
    const docMessage: TelegramDocumentMessage = {
      chat_id: chatId,
      document,
      caption,
      parse_mode: options?.parseMode || this.config.parseMode,
      disable_notification: options?.disableNotification ?? this.config.disableNotification,
    };

    const result = await this.post('sendDocument', docMessage);

    logger.info({
      chatId,
      messageId: (result as any)['message_id'],
      hasCaption: !!caption,
    }, 'Telegram document sent successfully');

    return result as any;
  }

  /**
   * Get bot information
   */
  public async getMe(): Promise<TelegramBotInfo> {
    if (!this.botInfo) {
      this.botInfo = await this.get<TelegramBotInfo>('getMe');
      logger.info({
        botId: this.botInfo.id,
        username: this.botInfo.username,
      }, 'Telegram bot info retrieved');
    }
    return this.botInfo;
  }

  /**
   * Get webhook information
   */
  public async getWebhookInfo(): Promise<TelegramWebhookInfo> {
    return await this.get<TelegramWebhookInfo>('getWebhookInfo');
  }

  /**
   * Delete webhook
   */
  public async deleteWebhook(): Promise<boolean> {
    return await this.get<boolean>('deleteWebhook');
  }

  /**
   * Health check for Telegram client
   */
  public async healthCheck(): Promise<{ healthy: boolean; botInfo?: TelegramBotInfo; error?: string }> {
    try {
      const botInfo = await this.getMe();
      return { healthy: true, botInfo };
    } catch (error) {
      return {
        healthy: false,
        error: error instanceof Error ? error.message : 'Unknown error',
      };
    }
  }
}

/**
 * Send message to admin result interface
 */
export interface SendMessageResult {
  success: boolean;
  messageId?: number;
  error?: string;
  attempts: number;
}

/**
 * Health check result interface
 */
export interface HealthCheckResult {
  healthy: boolean;
  botInfo?: TelegramBotInfo;
  error?: string;
}

/**
 * Export singleton instance
 */
let telegramClientInstance: TelegramClient | null = null;

export function getTelegramClient(): TelegramClient {
  if (!telegramClientInstance) {
    telegramClientInstance = new TelegramClient();
  }
  return telegramClientInstance;
}

/**
 * Telegram Client wrapper with admin-specific methods
 */
class TelegramClientWrapper {
  private client: TelegramClient;

  constructor() {
    this.client = getTelegramClient();
  }

  /**
   * Send message to admin (convenience method for notification service)
   */
  async sendToAdmin(message: string): Promise<SendMessageResult> {
    try {
      // Get admin chat ID from config
      const { getTelegramAdminChatId, isTelegramEnabled } = require('../../../config/telegram');

      if (!isTelegramEnabled()) {
        return {
          success: false,
          error: 'Telegram notifications are disabled',
          attempts: 0,
        };
      }

      const adminChatId = getTelegramAdminChatId();

      const result = await this.client.sendMessage(adminChatId, message, {
        parseMode: 'HTML',
        disableWebPagePreview: true,
      });

      return {
        success: true,
        messageId: result.message_id,
        attempts: 1,
      };
    } catch (error: any) {
      logger.error({ error }, 'Failed to send admin notification via Telegram');
      return {
        success: false,
        error: error.message,
        attempts: 1,
      };
    }
  }

  /**
   * Health check
   */
  async healthCheck(): Promise<HealthCheckResult> {
    return await this.client.healthCheck();
  }

  /**
   * Get underlying client for direct access
   */
  getClient(): TelegramClient {
    return this.client;
  }
}

// Export singleton wrapper instance for notification service compatibility
export const telegramClient = new TelegramClientWrapper();

export default TelegramClient;
