import * as fs from 'fs';
import * as path from 'path';
import * as os from 'os';
import type {
  FilePermissionConfig,
  FilePermissionContext,
  PermissionCheckResult,
  PermissionDecision,
  PermissionContext,
} from '../types.js';
import type { PermissionChecker } from './base.js';

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

// 默认文件权限配置
const DEFAULT_CONFIG: FilePermissionConfig = {
  externalDirectory: 'ask',
  operations: {
    read: 'allow',     // 读取默认允许
    write: 'ask',      // 写入需要确认
    edit: 'ask',       // 编辑需要确认
    list: 'allow',     // 列目录默认允许
    search: 'allow',   // 搜索默认允许
    grep: 'allow',     // 内容搜索默认允许
    info: 'allow',     // 获取文件信息默认允许
    move: 'ask',       // 移动需要确认
    copy: 'ask',       // 复制需要确认
    delete: 'ask',     // 删除需要确认
    mkdir: 'ask',      // 创建目录需要确认
  },
  sensitivePaths: [
    // 系统关键路径 - 拒绝
    { pattern: '/etc/*', action: 'deny' },
    { pattern: '/usr/*', action: 'deny' },
    { pattern: '/bin/*', action: 'deny' },
    { pattern: '/sbin/*', action: 'deny' },
    { pattern: '/System/*', action: 'deny' },
    { pattern: '/var/*', action: 'deny' },
    { pattern: 'C:\\Windows\\*', action: 'deny' },
    { pattern: 'C:\\Program Files\\*', action: 'deny' },

    // 用户敏感文件 - 需要确认
    { pattern: '*/.ssh/*', action: 'ask' },
    { pattern: '*/.gnupg/*', action: 'ask' },
    { pattern: '*/.aws/*', action: 'ask' },
    { pattern: '*/.kube/*', action: 'ask' },
    { pattern: '*/.env', action: 'ask' },
    { pattern: '*/.env.*', action: 'ask' },
    { pattern: '*/credentials*', action: 'ask' },
    { pattern: '*/secrets*', action: 'ask' },
    { pattern: '*/.git/config', action: 'ask' },
  ],
};

/**
 * 文件操作权限检查器
 */
export class FilePermissionChecker implements PermissionChecker {
  readonly name = 'file';

  private config: FilePermissionConfig;
  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(): FilePermissionConfig {
    try {
      if (fs.existsSync(FILE_PERMISSION_FILE)) {
        const content = fs.readFileSync(FILE_PERMISSION_FILE, 'utf-8');
        const stored = JSON.parse(content) as Partial<FilePermissionConfig>;
        return {
          externalDirectory: stored.externalDirectory || DEFAULT_CONFIG.externalDirectory,
          operations: { ...DEFAULT_CONFIG.operations, ...stored.operations },
          sensitivePaths: stored.sensitivePaths || DEFAULT_CONFIG.sensitivePaths,
        };
      }
    } catch {
      // 忽略加载错误
    }
    return { ...DEFAULT_CONFIG, operations: { ...DEFAULT_CONFIG.operations } };
  }

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

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

  /**
   * 将通配符模式转换为正则表达式
   */
  private patternToRegex(pattern: string): RegExp {
    const escaped = pattern
      .replace(/[.+^${}()|[\]\\]/g, '\\$&')
      .replace(/\*/g, '.*')
      .replace(/\?/g, '.');
    return new RegExp(`^${escaped}$`, 'i');
  }

  /**
   * 检查路径是否匹配敏感路径规则
   */
  private matchSensitivePath(targetPath: string): { matched: boolean; action?: 'allow' | 'deny' | 'ask' } {
    const normalizedPath = targetPath.replace(/\\/g, '/');

    for (const rule of this.config.sensitivePaths) {
      const regex = this.patternToRegex(rule.pattern);
      if (regex.test(normalizedPath)) {
        return { matched: true, action: rule.action };
      }
    }

    return { matched: false };
  }

  /**
   * 展开路径中的 ~ 符号
   */
  private expandTilde(targetPath: string): string {
    if (targetPath.startsWith('~/')) {
      return path.join(os.homedir(), targetPath.slice(2));
    }
    if (targetPath === '~') {
      return os.homedir();
    }
    return targetPath;
  }

  /**
   * 检查文件操作权限
   */
  async checkFilePermission(ctx: FilePermissionContext): Promise<PermissionCheckResult> {
    const { operation, path: targetPath, workdir } = ctx;

    // 展开 ~ 并解析绝对路径
    const expandedPath = this.expandTilde(targetPath);
    const absolutePath = path.isAbsolute(expandedPath)
      ? expandedPath
      : path.resolve(workdir, expandedPath);

    // 生成会话权限 key
    const sessionKey = `${operation}:${absolutePath}`;

    // 1. 检查会话级别的临时权限
    const sessionPerm = this.sessionPermissions.get(sessionKey);
    if (sessionPerm === 'deny') {
      return {
        allowed: false,
        action: 'deny',
        reason: `本次会话已拒绝此操作: ${operation} ${absolutePath}`,
      };
    }
    if (sessionPerm === 'allow') {
      return {
        allowed: true,
        action: 'allow',
        reason: '本次会话已允许此操作',
      };
    }

    // 2. 检查敏感路径规则
    const sensitiveMatch = this.matchSensitivePath(absolutePath);
    if (sensitiveMatch.matched && sensitiveMatch.action) {
      if (sensitiveMatch.action === 'deny') {
        return {
          allowed: false,
          action: 'deny',
          reason: `路径匹配敏感路径规则，禁止访问: ${absolutePath}`,
        };
      }
      if (sensitiveMatch.action === 'ask') {
        return this.handleAskPermission(ctx, absolutePath, sessionKey, '敏感路径需要确认');
      }
      // allow 则继续检查
    }

    // 3. 检查外部目录访问
    if (!this.isInProjectDirectory(absolutePath)) {
      const extAction = this.config.externalDirectory;

      if (extAction === 'deny') {
        return {
          allowed: false,
          action: 'deny',
          reason: `禁止访问项目目录外的路径: ${absolutePath}`,
        };
      }

      if (extAction === 'ask') {
        return this.handleAskPermission(ctx, absolutePath, sessionKey, '访问项目外部路径需要确认');
      }
    }

    // 4. 根据操作类型的默认策略处理
    const operationAction = this.config.operations[operation];

    if (operationAction === 'allow') {
      return {
        allowed: true,
        action: 'allow',
        reason: `${operation} 操作默认允许`,
      };
    }

    if (operationAction === 'deny') {
      return {
        allowed: false,
        action: 'deny',
        reason: `${operation} 操作默认拒绝`,
      };
    }

    // operationAction === 'ask'
    return this.handleAskPermission(ctx, absolutePath, sessionKey, `${operation} 操作需要确认`);
  }

  /**
   * 处理需要询问的权限
   */
  private async handleAskPermission(
    ctx: FilePermissionContext,
    absolutePath: string,
    sessionKey: string,
    reason: string
  ): Promise<PermissionCheckResult> {
    // 如果没有回调，返回需要确认的状态
    if (!this.askCallback) {
      return {
        allowed: false,
        action: 'ask',
        needsConfirmation: true,
        reason,
        patterns: [absolutePath],
      };
    }

    // 构造兼容的 PermissionContext
    const permCtx: PermissionContext = {
      command: `${ctx.operation} ${ctx.path}`,
      workdir: ctx.workdir,
      patterns: [ctx.operation],
      externalPaths: this.isInProjectDirectory(absolutePath) ? undefined : [absolutePath],
    };

    const decision = await this.askCallback(permCtx);

    if (decision.remember) {
      this.sessionPermissions.set(sessionKey, decision.allow ? 'allow' : 'deny');
    }

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

  /**
   * 实现 PermissionChecker 接口的 check 方法
   * 从 PermissionContext 提取文件操作信息
   */
  async check(ctx: PermissionContext): Promise<PermissionCheckResult> {
    // 从 command 解析操作类型和路径
    // 格式: "operation path" 如 "read /path/to/file"
    const parts = ctx.command.split(' ');
    const operation = parts[0] as FilePermissionContext['operation'];
    const targetPath = parts.slice(1).join(' ');

    return this.checkFilePermission({
      operation,
      path: targetPath,
      workdir: ctx.workdir,
    });
  }

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

  /**
   * 获取当前配置
   */
  getConfig(): FilePermissionConfig {
    return {
      ...this.config,
      operations: { ...this.config.operations },
      sensitivePaths: [...this.config.sensitivePaths],
    };
  }
}
