import { Disposable } from "../utils/dispose";
import * as vscode from "vscode";

export const enum ConfigKey {
  GitHookEnabled = "gitHookEnabled",
  AutoOpenOptimizationViewOnComplete = "autoOpenOptimizationViewOnComplete",
  DefaultInterpreterPath = "defaultInterpreterPath",
  configFilePath = "configFilePath", // if not set, the LSP server will try to find it automatically
}

export class ConfigService extends Disposable {
  private currentConfig: Map<ConfigKey, unknown> = new Map();
  private reloading: boolean = false;
  private configListener?: vscode.Disposable;
  constructor() {
    super();
    // Initial load
    this.loadMemoryConfig(vscode.workspace.getConfiguration("codeflash"));
  }

  public listenForConfigChanges(): void {
    if (this.configListener) {
      return; // already listening
    }
    // Listen for configuration changes from VS Code (workspace/user)
    this.configListener = vscode.workspace.onDidChangeConfiguration((event) => {
      if (event.affectsConfiguration("codeflash")) {
        const config = vscode.workspace.getConfiguration("codeflash");
        const prevInterpreter = this.currentConfig.get(
          ConfigKey.DefaultInterpreterPath,
        ) as string | null;
        const prevPyproject = this.currentConfig.get(
          ConfigKey.configFilePath,
        ) as string | null;

        // If certain keys changed, you can reload or react here
        if (
          config.get<string>(ConfigKey.DefaultInterpreterPath) !==
          prevInterpreter
        ) {
          this.scheduleReload();
        }

        if (config.get<string>(ConfigKey.configFilePath) !== prevPyproject) {
          this.scheduleReload();
        }

        this.loadMemoryConfig(config);
      }
    });
  }

  private scheduleReload(delay: number = 600): void {
    if (this.reloading) {
      return;
    }
    this.reloading = true;
    setTimeout(() => {
      if (!this.isDisposed) {
        vscode.commands.executeCommand("codeflash.reloadExtension");
        this.configListener?.dispose();
      }
    }, delay);
  }

  /**
   * Loads the current configuration from VS Code into memory.
   */
  public loadMemoryConfig(cfg: vscode.WorkspaceConfiguration) {
    this.currentConfig.set(
      ConfigKey.GitHookEnabled,
      cfg.get<boolean>(ConfigKey.GitHookEnabled, true),
    );
    this.currentConfig.set(
      ConfigKey.AutoOpenOptimizationViewOnComplete,
      cfg.get<boolean>(ConfigKey.AutoOpenOptimizationViewOnComplete, false),
    );
    this.currentConfig.set(
      ConfigKey.DefaultInterpreterPath,
      cfg.get<string | null>(ConfigKey.DefaultInterpreterPath, null),
    );
    this.currentConfig.set(
      ConfigKey.configFilePath,
      cfg.get<string | null>(ConfigKey.configFilePath, null),
    );
  }

  /**
   * Retrieves a configuration value from memory.
   */
  public get(key: ConfigKey): unknown {
    return (
      this.currentConfig.get(key) ||
      vscode.workspace.getConfiguration("codeflash").get(key)
    );
  }

  /**
   * Updates a configuration value in VS Code settings (workspace-aware).
   * The update is written back to the same scope (workspace/global) where it was originally set.
   */
  public async set(
    key: ConfigKey,
    value: unknown,
    target: vscode.ConfigurationTarget = vscode.ConfigurationTarget.Global,
  ): Promise<void> {
    const config = vscode.workspace.getConfiguration("codeflash");
    const inspect = config.inspect(key);

    // Detect which level the setting was defined at
    const finalTarget =
      target ??
      (inspect?.workspaceFolderValue !== undefined
        ? vscode.ConfigurationTarget.WorkspaceFolder
        : inspect?.workspaceValue !== undefined
          ? vscode.ConfigurationTarget.Workspace
          : vscode.ConfigurationTarget.Global);

    await config.update(key, value, finalTarget);

    // Reload in-memory configuration to stay consistent
    this.loadMemoryConfig(vscode.workspace.getConfiguration("codeflash"));
  }
  public override dispose(): void {
    super.dispose();
    if (this.configListener) {
      this.configListener.dispose();
    }
  }
}
