# frozen_string_literal: true

require 'tempfile'
require_relative '../lib/file_path_utils'
require_relative '../models/custom_integration_code'

# 将 lib 目录添加到 require 路径中
$LOAD_PATH.unshift('./lib')

# 脚本执行器
# 单一职责：执行设备上的脚本并处理输出
class ScriptExecutor
  # 执行单个脚本
  # @param device [TelnetDevice] 已登录的设备对象
  # @param script_info [Hash] 脚本信息
  # @return [Hash] 执行结果
  def execute(device, script_info)
    script_name = script_info['name'] || script_info[:name] || 'unknown'
    code = script_info['code'] || script_info[:code]
    log_file_path = script_info['log_file_path'] || script_info[:log_file_path]
    error = nil
    conclusion = 'success'
    result = nil
    script_instance = nil
    script_error = nil

    # 创建日志缓冲器，支持实时写入文件
    log_buffer = LogBuffer.new(log_file_path)

    begin
      if code && !code.empty?
        # 1. 将 code 字段的值写入临时文件
        temp_file = Tempfile.new(['netcat_script_', '.rb'], FilePathUtils.task_tmp_dir)
        begin
          temp_file.write(code)
          puts code
          temp_file.close

          log_buffer.append("[信息] 开始执行脚本: #{script_name}\n")
          log_buffer.append("[信息] 临时文件: #{temp_file.path}\n")

          # 2. load 该文件（使用 load 而不是 require，确保每次都重新加载）
          log_buffer.append("[信息] 加载脚本文件...\n")

          # 加载自定义集成代码（如果启用）
          load_custom_integration_code(log_buffer)

          load temp_file.path

          # 3. 查找脚本类并调用 run 函数
          log_buffer.append("[信息] 查找脚本类...\n")
          script_class = find_script_class(code)
          if script_class
            log_buffer.append("[信息] 找到脚本类: #{script_class.name}\n")
            script_instance = script_class.new({}, log_buffer)

            # 记录开始时间
            script_instance.record_start!

            # 创建连接包装对象（传入脚本实例用于记录对话）
            connection = create_connection_wrapper(device, log_buffer, script_instance)

            # 设置脚本实例的 connection，这样 log_* 方法可以通过 connection 记录日志
            script_instance.connection = connection

            # 4. 调用其中的 run 函数，将 telnet 对象传入
            log_buffer.append("[信息] 调用 run(connection)...\n")
            result = script_instance.run(connection)
            log_buffer.append("[信息] 脚本执行完成\n")
            log_buffer.append("[结果] \n#{format_script_result(result)}\n") if result
          else
            log_buffer.append("[错误] 无法找到脚本类\n")
            conclusion = 'failure'
          end
        ensure
          temp_file.close! if temp_file
        end
      else
        log_buffer.append("[警告] 脚本代码为空\n")
      end

    rescue => e
      script_error = e
      error = e.message
      conclusion = 'failure'
      log_buffer.append("[错误] 脚本执行异常: #{e.message}\n")
      log_buffer.append("[错误] 堆栈跟踪:\n#{e.backtrace.join("\n")}\n")
    ensure
      log_buffer.close
    end

    # 构建返回结果
    execution_result = {
      output: log_buffer.content,
      error: error,
      conclusion: conclusion,
      result: result
    }

    # 如果脚本实例存在，添加检查点和对话记录
    if script_instance
      script_result = script_instance.build_script_result(script_error)
      execution_result[:checkpoints] = script_result.checkpoints
      execution_result[:script_result] = script_result.to_h
    end

    execution_result
  end

  private

  # 加载自定义集成代码
  def load_custom_integration_code(log_buffer)
    begin
      # 检查是否启用自定义集成
      require_relative '../config/settings'
      settings = Settings.instance
      enabled = settings.get(:custom_integration, :enabled)
      return unless enabled

      code = CustomIntegrationCode.current_code
      return if code.nil? || code.empty? || code.strip == '# 自定义集成代码不可用'

      log_buffer.append("[信息] 加载自定义集成代码...\n")

      # 使用 eval 加载代码到当前上下文
      # 注意：这里存在安全风险，但代码来自可信源（用户或服务器）
      # 生产环境应考虑沙箱执行
      eval(code)
      log_buffer.append("[信息] 自定义集成代码加载完成\n")
    rescue StandardError => e
      log_buffer.append("[错误] 加载自定义集成代码失败: #{e.message}\n")
      # 不抛出异常，继续执行脚本
    end
  end

  # 格式化脚本结果，生成易读的输出
  def format_script_result(result)
    return result.inspect unless result.is_a?(NetCat::ScriptResult)

    output = []
    output << "=" * 60
    output << "脚本执行结果"
    output << "=" * 60

    # 基本信息
    output << "状态: #{result.success ? '成功' : '失败'}"
    output << "执行结果: #{result.status}"
    output << "错误: #{result.error || '无'}"

    # 摘要统计
    if result.summary.is_a?(Hash)
      passed = result.summary[:passed] || result.summary['passed'] || 0
      failed = result.summary[:failed] || result.summary['failed'] || 0
      total = result.summary[:total] || result.summary['total'] || 0
      output << "检查点统计: 通过 #{passed}/#{total}，失败 #{failed}/#{total}"
    end

    # 执行时间
    output << "执行时长: #{'%.3f' % result.duration} 秒"
    output << "开始时间: #{result.started_at}"
    output << "结束时间: #{result.finished_at}"

    # 检查点详情
    if result.checkpoints && !result.checkpoints.empty?
      output << ""
      output << "检查点详情:"
      output << "-" * 40

      result.checkpoints.each_with_index do |checkpoint, i|
        idx = i + 1
        status = if checkpoint[:passed] == true
                   "✓ 通过"
                 elsif checkpoint[:passed] == false
                   "✗ 失败"
                 else
                   "? 未完成"
                 end

        output << "#{idx}. #{checkpoint[:name]} - #{status}"

        if checkpoint[:value]
          # 限制值长度，避免过长
          value_str = checkpoint[:value].to_s.gsub(/\r?\n/, ' ')
          if value_str.length > 80
            value_str = value_str[0..77] + "..."
          end
          output << "   值: #{value_str}"
        end

        if checkpoint[:command]
          output << "   命令: #{checkpoint[:command]}"
        end

        if checkpoint[:expected]
          output << "   预期: #{checkpoint[:expected]}"
        end

        if checkpoint[:reason] && checkpoint[:passed] == false
          output << "   原因: #{checkpoint[:reason].to_s.gsub(/\r?\n/, ' ')}"
        end

        if checkpoint[:suggestion] && checkpoint[:passed] == false
          output << "   建议: #{checkpoint[:suggestion]}"
        end

        output << "" unless i == result.checkpoints.size - 1
      end
    end

    output << "=" * 60
    output.join("\n")
  end

  # 日志缓冲器，支持实时写入文件并在内存中保留完整内容
  class LogBuffer
    def initialize(log_file_path = nil)
      @content = +''
      @log_file = nil
      if log_file_path
        FilePathUtils.ensure_file_dir_exists(log_file_path)
        @log_file = File.open(log_file_path, 'w:utf-8')
      end
    end

    # 获取带时间戳的前缀
    def timestamp_prefix
      Time.now.strftime('%Y-%m-%d %H:%M:%S')
    end

    # 追加日志内容，同时写入内存和文件（自动添加时间戳）
    def append(text)
      # 如果是以 [INFO]、[ERROR]、[WARN]、[DEBUG]、[命令]、[输出]、[信息]、[错误]、[警告]、[结果] 开头的，给第一行添加时间戳
      if text.match?(/\A\[(INFO|ERROR|WARN|DEBUG|命令|输出|信息|错误|警告|结果|提示)\]/)
        lines = text.split("\n")
        first_line = true
        lines.each do |line|
          next if line.empty?
          if first_line
            timed_line = "[#{timestamp_prefix}] #{line}\n"
            @content << timed_line
            @log_file.write(timed_line) if @log_file
            first_line = false
          else
            @content << "#{line}\n"
            @log_file.write("#{line}\n") if @log_file
          end
        end
      else
        # 其他内容直接添加
        @content << text
        @log_file.write(text) if @log_file
      end
      @log_file.flush if @log_file
    end

    # 获取完整日志内容
    def content
      @content
    end

    # 关闭日志文件
    def close
      @log_file.close if @log_file
    end

    # 日志方法兼容
    def info(msg); append("[INFO] #{msg}\n"); end
    def error(msg); append("[ERROR] #{msg}\n"); end
    def warn(msg); append("[WARN] #{msg}\n"); end
    def debug(msg); append("[DEBUG] #{msg}\n"); end
  end

  # 查找脚本类
  # 从代码中解析类名，并查找继承自 NetCat::DeviceScript 的子类
  def find_script_class(code)
    # 从代码中匹配类定义，如 "class CapturePacketLength_2fc8cc2f < NetCat::DeviceScript"
    class_match = code.match(/class\s+(\w+)\s*<\s*NetCat::DeviceScript/)
    if class_match
      class_name = class_match[1]
      log_buffer.append("[信息] 从代码中解析到类名: #{class_name}\n") if defined?(log_buffer)

      # 在 NetCatScript 模块中查找
      if NetCatScript.const_defined?(class_name)
        klass = NetCatScript.const_get(class_name)
        return klass if klass.is_a?(Class) && klass <= NetCat::DeviceScript
      end
    end
  end

  # 创建连接包装对象
  def create_connection_wrapper(device, log_buffer, script_instance = nil)
    Object.new.tap do |wrapper|
      # 定义 cli 方法
      wrapper.define_singleton_method(:cli) do |command|
        log_buffer.append("[命令] #{command}\n")
        result = device.cli(command)
        if result && !result.empty?
          log_buffer.append("[响应]\n#{result}\n")
        end
        result
      end

      # 定义 log_info 方法
      wrapper.define_singleton_method(:log_info) do |message|
        log_buffer.append("[INFO] #{message}\n")
        log_buffer.append("---------------------------------------------[INFO 结束]---------------------------------------------\n")
      end

      # 定义 log_error 方法
      wrapper.define_singleton_method(:log_error) do |message|
        log_buffer.append("[ERROR] #{message}\n")
        log_buffer.append("---------------------------------------------[ERROR 结束]---------------------------------------------\n")
      end

      # 定义 log_warn 方法
      wrapper.define_singleton_method(:log_warn) do |message|
        log_buffer.append("[WARN] #{message}\n")
        log_buffer.append("---------------------------------------------[WARN 结束]---------------------------------------------\n")
      end

      # 定义 log_debug 方法
      wrapper.define_singleton_method(:log_debug) do |message|
        log_buffer.append("[DEBUG] #{message}\n")
        log_buffer.append("---------------------------------------------[DEBUG 结束]---------------------------------------------\n")
      end
    end
  end
end
