import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import type {
  BashPermissionConfig,
  PermissionContext,
  PermissionCheckResult,
  PermissionDecision,
  PermissionRule,
} from '../types.js';
import type { PermissionChecker } from './base.js';
import { matchRulesAsync } from '../wildcard.js';
import { parseBashCommand } from '../bash-parser.js';

const CONFIG_DIR = path.join(os.homedir(), '.ai-terminal-assistant');
const PERMISSION_FILE = path.join(CONFIG_DIR, 'permissions.json');

// 默认权限配置
const DEFAULT_CONFIG: BashPermissionConfig = {
  rules: [
    // 默认允许的安全命令
    { pattern: 'ls *', action: 'allow' },
    { pattern: 'cat *', action: 'allow' },
    { pattern: 'head *', action: 'allow' },
    { pattern: 'tail *', action: 'allow' },
    { pattern: 'grep *', action: 'allow' },
    { pattern: 'find *', action: 'allow' },
    { pattern: 'echo *', action: 'allow' },
    { pattern: 'pwd', action: 'allow' },
    { pattern: 'which *', action: 'allow' },
    { pattern: 'type *', action: 'allow' },
    { pattern: 'git status', action: 'allow' },
    { pattern: 'git log *', action: 'allow' },
    { pattern: 'git diff *', action: 'allow' },
    { pattern: 'git branch *', action: 'allow' },
    { pattern: 'npm list *', action: 'allow' },
    { pattern: 'npm run *', action: 'ask' },

    // 需要确认的命令
    { pattern: 'git push *', action: 'ask' },
    { pattern: 'git commit *', action: 'ask' },
    { pattern: 'git checkout *', action: 'ask' },
    { pattern: 'git reset *', action: 'ask' },
    { pattern: 'npm install *', action: 'ask' },
    { pattern: 'npm uninstall *', action: 'ask' },
    { pattern: 'yarn *', action: 'ask' },
    { pattern: 'pnpm *', action: 'ask' },

    // 危险命令 - 默认拒绝
    { pattern: 'rm -rf *', action: 'deny' },
    { pattern: 'rm -r *', action: 'ask' },
    { pattern: 'sudo *', action: 'deny' },
    { pattern: 'chmod 777 *', action: 'deny' },
    { pattern: 'mkfs *', action: 'deny' },
    { pattern: 'dd *', action: 'deny' },
    { pattern: '> /dev/*', action: 'deny' },
  ],
  externalDirectory: 'ask',
  default: 'ask',
};

/**
 * Bash 命令权限检查器
 * 使用 tree-sitter 解析命令并检查权限
 */
export class BashPermissionChecker implements PermissionChecker {
  readonly name = 'bash';

  private config: BashPermissionConfig;
  private projectRoot: string;
  private askCallback?: (ctx: PermissionContext) => Promise<PermissionDecision>;
  private sessionPermissions = new Map<string, 'allow' | 'deny'>();

  constructor(projectRoot: string = process.cwd()) {
    this.projectRoot = path.resolve(projectRoot);
    this.config = this.loadConfig();
  }

  /**
   * 设置权限询问回调
   */
  setAskCallback(callback: (ctx: PermissionContext) => Promise<PermissionDecision>): void {
    this.askCallback = callback;
  }

  /**
   * 加载权限配置
   */
  private loadConfig(): BashPermissionConfig {
    try {
      if (fs.existsSync(PERMISSION_FILE)) {
        const content = fs.readFileSync(PERMISSION_FILE, 'utf-8');
        const stored = JSON.parse(content) as Partial<BashPermissionConfig>;
        return {
          rules: stored.rules || DEFAULT_CONFIG.rules,
          externalDirectory: stored.externalDirectory || DEFAULT_CONFIG.externalDirectory,
          default: stored.default || DEFAULT_CONFIG.default,
        };
      }
    } catch {
      // 忽略加载错误
    }
    return { ...DEFAULT_CONFIG };
  }

  /**
   * 保存权限配置
   */
  saveConfig(): void {
    if (!fs.existsSync(CONFIG_DIR)) {
      fs.mkdirSync(CONFIG_DIR, { recursive: true });
    }
    fs.writeFileSync(PERMISSION_FILE, JSON.stringify(this.config, null, 2));
  }

  /**
   * 添加权限规则
   */
  addRule(rule: PermissionRule): void {
    const existingIndex = this.config.rules.findIndex(r => r.pattern === rule.pattern);
    if (existingIndex >= 0) {
      this.config.rules[existingIndex] = rule;
    } else {
      this.config.rules.unshift(rule);
    }
    this.saveConfig();
  }

  /**
   * 检查路径是否在项目目录内
   */
  private isInProjectDirectory(targetPath: string): boolean {
    const resolved = path.resolve(targetPath);
    return resolved.startsWith(this.projectRoot + path.sep) || resolved === this.projectRoot;
  }

  /**
   * 从解析后的命令中提取可能的外部路径
   */
  private async extractPathsFromCommand(command: string, workdir: string): Promise<string[]> {
    const externalPaths: string[] = [];
    const parseResult = await parseBashCommand(command);

    const pathCommands = new Set(['cd', 'rm', 'cp', 'mv', 'mkdir', 'touch', 'chmod', 'chown', 'cat', 'ls']);

    for (const cmd of parseResult.commands) {
      if (!pathCommands.has(cmd.name)) continue;

      const pathsToCheck = [cmd.subcommand, ...cmd.args].filter(Boolean) as string[];

      for (const arg of pathsToCheck) {
        if (arg.startsWith('-')) continue;

        let resolved: string | null = null;

        if (arg.startsWith('/')) {
          resolved = arg;
        } else if (arg.startsWith('~')) {
          resolved = arg.replace('~', os.homedir());
        } else if (arg.includes('..') || arg.includes('/')) {
          resolved = path.resolve(workdir, arg);
        }

        if (resolved && !this.isInProjectDirectory(resolved)) {
          externalPaths.push(resolved);
        }
      }
    }

    return [...new Set(externalPaths)];
  }

  /**
   * 检查命令权限
   */
  async check(ctx: PermissionContext): Promise<PermissionCheckResult> {
    const { command, workdir } = ctx;

    // 1. 使用 tree-sitter 解析命令并检查权限
    const parseResult = await matchRulesAsync(command, this.config.rules, this.config.default);

    // 2. 检查会话级别的临时权限
    for (const pattern of parseResult.askPatterns) {
      const sessionPerm = this.sessionPermissions.get(pattern);
      if (sessionPerm === 'deny') {
        return {
          allowed: false,
          action: 'deny',
          reason: `本次会话已拒绝此类命令: ${pattern}`,
        };
      }
      if (sessionPerm === 'allow') {
        const allAllowed = parseResult.askPatterns.every(p => this.sessionPermissions.get(p) === 'allow');
        if (allAllowed && parseResult.action === 'ask') {
          return {
            allowed: true,
            action: 'allow',
            reason: '本次会话已允许此类命令',
          };
        }
      }
    }

    // 3. 检查外部目录访问
    const externalPaths = await this.extractPathsFromCommand(command, workdir);
    if (externalPaths.length > 0) {
      const extAction = this.config.externalDirectory;

      if (extAction === 'deny') {
        return {
          allowed: false,
          action: 'deny',
          reason: `命令访问项目目录外的路径: ${externalPaths.join(', ')}`,
        };
      }

      if (extAction === 'ask') {
        if (!this.askCallback) {
          return {
            allowed: false,
            action: 'ask',
            needsConfirmation: true,
            reason: `命令访问项目目录外的路径`,
            patterns: externalPaths,
          };
        }

        const decision = await this.askCallback({
          ...ctx,
          externalPaths,
        });

        if (!decision.allow) {
          return {
            allowed: false,
            action: 'deny',
            reason: '用户拒绝访问外部目录',
          };
        }
      }
    }

    // 4. 根据解析结果处理权限
    const { action, matchedPattern, allCommands, askPatterns } = parseResult;

    if (action === 'allow') {
      return {
        allowed: true,
        action: 'allow',
        reason: matchedPattern ? `匹配规则: ${matchedPattern}` : '默认允许',
      };
    }

    if (action === 'deny') {
      return {
        allowed: false,
        action: 'deny',
        reason: matchedPattern
          ? `匹配规则: ${matchedPattern}`
          : `包含被拒绝的命令: ${allCommands.map(c => c.name).join(', ')}`,
      };
    }

    // action === 'ask'
    if (!this.askCallback) {
      return {
        allowed: false,
        action: 'ask',
        needsConfirmation: true,
        patterns: askPatterns,
      };
    }

    const decision = await this.askCallback({
      ...ctx,
      patterns: askPatterns,
    });

    if (decision.remember) {
      for (const pattern of askPatterns) {
        this.sessionPermissions.set(pattern, decision.allow ? 'allow' : 'deny');
      }
    }

    return {
      allowed: decision.allow,
      action: decision.allow ? 'allow' : 'deny',
      reason: decision.allow ? '用户允许' : '用户拒绝',
    };
  }

  /**
   * 清除会话权限
   */
  clearSessionPermissions(): void {
    this.sessionPermissions.clear();
  }

  /**
   * 获取当前配置
   */
  getConfig(): BashPermissionConfig {
    return { ...this.config };
  }
}
