import { Provide, Inject, Get, Post, Body, Query } from '@midwayjs/core';
import { CoolController, BaseController } from '@cool-midway/core';
import { Context } from '@midwayjs/koa';
import * as fs from 'fs/promises';
import * as path from 'path';
import { NetaClawSkillEntity } from '../../entity/skill.js';
import { SkillLoaderService } from '../../service/skill_loader.js';
import { SkillInstallerService } from '../../service/skill_installer.js';
import { SkillSecretService } from '../../service/skill_secret.js';

@Provide()
@CoolController({
  api: ['info', 'list', 'page'],
  entity: NetaClawSkillEntity,
  pageQueryOp: {
    keyWordLikeFields: ['name', 'label', 'description'],
    fieldEq: ['status', 'source', 'skillType'],
    addOrderBy: { createTime: 'DESC' },
  },
})
export class AdminNetaClawSkillController extends BaseController {
  @Inject()
  skillLoader: SkillLoaderService;

  @Inject()
  skillInstaller: SkillInstallerService;

  @Inject()
  skillSecret: SkillSecretService;

  @Inject()
  ctx: Context;

  @Get('/metas', { summary: '获取已启用 skill 元数据快照' })
  async metas() {
    await this.skillLoader.scanSkills();
    return this.ok(await this.skillLoader.getSkillMetas());
  }

  @Post('/setStatus', { summary: '启用/禁用 skill' })
  async setStatus(@Body() body: { name: string; status: number }) {
    await this.skillLoader.setSkillStatus(body.name, body.status);
    return this.ok();
  }

  @Post('/install', { summary: '安装 skill (GitHub)' })
  async install(@Body() body: { url: string; branch?: string; version?: string }) {
    try {
      const meta = await this.skillInstaller.installFromGitHub(
        body.url,
        { branch: body.branch, tag: body.version },
      );
      return this.ok(meta);
    } catch (e: any) {
      return this.fail(e.message);
    }
  }

  @Post('/uploadInstall', { summary: '上传 ZIP 安装 skill' })
  async uploadInstall() {
    try {
      const files = (this.ctx as any).files;
      if (!files || files.length === 0) {
        return this.fail('请上传 ZIP 文件');
      }
      const file = files[0];
      // 校验文件类型
      const fileName: string = file.filename || file.originalname || '';
      if (!fileName.toLowerCase().endsWith('.zip')) {
        return this.fail('仅支持 .zip 文件');
      }
      // 读取文件内容
      const buffer: Buffer = typeof file.data === 'string'
        ? await fs.readFile(file.data)
        : file.data;
      const meta = await this.skillInstaller.installFromZip(buffer);
      return this.ok(meta);
    } catch (e: any) {
      return this.fail(e.message);
    }
  }

  @Post('/uninstall', { summary: '卸载 skill' })
  async uninstall(@Body() body: { name: string }) {
    try {
      await this.skillInstaller.uninstall(body.name);
      return this.ok();
    } catch (e: any) {
      return this.fail(e.message);
    }
  }

  @Post('/upgrade', { summary: '更新 skill (重新拉取)' })
  async upgrade(@Body() body: { name: string }) {
    try {
      const meta = await this.skillInstaller.update(body.name);
      return this.ok(meta);
    } catch (e: any) {
      return this.fail(e.message);
    }
  }

  @Get('/content', { summary: '按需获取完整 SKILL.md 内容' })
  async content(@Query('name') name: string) {
    const content = this.skillLoader.getSkillContent(name);
    if (!content) return this.fail('Skill 不存在');
    return this.ok({ name, content });
  }

  @Get('/files', { summary: '获取 skill 附属文件列表' })
  async files(@Query('name') name: string) {
    const files = this.skillLoader.getSkillFiles(name);
    return this.ok({ name, files });
  }

  @Get('/fileContent', { summary: '获取 skill 附属文件内容' })
  async fileContent(@Query('name') name: string, @Query('path') filePath: string) {
    const absPath = this.skillLoader.getSkillFilePath(name, filePath);
    if (!absPath) return this.fail('文件不存在或路径无效');
    try {
      const content = await fs.readFile(absPath, 'utf-8');
      return this.ok({ name, path: filePath, content });
    } catch {
      return this.fail('读取文件失败');
    }
  }

  @Post('/checkUpdates', { summary: '检查所有 skill 是否有更新' })
  async checkUpdates() {
    const results = await this.skillInstaller.checkUpdates();
    return this.ok(results);
  }

  @Post('/installDeps', { summary: '手动触发依赖安装' })
  async installDeps(@Body() body: { name: string }) {
    try {
      const result = await this.skillInstaller.installDependencies(body.name);
      return this.ok(result);
    } catch (e: any) {
      return this.fail(e.message);
    }
  }

  @Post('/create', { summary: 'Agent 自主创建 skill' })
  async create(@Body() body: { name: string; content: string }) {
    try {
      const skillsDir = this.skillLoader.getSkillsDir();
      const targetDir = path.join(skillsDir, body.name);
      // 路径安全检查
      const realSkillsDir = await fs.realpath(skillsDir).catch(() => skillsDir);
      const realTarget = path.resolve(realSkillsDir, body.name);
      if (!realTarget.startsWith(realSkillsDir)) {
        return this.fail('路径安全检查失败');
      }
      await fs.mkdir(targetDir, { recursive: true });
      await fs.writeFile(path.join(targetDir, 'SKILL.md'), body.content, 'utf-8');
      await this.skillLoader.scanSkills();
      return this.ok();
    } catch (e: any) {
      return this.fail(e.message);
    }
  }

  @Post('/edit', { summary: 'Agent 自主编辑 skill' })
  async edit(@Body() body: { name: string; content: string }) {
    return this.create(body);
  }

  @Get('/envSchema', { summary: '获取 skill 的 env 声明和配置状态' })
  async envSchema(@Query('name') name: string) {
    if (!name) return this.fail('缺少 name 参数');
    const keys = await this.skillSecret.getConfiguredKeys(name);
    const schema = await this.skillSecret.getEnvSchema(name);
    return this.ok({ name, schema, configured: keys });
  }

  @Post('/secrets', { summary: '保存 skill secrets' })
  async saveSecrets(@Body() body: { name: string; secrets: Record<string, string> }) {
    if (!body.name || !body.secrets) return this.fail('缺少 name 或 secrets');
    try {
      await this.skillSecret.saveSecrets(body.name, body.secrets);
      return this.ok();
    } catch (e: any) {
      return this.fail(e.message);
    }
  }

  @Get('/diagnostics', { summary: '获取 skill 诊断信息' })
  async diagnostics(@Query('level') level?: string) {
    const diags = this.skillLoader.getDiagnostics(level);
    return this.ok(diags);
  }
}
