import { Provide, Scope, ScopeEnum, Logger } from '@midwayjs/core';
import { ILogger } from '@midwayjs/logger';
import * as fs from 'fs/promises';
import * as path from 'path';
import * as yaml from 'js-yaml';
export type SkillRuntime = 'node' | 'python' | 'bash' | 'dotnet';
export type SkillClassification = 'prompt' | 'compute-entry' | 'compute-toolkit';

export interface SkillInterfaceField {
  type: string;
  required?: boolean;
  default?: string;
  description?: string;
}

export interface SkillRouteRule {
  match: string[];
  required_refs: string[];
}

export interface SkillConfig {
  runtime?: SkillRuntime;
  entrypoint?: string;
  timeout?: number;
  env?: Array<{ name: string; required: boolean; description?: string; default?: string }>;
  dependencies?: {
    system?: Array<{ name: string; check: string }>;
    python?: { source?: string; inline?: string[] };
    node?: { packages?: string[] };
    dotnet?: { project?: string };
  };
  setup?: { posix?: string; win32?: string };
  interface?: {
    input?: Record<string, SkillInterfaceField>;
    output?: Record<string, SkillInterfaceField>;
  };
  references?: {
    required?: string[];
    optional?: string[];
    routes?: SkillRouteRule[];
  };
}

@Provide()
@Scope(ScopeEnum.Singleton)
export class SkillConfigService {
  @Logger()
  logger: ILogger;

  private configs: Map<string, SkillConfig> = new Map();
  private parseErrors: Map<string, string> = new Map();

  async loadConfig(skillDir: string, skillName: string): Promise<SkillConfig | null> {
    const configPath = path.join(skillDir, 'skill.config.yaml');
    try {
      const raw = await fs.readFile(configPath, 'utf-8');
      const config = yaml.load(raw) as SkillConfig;
      this.configs.set(skillName, config);
      this.parseErrors.delete(skillName);
      return config;
    } catch (e: any) {
      if (e?.code !== 'ENOENT') {
        this.logger.warn('[SkillConfig] Failed to parse %s: %s', configPath, e.message);
        this.parseErrors.set(skillName, e.message);
      }
      this.configs.delete(skillName);
      return null;
    }
  }

  getParseError(skillName: string): string | null {
    return this.parseErrors.get(skillName) || null;
  }

  getConfig(skillName: string): SkillConfig | null {
    return this.configs.get(skillName) || null;
  }

  classify(skillName: string): SkillClassification {
    const config = this.configs.get(skillName);
    if (!config) return 'prompt';
    if (config.entrypoint) return 'compute-entry';
    if (config.runtime) return 'compute-toolkit';
    return 'prompt';
  }

  hasComputeEntrySkills(skillNames: string[]): boolean {
    return skillNames.some(name => this.classify(name) === 'compute-entry');
  }

  clearAll(): void {
    this.configs.clear();
    this.parseErrors.clear();
  }
}
