/**
 * @license
 * Copyright 2025 Google LLC
 * SPDX-License-Identifier: Apache-2.0
 */

import * as fs from 'node:fs';
import * as path from 'node:path';
import { Storage } from '../config/storage.js';
import {
  getHookKey,
  HookType,
  type HookDefinition,
  type HookEventName,
} from './types.js';
import { debugLogger } from '../utils/debugLogger.js';

interface TrustedHooksConfig {
  [projectPath: string]: string[]; // Array of trusted hook keys (name:command)
}

export class TrustedHooksManager {
  private configPath: string;
  private trustedHooks: TrustedHooksConfig = {};

  constructor() {
    this.configPath = path.join(
      Storage.getGlobalGeminiDir(),
      'trusted_hooks.json',
    );
    this.load();
  }

  private load(): void {
    try {
      if (fs.existsSync(this.configPath)) {
        const content = fs.readFileSync(this.configPath, 'utf-8');
        // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
        this.trustedHooks = JSON.parse(content);
      }
    } catch (error) {
      debugLogger.warn('Failed to load trusted hooks config', error);
      this.trustedHooks = {};
    }
  }

  private save(): void {
    try {
      const dir = path.dirname(this.configPath);
      if (!fs.existsSync(dir)) {
        fs.mkdirSync(dir, { recursive: true });
      }
      fs.writeFileSync(
        this.configPath,
        JSON.stringify(this.trustedHooks, null, 2),
      );
    } catch (error) {
      debugLogger.warn('Failed to save trusted hooks config', error);
    }
  }

  /**
   * Get untrusted hooks for a project
   * @param projectPath Absolute path to the project root
   * @param hooks The hooks configuration to check
   * @returns List of untrusted hook commands/names
   */
  getUntrustedHooks(
    projectPath: string,
    hooks: { [K in HookEventName]?: HookDefinition[] },
  ): string[] {
    const trustedKeys = new Set(this.trustedHooks[projectPath] || []);
    const untrusted: string[] = [];

    for (const eventName of Object.keys(hooks)) {
      // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
      const definitions = hooks[eventName as HookEventName];
      if (!Array.isArray(definitions)) continue;

      for (const def of definitions) {
        if (!def || !Array.isArray(def.hooks)) continue;
        for (const hook of def.hooks) {
          if (hook.type === HookType.Runtime) continue;
          const key = getHookKey(hook);
          if (!trustedKeys.has(key)) {
            // Return friendly name or command
            untrusted.push(hook.name || hook.command || 'unknown-hook');
          }
        }
      }
    }

    return Array.from(new Set(untrusted)); // Deduplicate
  }

  /**
   * Trust all provided hooks for a project
   */
  trustHooks(
    projectPath: string,
    hooks: { [K in HookEventName]?: HookDefinition[] },
  ): void {
    const currentTrusted = new Set(this.trustedHooks[projectPath] || []);

    for (const eventName of Object.keys(hooks)) {
      // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion
      const definitions = hooks[eventName as HookEventName];
      if (!Array.isArray(definitions)) continue;

      for (const def of definitions) {
        if (!def || !Array.isArray(def.hooks)) continue;
        for (const hook of def.hooks) {
          if (hook.type === HookType.Runtime) continue;
          currentTrusted.add(getHookKey(hook));
        }
      }
    }

    this.trustedHooks[projectPath] = Array.from(currentTrusted);
    this.save();
  }
}
