#!/usr/bin/env ruby
# frozen_string_literal: true

# 升级包制作工具
# 独立的Sinatra应用，用于制作升级压缩包

require 'sinatra'
require 'zip'
require 'json'
require 'fileutils'
require 'securerandom'

# 配置
set :port, 4568  # 使用不同端口避免与主应用冲突
set :bind, '0.0.0.0'
set :views, File.join(File.dirname(__FILE__), 'views')

# 升级包存放目录
UPGRADE_OUTPUT_DIR = './data/upgrade_packages'.freeze
FileUtils.mkdir_p(UPGRADE_OUTPUT_DIR)

# 生成manifest
def build_manifest(name, version, files)
  {
    name: name,
    version: version,
    created_at: Time.now.iso8601,
    files: files.map do |f|
      {
        source: f['source'],
        target: f['target'],
        description: f['description'] || ''
      }
    end
  }
end

# 递归添加文件到ZIP
def add_files_to_zip(zipfile, base_dir, relative_path)
  full_path = File.join(base_dir, relative_path)

  if File.directory?(full_path)
    # 添加目录自身
    unless relative_path.empty?
      zipfile.add(relative_path, full_path)
    end
    # 遍历目录内容
    Dir.entries(full_path).reject { |e| e.start_with?('.') }.each do |entry|
      new_path = relative_path.empty? ? entry : "#{relative_path}/#{entry}"
      add_files_to_zip(zipfile, base_dir, new_path)
    end
  else
    zipfile.add(relative_path, full_path)
  end
end

# 创建升级包
def create_upgrade_package(name, version, files)
  package_id = SecureRandom.uuid
  temp_dir = File.join('./tmp/upgrade_create', package_id)
  FileUtils.mkdir_p(temp_dir)

  begin
    # 复制文件到临时目录
    files.each do |file_info|
      source_path = file_info['source_path']
      dest_relative = file_info['source']
      dest_path = File.join(temp_dir, dest_relative)

      next if source_path.nil? || source_path.empty?
      next unless File.exist?(source_path)

      FileUtils.mkdir_p(File.dirname(dest_path))
      if File.directory?(source_path)
        # 目录：递归复制
        FileUtils.cp_r(source_path, dest_path)
      else
        # 文件：直接复制
        FileUtils.cp(source_path, dest_path)
      end
    end

    # 生成manifest.json
    manifest = build_manifest(name, version, files)
    File.write(File.join(temp_dir, 'manifest.json'), JSON.pretty_generate(manifest))

    # 创建ZIP包
    zip_path = File.join(UPGRADE_OUTPUT_DIR, "#{package_id}.zip")
    Zip::File.open(zip_path, Zip::File::CREATE) do |zipfile|
      # 添加manifest
      zipfile.add('manifest.json', File.join(temp_dir, 'manifest.json'))

      # 添加用户选择的文件（递归）
      files.each do |f|
        add_files_to_zip(zipfile, temp_dir, f['source'])
      end
    end

    {
      success: true,
      package_id: package_id,
      zip_path: zip_path,
      filename: "#{name}_v#{version}.zip"
    }
  rescue StandardError => e
    { success: false, error: e.message }
  ensure
    FileUtils.rm_rf(temp_dir) if temp_dir
  end
end

get '/' do
  erb :update_tool, layout: false
end

post '/api/create' do
  content_type :json

  begin
    data = JSON.parse(request.body.read)
    name = data['name'] || 'upgrade_package'
    version = data['version'] || '1.0.0'
    files = data['files'] || []

    if files.empty?
      return { success: false, error: '请至少添加一个文件' }.to_json
    end

    # 验证文件是否存在
    files.each do |f|
      unless File.exist?(f['source_path'])
        return { success: false, error: "文件不存在: #{f['source_path']}" }.to_json
      end
    end

    result = create_upgrade_package(name, version, files)

    if result[:success]
      {
        success: true,
        package_id: result[:package_id],
        download_url: "/api/download/#{result[:package_id]}?filename=#{result[:filename]}"
      }.to_json
    else
      status 400
      { success: false, error: result[:error] }.to_json
    end
  rescue JSON::ParserError
    status 400
    { success: false, error: '无效的JSON格式' }.to_json
  end
end

get '/api/download/:package_id' do
  package_id = params[:package_id]
  filename = params[:filename] || 'upgrade_package.zip'

  zip_path = File.join(UPGRADE_OUTPUT_DIR, "#{package_id}.zip")

  if File.exist?(zip_path)
    send_file zip_path, filename: filename, type: 'application/zip'
  else
    status 404
    { error: '升级包不存在' }.to_json
  end
end

# 获取根目录下的文件和目录
get '/api/root-directory' do
  content_type :json

  base_path = '.'
  # 排除不需要显示的目录
  exclude_dirs = ['vendor', 'tmp', 'log', '.git', '.bundle', 'node_modules', 'data', 'db', 'AGENTS.md', 'CLAUDE.md', 'Dockerfile',
  'Dockerfile.dev','Gemfile', 'Gemfile.lock', 'ai_custom_client.md'
  ]

  entries = Dir.entries(base_path).reject do |e|
    e.start_with?('.') || exclude_dirs.include?(e)
  end.sort

  result = entries.map do |name|
    entry_path = File.join(base_path, name)
    entry_size = File.directory?(entry_path) ? 0 : (File.size(entry_path) rescue 0)
    {
      name: name,
      type: File.directory?(entry_path) ? 'directory' : 'file',
      size: entry_size
    }
  end

  result.to_json
end