# frozen_string_literal: true

require 'securerandom'
require 'sequel'
require 'json'
require 'time'
require 'fileutils'
require_relative '../config/database'
require_relative '../lib/file_path_utils'
require_relative '../lib/task_execution_data_loader'

# 确保数据库连接已建立并设置全局Sequel模型数据库
Database.connection

# 任务执行记录模型
class LocalTaskExecution < Sequel::Model(:local_task_executions)
  # Sequel模型插件
  plugin :timestamps, update_on_create: true
  plugin :validation_helpers
  plugin :json_serializer
  plugin :serialization
  
  # JSON序列化字段
  serialize_attributes :json, :execution_result, :execution_units
  
  # 关联
  many_to_one :task, class: :LocalTask, key: :task_id
  
  # 验证
  def validate
    super
    validates_presence :task_id
  end
  
  # 状态文本映射
  def status_text
    status_map = {
      'pending' => '待执行',
      'running' => '执行中',
      'completed' => '已完成',
      'failed' => '失败',
      'cancelled' => '已取消'
    }
    status_map[status] || status
  end
  def datetime_to_filename(datetime = Time.now, format = "%Y%m%d_%H%M%S")
    # 将日期时间对象转换为指定格式的字符串
    # 默认格式: 20231231_235959 (年月日_时分秒)
    datetime.strftime(format)
  end
  # 获取任务名称（固定使用 created_at 时间，确保不会变化）
  def task_dir_name
    base_time = created_at || started_at || Time.now
    datetime_to_filename(base_time) + "_#{id}"
  end
  
  # 获取任务根目录
  def task_root_dir
    FilePathUtils.task_root_dir(task_dir_name)
  end
  
  # 获取任务JSON状态文件路径
  def task_json_status_file_path
    FilePathUtils.task_json_status_file_path(task_dir_name)
  end
  
  # 开始执行
  def start!
    self.status = 'running'
    self.progress = 0
    self.started_at = Time.now
    
    # 初始化文件存储
    initialize_file_storage
    
    # 立即保存数据库（开始时需要保存）
    save
  end
  
  # 完成执行 - 任务结束时才保存数据库
  def complete!(success: true, result: nil)
    self.status = success ? 'completed' : 'failed'
    self.progress = 100
    self.completed_at = Time.now
    self.execution_result = result if result

    # 最终保存到数据库
    save_final_to_database
  end

  # 从内存状态完成执行 - 使用内存中的最终状态保存到数据库
  def complete_from_memory!(mem_state, success: true)
    puts "[LocalTaskExecution] complete_from_memory! called, success=#{success}"
    self.status = success ? 'completed' : 'failed'
    self.progress = mem_state[:progress] || mem_state['progress'] || 100
    self.completed_at = Time.now
    self.execution_units = mem_state[:execution_units] || mem_state['execution_units']

    # 直接保存到数据库，不要再从文件加载
    save
    puts "[LocalTaskExecution] Saved to database, status=#{self.status}, progress=#{self.progress}"
  end
  
  # 取消执行 - 任务结束时才保存数据库
  def cancel!
    self.status = 'cancelled'
    self.completed_at = Time.now
    
    # 最终保存到数据库
    save_final_to_database
  end
  
  # 更新进度 - 只保存到文件
  def update_progress!(progress)
    self.progress = [0, [progress, 100].min].max
    # 不保存数据库，只保存到文件
    save_status_to_file
  end
  
  # 创建新的执行记录
  def self.create_for_task(task_id)
    execution = new
    execution.id = SecureRandom.uuid
    execution.task_id = task_id
    execution.status = 'pending'
    execution.progress = 0
    
    # 解析并存储执行单元
    task = LocalTask[task_id]
    execution.execution_units = execution.parse_task_info(task) if task
    
    execution.save
    execution
  end
  def convert(grouped_format)
    devices = []
    scripts = []

    return { devices: [], scripts: [] } unless grouped_format.is_a?(Hash)

    LogService.info("[LocalTaskExecution] convert 输入: #{grouped_format.inspect[0..1500]}")

    # 支持多种格式：
    # 1. 简单格式: { devices: [...], scripts: [...] }
    # 2. 分组格式: { group1: { devs: [...], scripts: [...] }, ... }
    # 3. 包装在 task_json 中的格式: { task_json: {...} }

    # 先检查是否有 task_json 包装
    data = grouped_format
    if grouped_format.key?('task_json') || grouped_format.key?(:task_json)
      data = grouped_format['task_json'] || grouped_format[:task_json] || {}
      LogService.info("[LocalTaskExecution] convert 从 task_json 提取数据: #{data.inspect[0..1000]}")
    end

    return { devices: [], scripts: [] } unless data.is_a?(Hash)

    if data.key?('devices') || data.key?(:devices)
      # 简单格式
      devices = data['devices'] || data[:devices] || []
      scripts = data['scripts'] || data[:scripts] || []
      LogService.info("[LocalTaskExecution] convert 使用简单格式，设备数: #{devices.length}, 脚本数: #{scripts.length}")
    else
      # 分组格式
      data.each_value do |group|
        next unless group.is_a?(Hash)
        # 处理设备
        devs = group['devs'] || group[:devs] || []
        devices.concat(devs)

        # 处理脚本
        group_scripts = group['scripts'] || group[:scripts] || []
        scripts.concat(group_scripts)
      end
      LogService.info("[LocalTaskExecution] convert 使用分组格式，设备数: #{devices.length}, 脚本数: #{scripts.length}")
    end

    result = {
      devices: devices.uniq { |d| d['id'] || d[:id] },
      scripts: scripts.uniq { |s| s['id'] || s[:id] }
    }

    LogService.info("[LocalTaskExecution] convert 结果: #{result.inspect[0..1000]}")
    result
  end
  # 从任务信息解析执行单元
  def parse_task_info(task)
    LogService.info("[LocalTaskExecution] parse_task_info 被调用，task.task_info: #{task.task_info.inspect[0..1500]}")

    task_info = convert(task.task_info)

    LogService.info("[LocalTaskExecution] parse_task_info convert 结果: #{task_info.inspect[0..1000]}")

    return {} unless task_info && task_info.is_a?(Hash)

    # 获取未选择的设备和脚本列表
    unselected_devices = task.respond_to?(:unselected_device_ids_array) ? task.unselected_device_ids_array : []
    unselected_scripts = task.respond_to?(:unselected_script_ids_array) ? task.unselected_script_ids_array : []

    # 使用数据加载器一次性批量加载所有需要的数据
    loader = TaskExecutionDataLoader.new(task_info).load!

    build_execution_units(task_info, loader, unselected_devices, unselected_scripts)
  end
  # 获取执行单元 - 优先从文件读取
  def get_execution_units
    # 优先从文件读取最新状态
    file_data = load_status_from_file
    if file_data && file_data.is_a?(Hash)
      return file_data['execution_units'] || file_data[:execution_units] || execution_units || {}
    end

    # 文件没有的话从数据库读取
    execution_units || {}
  end
  # 更新脚本执行状态 - 只保存到文件，不保存数据库
  def update_script_status!(device_id, script_id, status, **options)
    units = get_execution_units
    
    # 兼容符号键和字符串键
    device_key = units.key?(device_id) ? device_id : device_id.to_s
    return unless units[device_key]
    
    scripts = units[device_key]
    scripts_key = scripts.key?('scripts') ? 'scripts' : :scripts
    return unless scripts[scripts_key]
    
    script_list = scripts[scripts_key]
    script_key = script_list.key?(script_id) ? script_id : script_id.to_s
    return unless script_list[script_key]
    
    script_info = script_list[script_key]
    
    # 使用字符串键更新
    script_info['status'] = status
    
    case status
    when 'running'
      script_info['started_at'] = Time.now.iso8601
    when 'completed', 'failed'
      script_info['completed_at'] = Time.now.iso8601
      script_info['conclusion'] = options[:conclusion] if options[:conclusion]
      script_info['output'] = options[:output] if options[:output]
      script_info['error'] = options[:error] if options[:error]
    end
    
    # 只保存到文件，不保存数据库
    self.execution_units = units
    save_status_to_file
  end
  

  
  
  # 构建执行单元结构
  def build_execution_units(task_info, loader, unselected_devices = [], unselected_scripts = [])
    execution_units = {}

    devices = task_info['devices'] || task_info[:devices] || []
    scripts = task_info['scripts'] || task_info[:scripts] || []

    devices.each do |device_data|
      device_id = device_data['id'] || device_data[:id]
      next unless device_id

      # 跳过未选择的设备
      device_id_str = device_id.to_s
      next if unselected_devices.include?(device_id_str)

      # 从批量加载的hash中获取设备
      local_device = loader.get_device(device_id)

      execution_units[device_id] = build_device_unit(device_data, local_device)

      # 为每个设备添加脚本（过滤未选择的脚本）
      add_scripts_to_device(execution_units[device_id], scripts, loader, device_id, unselected_scripts)
    end

    execution_units
  end
  
  # 构建单个网元单元
  def build_device_unit(device_data, local_device)
    device_ip = local_device&.ip_address || (device_data['device_ip'] || device_data[:device_ip])
    {
      id: device_data['id'] || device_data[:id],
      name: device_data['name'] || device_data[:name],
      device_model: device_data['device_model'] || device_data[:device_model],
      device_ip: device_ip,
      description: local_device&.description || device_data['description'] || device_data[:description],
      connection_info: local_device ? build_device_connection_info(local_device) : {},
      login_log_path: generate_login_log_path(device_ip),
      scripts: {}
    }
  end
  
  # 为网元添加脚本
  def add_scripts_to_device(device_unit, scripts, loader, device_id, unselected_scripts = [])
    scripts.each do |script_data|
      script_id = script_data['id'] || script_data[:id] || SecureRandom.uuid

      # 跳过未选择的脚本（格式：device_id-script_id）
      combined_id = "#{device_id}-#{script_id}"
      next if unselected_scripts.include?(combined_id)

      # 从批量加载的hash中获取脚本
      cached_script = loader.get_script(script_id)
      # binding.pry
      device_unit[:scripts][script_id] = build_script_unit(
        script_data,
        cached_script,
        device_id,
        script_id,
        device_unit
      )
    end
  end
  
  # 构建单个脚本单元
  def build_script_unit(script_data, cached_script, device_id, script_id, device_unit)
    {
      id: script_id,
      name: script_data['name'] || script_data[:name] || "Script #{script_id[0..7]}",
      script_name: script_data['name'] || script_data[:name] || "Script #{script_id[0..7]}",
      description: script_data['description'] || script_data[:description],
      code: cached_script&.code || script_data['code'] || script_data[:code],
      log_file_path: generate_file_based_log_path(device_id, script_id, device_unit, script_data),
      started_at: nil,
      completed_at: nil,
      status: 'pending',
      conclusion: nil,
      output: nil,
      error: nil
    }
  end
  
  # 构建设备连接信息（包含解密后的密码）
  def build_device_connection_info(device)
    {
      host: device.ip_address,
      port: device.port,
      connection_type: device.connection_type,
      username: device.username,
      password: device.decrypt_password,
      enable_command: device.enable_command,
      enable_password: device.decrypt_enable_password,
      root_password: device.respond_to?(:decrypt_root_password) ? device.decrypt_root_password : nil,
      ssh_key_path: device.ssh_key_path
    }
  end
  
  # 生成基于文件结构的日志路径
  def generate_file_based_log_path(device_id, script_id, device_info, script_info)
    device_ip = device_info[:device_ip] || device_info['device_ip'] || device_id
    script_name = script_info[:name] || script_info['name'] || script_id
    
    FilePathUtils.devlog_file_path(task_dir_name, device_ip, script_name)
  end

  # 生成登录日志文件路径
  def generate_login_log_path(device_ip)
    FilePathUtils.login_log_file_path(task_dir_name, device_ip)
  end
  
  # 生成旧的日志文件路径（保持兼容性）
  def generate_log_file_path(device_id, script_id)
    log_base_dir = log_directory || './logs'
    File.join(log_base_dir, id, device_id, "#{script_id}.log")
  end


  
  # 初始化文件存储结构
  def initialize_file_storage
    root_dir = task_root_dir
    FilePathUtils.ensure_dir_exists(root_dir)
    # 重新解析任务信息（确保获取到最新同步的设备和脚本数据）
    # 因为设备/脚本可能在任务创建后才同步到本地
    units = parse_task_info(task)
    # 更新 execution_units，这样保存到文件时就有完整的连接信息和脚本代码了
    self.execution_units = units
    units.each do |device_id, device_info|
      device_ip = device_info['device_ip'] || device_info[:device_ip] || device_id
      scripts = device_info['scripts'] || device_info[:scripts] || {}

      scripts.each do |script_id, script_info|
        script_name = script_info['script_name'] || script_info[:script_name] || script_info['name'] || script_info[:name] || script_id

        # 确保脚本日志目录存在
        devlog_path = FilePathUtils.devlog_file_path(task_dir_name, device_ip, script_name)
        FilePathUtils.ensure_file_dir_exists(devlog_path)
      end
    end
    # 保存初始状态到文件（此时保存的是最新解析的数据，包含密码和脚本代码）
    save_status_to_file
  end
  
  # 保存状态到文件
  def save_status_to_file
    status_data = {
      execution_id: id,
      task_id: task_id,
      status: status,
      progress: progress,
      started_at: started_at&.iso8601,
      completed_at: completed_at&.iso8601,
      execution_units: execution_units,
      updated_at: Time.now.iso8601
    }
    
    file_path = task_json_status_file_path
    FilePathUtils.write_json_status(file_path, status_data)
  end
  
  # 从文件加载状态 - 返回 execution_units（保持向后兼容）
  def load_status_from_file
    file_path = task_json_status_file_path
    data = FilePathUtils.read_json_status(file_path)
    return nil unless data

    # 更新内存中的状态（使用set确保Sequel知道属性已修改）
    updates = {}
    updates[:status] = data['status'] if data['status']
    updates[:progress] = data['progress'] if data['progress']
    updates[:started_at] = Time.parse(data['started_at']) if data['started_at']
    updates[:completed_at] = Time.parse(data['completed_at']) if data['completed_at']
    updates[:execution_units] = data['execution_units'] if data['execution_units']

    set(updates) unless updates.empty?

    data['execution_units']
  end

  # 从文件加载完整状态 - 返回完整的 data hash
  def load_full_status_from_file
    file_path = task_json_status_file_path
    data = FilePathUtils.read_json_status(file_path)
    return nil unless data

    puts "[LocalTaskExecution] load_full_status_from_file, data keys: #{data.keys}"

    # 更新内存中的状态（使用set确保Sequel知道属性已修改）
    updates = {}
    updates[:status] = data['status'] if data['status']
    updates[:progress] = data['progress'] if data['progress']
    updates[:started_at] = Time.parse(data['started_at']) if data['started_at']
    updates[:completed_at] = Time.parse(data['completed_at']) if data['completed_at']
    updates[:execution_units] = data['execution_units'] if data['execution_units']

    set(updates) unless updates.empty?

    data
  end
  
  # 最终保存到数据库（任务结束时调用）
  def save_final_to_database
    # 先从文件加载最新状态
    file_data = load_full_status_from_file
    puts "[LocalTaskExecution] save_final_to_database, file_data: #{file_data ? 'present' : 'nil'}"
    # file_data 已经通过 load_full_status_from_file 更新到当前对象了

    # 保存到数据库
    save
  end
  
  # 转换为API响应格式
  def to_api_hash
    {
      id: id,
      task_id: task_id,
      status: status,
      status_text: status_text,
      progress: progress,
      log_directory: log_directory,
      started_at: started_at&.iso8601,
      completed_at: completed_at&.iso8601,
      created_at: created_at&.iso8601,
      updated_at: updated_at&.iso8601,
      execution_units: get_execution_units
    }
  end
end
