# frozen_string_literal: true

require 'json'

# 报告数据生成服务
class ReportGenerator
  def self.instance
    @instance ||= new
  end

  # 生成任务报告数据
  def generate_report(task)
    execution = task.latest_execution
    raise '任务执行记录不存在' unless execution

    execution_units = execution.get_execution_units || {}
    devices_data = parse_execution_units(execution_units)

    {
      task: task_summary(task, execution),
      statistics: calculate_statistics(devices_data),
      devices: devices_data,
      errors: collect_errors(devices_data),
      flat_scripts: generate_flat_scripts(devices_data)
    }
  end

  private

  # 任务摘要
  def task_summary(task, execution)
    {
      id: task.id,
      execution_id: execution.id,
      name: task.name,
      description: task.description,
      status: execution.status,
      status_text: execution.status_text,
      progress: execution.progress,
      created_at: task.created_at&.iso8601,
      started_at: execution.started_at&.iso8601,
      completed_at: execution.completed_at&.iso8601,
      duration: calculate_duration(execution.started_at, execution.completed_at)
    }
  end

  # 计算执行时长
  def calculate_duration(started_at, completed_at)
    return nil unless started_at && completed_at

    seconds = (completed_at - started_at).to_i
    hours = seconds / 3600
    minutes = (seconds % 3600) / 60
    secs = seconds % 60

    if hours > 0
      "#{hours}小时#{minutes}分#{secs}秒"
    elsif minutes > 0
      "#{minutes}分#{secs}秒"
    else
      "#{secs}秒"
    end
  end

  # 解析执行单元为设备数据
  def parse_execution_units(execution_units)
    devices = []

    execution_units.each do |device_id, device_info|
      # 兼容符号键和字符串键
      device_info = device_info.transform_keys(&:to_s)
      scripts = device_info['scripts'] || device_info[:scripts] || {}

      scripts_data = scripts.map do |script_id, script_info|
        script_info = script_info.transform_keys(&:to_s)
        parse_script(script_id, script_info)
      end.compact

      device_status = calculate_device_status(scripts_data)
      device_duration = calculate_device_duration(scripts_data)

      devices << {
        id: device_id,
        name: device_info['name'] || device_info[:name],
        device_ip: device_info['device_ip'] || device_info[:device_ip],
        device_model: device_info['device_model'] || device_info[:device_model],
        description: device_info['description'] || device_info[:description],
        status: device_status,
        status_text: status_text(device_status),
        scripts_count: scripts_data.size,
        scripts_success: scripts_data.count { |s| s[:status] == 'completed' },
        duration: device_duration,
        scripts: scripts_data
      }
    end

    devices
  end

  # 解析单个脚本
  def parse_script(script_id, script_info)
    started_at = script_info['started_at']
    completed_at = script_info['completed_at']

    {
      id: script_id,
      name: script_info['name'] || script_info['script_name'] || "脚本#{script_id[0..7]}",
      status: script_info['status'] || 'pending',
      status_text: script_status_text(script_info['status']),
      conclusion: script_info['conclusion'],
      output: script_info['output'],
      error: script_info['error'],
      started_at: started_at,
      completed_at: completed_at,
      duration: script_duration(started_at, completed_at),
      log_file_path: script_info['log_file_path']
    }
  end

  # 计算设备状态
  def calculate_device_status(scripts)
    return 'pending' if scripts.empty?

    statuses = scripts.map { |s| s[:status] }.uniq

    if statuses.include?('running')
      'running'
    elsif statuses.all? { |s| s == 'completed' }
      'completed'
    elsif statuses.include?('completed')
      'partial'
    elsif statuses.include?('failed')
      'failed'
    else
      'pending'
    end
  end

  # 计算设备执行时长
  def calculate_device_duration(scripts)
    started_times = scripts.map { |s| s[:started_at] }.compact
    completed_times = scripts.map { |s| s[:completed_at] }.compact

    return nil if started_times.empty? || completed_times.empty?

    begin
      started = started_times.map { |t| Time.parse(t) }.min
      completed = completed_times.map { |t| Time.parse(t) }.max
      seconds = (completed - started).to_i

      if seconds >= 60
        "#{seconds / 60}分#{seconds % 60}秒"
      else
        "#{seconds}秒"
      end
    rescue
      nil
    end
  end

  # 脚本执行时长
  def script_duration(started_at, completed_at)
    return nil unless started_at && completed_at

    begin
      started = Time.parse(started_at)
      completed = Time.parse(completed_at)
      seconds = (completed - started).to_i

      if seconds >= 60
        "#{seconds / 60}分#{seconds % 60}秒"
      elsif seconds > 0
        "#{seconds}秒"
      else
        "<1秒"
      end
    rescue
      nil
    end
  end

  # 设备状态文本
  def status_text(status)
    {
      'completed' => '全部成功',
      'partial' => '部分成功',
      'failed' => '全部失败',
      'running' => '执行中',
      'pending' => '待执行'
    }[status] || status
  end

  # 脚本状态文本
  def script_status_text(status)
    {
      'completed' => '成功',
      'failed' => '失败',
      'running' => '执行中',
      'pending' => '待执行',
      'cancelled' => '已取消'
    }[status] || status
  end

  # 计算统计数据
  def calculate_statistics(devices)
    total_devices = devices.size
    total_scripts = devices.sum { |d| d[:scripts_count] }
    success_scripts = devices.sum { |d| d[:scripts_success] }

    {
      total_devices: total_devices,
      all_success_devices: devices.count { |d| d[:status] == 'completed' },
      partial_success_devices: devices.count { |d| d[:status] == 'partial' },
      all_failed_devices: devices.count { |d| d[:status] == 'failed' },
      total_scripts: total_scripts,
      success_scripts: success_scripts,
      failed_scripts: total_scripts - success_scripts,
      success_rate: total_devices > 0 ? "#{((devices.count { |d| d[:status] == 'completed' } * 100.0) / total_devices).round(1)}%" : '0%',
      script_success_rate: total_scripts > 0 ? "#{((success_scripts * 100.0) / total_scripts).round(1)}%" : '0%'
    }
  end

  # 收集错误
  def collect_errors(devices)
    errors = Hash.new { |h, k| h[k] = [] }

    devices.each do |device|
      device[:scripts].each do |script|
        next unless script[:status] == 'failed'

        error_msg = script[:error] || script[:conclusion] || '未知错误'
        errors[error_msg] << {
          device_id: device[:id],
          device_name: device[:name],
          device_ip: device[:device_ip],
          script_id: script[:id],
          script_name: script[:name]
        }
      end
    end

    errors.map do |error, items|
      {
        error: error,
        count: items.size,
        affected_devices: items
      }
    end.sort_by { |e| -e[:count] }
  end

  # 生成扁平的脚本列表
  def generate_flat_scripts(devices)
    devices.flat_map do |device|
      device[:scripts].map do |script|
        script.merge(
          device_id: device[:id],
          device_name: device[:name],
          device_ip: device[:device_ip],
          device_model: device[:device_model]
        )
      end
    end
  end
end
