import * as vscode from "vscode";
import type { LanguageClient } from "vscode-languageclient/node";
import {
  State as LanguageClientState,
  ResponseError,
} from "vscode-languageclient/node";
import type { OptimizableFunctionsResponse } from "../types";
import { LSP_COMMANDS } from "../constants";
import { getFileNameFromUri, getFileNameFromUriString, Logger } from "../utils";
import type { SidebarStatus } from "@codeflash/types";

export class AnalysisService {
  private logger: Logger;

  constructor(private client: LanguageClient) {
    this.logger = new Logger("Codeflash Analysis");
  }

  async getOptimizableFunctions(uri: vscode.Uri): Promise<{
    status: SidebarStatus;
    message: string;
    functions: string[];
  }> {
    const uriString = uri.toString();
    const fileName = getFileNameFromUri(uri) || "Unknown";

    if (this.client.state !== LanguageClientState.Running) {
      this.logger.error(
        `LSP client not running for analysis of ${fileName}. State: ${this.client.state}`,
      );
      return {
        status: "serverError",
        message: "Cannot analyze: Language Server not running.",
        functions: [],
      };
    }

    this.logger.debug(
      `Requesting analysis for: ${fileName} with command: ${LSP_COMMANDS.GET_OPTIMIZABLE_FUNCTIONS}`,
    );

    try {
      const requestParams = { textDocument: { uri: uriString } };
      this.logger.debug(`Request params: ${JSON.stringify(requestParams)}`);

      const result = await this.client.sendRequest<
        OptimizableFunctionsResponse | null | undefined
      >(LSP_COMMANDS.GET_OPTIMIZABLE_FUNCTIONS, requestParams);

      this.logger.debug(
        `Received response for ${fileName}: ${JSON.stringify(result)}`,
      );
      return this.processAnalysisResult(result, uriString, fileName);
    } catch (error) {
      this.logger.error(
        `Error requesting analysis for ${fileName}`,
        error instanceof Error ? error : undefined,
      );
      if (error instanceof Error) {
        this.logger.error(
          `Error details: ${error.message}, Stack: ${error.stack}`,
        );
      }
      return this.handleAnalysisError(error, fileName);
    }
  }

  private processAnalysisResult(
    result: OptimizableFunctionsResponse | null | undefined,
    uriString: string,
    fileName: string,
  ): {
    status: SidebarStatus;
    message: string;
    functions: string[];
  } {
    let functions: string[] = [];
    let displayFileName = fileName;
    let status: SidebarStatus = "error";
    let message = `Analysis failed for ${fileName}. Unexpected response format.`;

    if (result && typeof result === "object") {
      const keys = Object.keys(result);

      if (keys.length === 1) {
        const fileKey = keys[0]!;
        // Normalize both paths for comparison (handle Windows paths and URIs)
        const requestedUri = vscode.Uri.parse(uriString);
        const requestedPath = requestedUri.fsPath.toLowerCase();
        let responsePath: string;

        // Check if fileKey is a URI or a file system path
        if (fileKey.startsWith("file://")) {
          const responseUri = vscode.Uri.parse(fileKey);
          responsePath = responseUri.fsPath.toLowerCase();
        } else {
          // It's a file system path (Windows or Unix)
          responsePath = fileKey.toLowerCase();
        }

        // Compare normalized file system paths (case-insensitive for Windows compatibility)
        if (responsePath === requestedPath) {
          displayFileName =
            getFileNameFromUriString(fileKey) || displayFileName;

          if (Array.isArray(result[fileKey])) {
            const potentialFunctions = result[fileKey];
            if (
              potentialFunctions?.every(
                (item: unknown) => typeof item === "string",
              )
            ) {
              functions = potentialFunctions;
              status = functions.length > 0 ? "success" : "empty";
              message =
                functions.length > 0
                  ? `Found ${functions.length} optimizable function${functions.length > 1 ? "s" : ""}.`
                  : `Analysis complete for ${displayFileName}. No optimizable functions found.`;
            } else {
              message = `Analysis complete for ${displayFileName}. Response format error (expected string array).`;
              status = "error";
              this.logger.warn(
                `Unexpected array content in response for ${fileName} (key: ${fileKey}). Expected strings, got: ${JSON.stringify(potentialFunctions)}`,
              );
            }
          } else {
            message = `Analysis complete for ${displayFileName}. Unexpected response structure (value for key '${fileKey}' is not an array).`;
            status = "error";
            this.logger.warn(
              `Response for ${fileName} had key '${fileKey}' but value was not an array: ${JSON.stringify(result[fileKey])}`,
            );
          }
        } else {
          message = `Analysis failed for ${fileName}. Server returned data for a different file URI: ${fileKey}`;
          status = "error";
          this.logger.warn(
            `Mismatched URI in response for ${fileName}. Requested: ${uriString} (fsPath: ${requestedPath}), Got Key: ${fileKey} (fsPath: ${responsePath})`,
          );
        }
      } else if (keys.length === 0) {
        status = "empty";
        message = `Analysis complete for ${displayFileName}. No optimizable functions found.`;
      } else {
        message = `Analysis failed for ${displayFileName}. Unexpected response format (multiple file keys).`;
        status = "error";
        this.logger.warn(
          `Response for ${fileName} had multiple keys: ${JSON.stringify(keys)}. Expected one key.`,
        );
      }
    } else if (result === null || result === undefined) {
      status = "empty";
      message = `Analysis complete for ${displayFileName}. Server returned no data (null/undefined).`;
    } else {
      message = `Analysis failed for ${displayFileName}. Unexpected response type (${typeof result}).`;
      status = "error";
      this.logger.warn(
        `Response for ${fileName} was not an object: ${JSON.stringify(result)}`,
      );
    }

    return { status, message, functions };
  }

  private handleAnalysisError(
    error: unknown,
    fileName: string,
  ): {
    status: SidebarStatus;
    message: string;
    functions: string[];
    fileName: string;
  } {
    let message = `Analysis request failed for ${fileName}.`;
    let status: SidebarStatus = "error";

    if (error instanceof ResponseError) {
      message += ` Server Error (Code: ${error.code}): ${error.message}`;
    } else if (error instanceof Error) {
      if (
        error.message.includes("timeout") ||
        error.message.includes("Connection got disposed")
      ) {
        message += " Connection issue or timeout.";
        status =
          this.client.state !== LanguageClientState.Running
            ? "serverError"
            : "error";
      } else {
        message += ` Reason: ${error.message}`;
      }
    } else {
      message += " An unknown error occurred.";
    }

    if (this.client.state !== LanguageClientState.Running) {
      status = "serverError";
      message = "Analysis failed: Language Server stopped.";
    }

    return { status, message, functions: [], fileName };
  }

  dispose(): void {
    this.logger.dispose();
  }
}
