# frozen_string_literal: true

require 'httparty'
require 'json'
require 'singleton'
require_relative '../config/settings'
require_relative '../models/local_device'
require_relative '../models/custom_integration_code'

# 云端API客户端
class CloudClient
  include Singleton
  
  attr_reader :api_token, :base_url
  attr_accessor :access_token, :access_token_expires_at
  
  def initialize(api_token: nil, base_url: nil)
    @api_token = api_token || settings.get(:api_token)
    @base_url = base_url || settings.get(:cloud_url)
    
    load_saved_token
    
    raise "API令牌未配置" if @api_token.nil? || @api_token.empty?
    raise "云端URL未配置" if @base_url.nil? || @base_url.empty?
  end
  
  # 类方法：测试 API Token 是否有效（不使用 Singleton）
  def self.test_token(api_token, base_url)
    client = allocate
    client.instance_variable_set(:@api_token, api_token)
    client.instance_variable_set(:@base_url, base_url)
    client.instance_variable_set(:@access_token, nil)
    client.instance_variable_set(:@access_token_expires_at, nil)

    # 直接调用 /auth/token 测试
    response = HTTParty.post(
      "#{base_url}/api/v1/auth/token",
      headers: {
        'X-API-Token' => api_token,
        'User-Agent' => "NetCat-Client/1.1.0"
      },
      timeout: 30
    )

    response.success?
  end

  # 类方法：测试云端连接（使用新的 /api/v1/auth/test 端点）
  # 返回详细的响应信息，包括成功/失败状态和消息
  def self.test_connection(api_token, base_url)
    client = allocate
    client.instance_variable_set(:@api_token, api_token)
    client.instance_variable_set(:@base_url, base_url)

    # 优先使用 Header 方式传递 API Token
    response = HTTParty.get(
      "#{base_url}/api/v1/auth/test",
      headers: {
        'X-API-Token' => api_token,
        'User-Agent' => "NetCat-Client/1.1.0"
      },
      timeout: 30
    )

    result = {
      success: response.success?,
      status: response.code,
      message: nil,
      data: nil
    }

    if response.success?
      begin
        json_response = JSON.parse(response.body)
        result[:message] = json_response['message'] || '连接测试成功'
        result[:data] = json_response['data']
      rescue JSON::ParserError
        result[:message] = '连接成功，但响应格式异常'
      end
    else
      begin
        json_response = JSON.parse(response.body)
        result[:message] = json_response['error'] || json_response['message'] || "连接失败 (HTTP #{response.code})"
      rescue JSON::ParserError
        result[:message] = "连接失败 (HTTP #{response.code}): #{response.message}"
      end
    end

    result
  rescue HTTParty::Error => e
    {
      success: false,
      status: nil,
      message: "网络错误: #{e.message}",
      data: nil
    }
  rescue StandardError => e
    {
      success: false,
      status: nil,
      message: "测试失败: #{e.message}",
      data: nil
    }
  end
  
  def test_connection
    begin
      ensure_valid_token!
      response = get('/api/v1/tasks', limit: 1)
      response.success?
    rescue StandardError => e
      LogService.error("云端连接测试失败: #{e.message}")
      false
    end
  end
  
  def get_tasks(status: nil, limit: 10)
    ensure_valid_token!
    
    response = get('/api/v1/tasks', status: status, limit: limit)
    
    if response.success?
      json_response = JSON.parse(response.body)
      json_response['data']
    else
      handle_api_error(response, '获取任务列表')
      []
    end
  end
  
  def get_task(task_id)
    ensure_valid_token!
    
    response = get("/api/v1/tasks/#{task_id}")
    
    if response.success?
      json_response = JSON.parse(response.body)
      json_response['data']
    else
      handle_api_error(response, '获取任务详情')
      nil
    end
  end
  
  def get_task_scripts(task_id)
    ensure_valid_token!
    
    response = get("/api/v1/tasks/#{task_id}/scripts")
    
    if response.success?
      json_response = JSON.parse(response.body)
      json_response['data']
    else
      handle_api_error(response, '获取任务脚本')
      []
    end
  end
  
  def update_task_status(task_id, status, result: nil, output: nil, error_message: nil)
    ensure_valid_token!
    
    params = { status: status }
    params[:result] = result.to_json if result
    params[:output] = output if output
    params[:error_message] = error_message if error_message
    
    response = patch("/api/v1/tasks/#{task_id}/update_status", params)
    
    if response.success?
      json_response = JSON.parse(response.body)
      json_response['success']
    else
      handle_api_error(response, '更新任务状态')
      false
    end
  end
  
  def create_task_from_natural_language(natural_language:, device_ids: [], enhance_requirement: true)
    ensure_valid_token!
    
    begin
      response = post('/api/v1/tasks/create_from_natural_language', {
        natural_language: natural_language
      })
      if response.success?
        json_response = JSON.parse(response.body)
        
        {
          success: true,
          task_definition: transform_cloud_response_to_task_definition(json_response['data'], natural_language)
        }
      else
        error_msg = parse_error_message(response)
        LogService.error("自然语言创建任务失败: #{response.code} - #{error_msg}")
        {
          success: false,
          error: error_msg
        }
      end
    rescue StandardError => e
      LogService.error("自然语言创建任务失败: #{e.message}")
      {
        success: false,
        error: e.message
      }
    end
  end
  
  # 将云端返回的新格式转换为任务定义格式
  def transform_cloud_response_to_task_definition(cloud_data, natural_language)
    LogService.info("[CloudClient] 转换云端响应数据，cloud_data 结构: #{cloud_data.inspect[0..1000]}")

    # 确保 cloud_data 是 Hash 格式
    data = if cloud_data.is_a?(String)
      JSON.parse(cloud_data) rescue {}
    else
      cloud_data
    end

    result = {
      id: "ai_task_#{Time.now.to_i}",
      name: "AI生成任务: #{natural_language[0..20]}...",
      description: natural_language,
      task_json: data,
      execution_mode: 'local',
      created_at: Time.now.iso8601
    }

    LogService.info("[CloudClient] 转换后的任务定义: #{result.inspect[0..1000]}")
    result
  end
  
  def match_scripts_by_device_model(device_model)
    ensure_valid_token!
    
    begin
      response = get('/api/v1/scripts/match_by_device_model', device_model: device_model)
      
      if response.success?
        json_response = JSON.parse(response.body)
        json_response['data']
      else
        []
      end
    rescue StandardError => e
      LogService.error("按设备型号匹配脚本失败: #{e.message}")
      []
    end
  end

  def get_scripts
    ensure_valid_token!
    
    begin
      response = get('/api/v1/scripts')
      
      if response.success?
        json_response = JSON.parse(response.body)
        json_response['data'] || []
      else
        handle_api_error(response, '获取脚本列表')
        []
      end
    rescue StandardError => e
      LogService.error("获取脚本列表失败: #{e.message}")
      []
    end
  end

  def get_script(script_id)
    ensure_valid_token!
    
    begin
      response = get("/api/v1/scripts/#{script_id}")
      
      if response.success?
        json_response = JSON.parse(response.body)
        json_response['data']
      else
        handle_api_error(response, '获取脚本详情')
        nil
      end
    rescue StandardError => e
      LogService.error("获取脚本详情失败: #{e.message}")
      nil
    end
  end

  def get_scripts_for_sync(since: nil)
    ensure_valid_token!
    
    LogService.debug("[脚本同步] 开始调用 get_scripts_for_sync, since: #{since.inspect}")
    LogService.debug("[脚本同步] API端点: #{base_url}/api/v1/scripts/sync")
    
    begin
      params = {}
      params[:since] = since if since
      
      LogService.debug("[脚本同步] 请求参数: #{params.inspect}")
      
      response = get('/api/v1/scripts/sync', params)
      
      LogService.debug("[脚本同步] 响应状态码: #{response.code}")
      LogService.debug("[脚本同步] 响应头: #{response.headers.inspect}")
      LogService.debug("[脚本同步] 响应体: #{response.body[0..500]}...") # 只记录前500字符防止日志过大
      
      if response.success?
        json_response = JSON.parse(response.body)
        data = json_response['data'] || []
        LogService.debug("[脚本同步] 解析成功, 脚本数量: #{data.size}")
        data.each_with_index do |script, idx|
          LogService.debug("[脚本同步] 脚本 #{idx + 1}: id=#{script['id']}, name=#{script['name']}, version=#{script['version']}")
        end
        data
      else
        handle_api_error(response, '获取同步脚本')
        LogService.debug("[脚本同步] API请求失败, 返回空数组")
        []
      end
    rescue StandardError => e
      LogService.error("获取同步脚本失败: #{e.message}")
      LogService.debug('[脚本同步] 异常堆栈: ' + e.backtrace.join("\n"))
      []
    end
  end
  
  def get_user_devices
    ensure_valid_token!
    
    response = get('/api/v1/network_elements')
    
    if response.success?
      json_response = JSON.parse(response.body)
      json_response['data'] || []
    else
      handle_api_error(response, '获取云端设备列表')
      []
    end
  end
  
  # 同步本地网元数据到云端
  # 只同步非敏感字段：name, device_model, description
  # 不包含：ip_address, port, username, password, ssh_key_path 等敏感信息
  def sync_devices
    ensure_valid_token!

    begin
      # 获取所有本地设备，使用 cloud_sync_info 只获取非敏感字段
      local_devices = LocalDevice.all.map(&:cloud_sync_info)

      LogService.info("开始同步 #{local_devices.size} 个设备到云端...")
      LogService.debug("同步数据: #{local_devices.inspect}")

      response = post('/api/v1/network_elements/sync', { devices: local_devices })

      LogService.debug("响应状态码: #{response.code}")
      LogService.debug("响应体: #{response.body}")

      if response.success?
        json_response = JSON.parse(response.body)
        LogService.debug("解析后的响应: #{json_response.inspect}")

        # 兼容不同的响应格式
        data = json_response['data'] || json_response

        synced = data['synced'] || 0
        errors = data['errors'] || 0

        LogService.info("设备同步完成: 成功 #{synced} 个, 失败 #{errors} 个")

        {
          success: true,
          synced: synced,
          errors: errors,
          results: data['results']
        }
      else
        error_msg = parse_error_message(response)
        LogService.error("设备同步失败: #{response.code} - #{error_msg}")

        {
          success: false,
          error: error_msg
        }
      end
    rescue StandardError => e
      LogService.error("设备同步异常: #{e.message}")
      LogService.debug("异常堆栈: #{e.backtrace.join("\n")}")

      {
        success: false,
        error: e.message
      }
    end
  end

  # 同步自定义集成代码从云端到本地
  def sync_custom_integration_code
    ensure_valid_token!

    begin
      response = get('/api/v1/custom_integrations/sync')
      if response.success?
        json_response = JSON.parse(response.body)
        data = json_response['data'] || {}

        code = data['code']
        version_hash = data['version_hash']
        updated_at = data['updated_at'] ? Time.parse(data['updated_at']) : Time.now
        puts code
        if code && version_hash
          CustomIntegrationCode.update_server(code, version_hash, updated_at)
          LogService.info("自定义集成代码同步成功，版本哈希: #{version_hash}")
          {
            success: true,
            version_hash: version_hash,
            updated_at: updated_at.iso8601
          }
        else
          LogService.warn("自定义集成代码同步失败：响应数据不完整")
          {
            success: false,
            error: '响应数据不完整'
          }
        end
      else
        handle_api_error(response, '同步自定义集成代码')
        {
          success: false,
          error: parse_error_message(response)
        }
      end
    rescue StandardError => e
      LogService.error("自定义集成代码同步异常: #{e.message}")
      LogService.debug("异常堆栈: #{e.backtrace.join("\n")}")
      {
        success: false,
        error: e.message
      }
    end
  end

  private
  
  def ensure_valid_token!
    if access_token.nil? || token_expired?
      refresh_access_token!
    end
  end
  
  def token_expired?
    return true if access_token_expires_at.nil?
    access_token_expires_at < Time.now + 5 * 60
  end
  
  def refresh_access_token!
    LogService.info("刷新JWT访问令牌...")
    
    response = HTTParty.post(
      "#{base_url}/api/v1/auth/token",
      headers: {
        'X-API-Token' => api_token,
        'User-Agent' => "NetCat-Client/#{client_version}"
      },
      timeout: 30
    )
    
    if response.success?
      json_response = JSON.parse(response.body)
      data = json_response['data']
      
      @access_token = data['access_token']
      @access_token_expires_at = Time.now + data['expires_in']
      
      save_token
      
      LogService.info("JWT访问令牌刷新成功")
    else
      error_msg = parse_error_message(response)
      LogService.error("刷新JWT访问令牌失败: #{response.code} - #{error_msg}")
      raise "无法获取访问令牌: #{error_msg}"
    end
  end
  
  def load_saved_token
    saved_token = settings.get(:access_token)
    saved_expires_at = settings.get(:access_token_expires_at)
    
    if saved_token && saved_expires_at
      @access_token = saved_token
      @access_token_expires_at = Time.parse(saved_expires_at) rescue nil
    end
  end
  
  def save_token
    settings.set(:access_token, access_token)
    settings.set(:access_token_expires_at, access_token_expires_at.iso8601)
  end
  
  def get(endpoint, params = {})
    headers = auth_headers
    options = {
      headers: headers,
      query: params,
      timeout: 30
    }
    
    HTTParty.get("#{base_url}#{endpoint}", options)
  end
  
  def post(endpoint, body = {})
    headers = auth_headers
    headers['Content-Type'] = 'application/json'
    
    options = {
      headers: headers,
      body: body.to_json,
      timeout: 60 * 5
    }
    
    HTTParty.post("#{base_url}#{endpoint}", options)
  end
  
  def patch(endpoint, body = {})
    headers = auth_headers
    headers['Content-Type'] = 'application/json'
    
    options = {
      headers: headers,
      body: body.to_json,
      timeout: 30
    }
    
    HTTParty.patch("#{base_url}#{endpoint}", options)
  end
  
  def auth_headers
    headers = {
      'User-Agent' => "NetCat-Client/#{client_version}"
    }
    
    headers['Authorization'] = "Bearer #{access_token}" if access_token
    
    headers
  end
  
  def handle_api_error(response, operation)
    error_msg = parse_error_message(response)
    error_code = parse_error_code(response)
    
    LogService.error("#{operation}失败: #{response.code} - #{error_msg} (code: #{error_code})")
    
    case error_code
    when 'invalid_token', 'missing_token'
      @access_token = nil
      @access_token_expires_at = nil
    when 'quota_exceeded'
      LogService.warn("配额已超限，请升级套餐")
    when 'feature_not_available'
      LogService.warn("当前套餐不支持此功能，请升级套餐")
    when 'subscription_expired'
      LogService.warn("订阅已过期，请续费")
    end
  end
  
  def parse_error_message(response)
    begin
      json = JSON.parse(response.body)
      json['error'] || json['message'] || response.message
    rescue
      response.message
    end
  end
  
  def parse_error_code(response)
    begin
      json = JSON.parse(response.body)
      json['code']
    rescue
      nil
    end
  end
  
  def client_version
    '1.1.0'
  end
  
  def simulate_natural_language_task(natural_language, device_ids)
    LogService.info("模拟自然语言任务创建: #{natural_language}")
    
    devices = if device_ids.any?
      LocalDevice.where(id: device_ids).all
    else
      LocalDevice.active
    end
    
    scripts = []
    
    if natural_language.include?('巡检') || natural_language.include?('检查')
      scripts = [{ name: 'show_version', description: '显示设备版本信息' }]
      
      if natural_language.include?('cpu') || natural_language.include?('温度')
        scripts << { name: 'check_cpu_temperature', description: '检查CPU温度' }
      end
      
      if natural_language.include?('内存')
        scripts << { name: 'check_memory', description: '检查内存使用率' }
      end
      
      if natural_language.include?('风扇')
        scripts << { name: 'check_fan_speed', description: '检查风扇转速' }
      end
    end
    
    task_definition = {
      id: "simulated_#{Time.now.to_i}",
      name: "自然语言任务: #{natural_language[0..30]}...",
      description: "根据自然语言指令生成的任务：#{natural_language}",
      natural_language_input: natural_language,
      definition: {
        devices: devices.map(&:cloud_sync_info),
        scripts: scripts,
        execution_mode: 'local',
        created_at: Time.now.iso8601
      },
      device_count: devices.size,
      script_count: scripts.size
    }
    
    {
      success: true,
      task_definition: task_definition
    }
  end

end
