/**
 * Command Parser Service
 *
 * Parses chat commands from Discord, Mattermost, Slack
 */

import { Injectable } from "@nestjs/common";
import {
  CommandAction,
  CommandParseResult,
  IssueReference,
  ParsedCommand,
} from "./command.interface";

@Injectable()
export class CommandParserService {
  private readonly MENTION_PATTERN = /^@mosaic(?:\s+|$)/i;
  private readonly ISSUE_PATTERNS = {
    // #42
    current: /^#(\d+)$/,
    // owner/repo#42
    crossRepo: /^([a-zA-Z0-9-_]+)\/([a-zA-Z0-9-_]+)#(\d+)$/,
    // https://git.example.com/owner/repo/issues/42
    url: /^https?:\/\/[^/]+\/([a-zA-Z0-9-_]+)\/([a-zA-Z0-9-_]+)\/issues\/(\d+)$/,
  };

  /**
   * Parse a chat command
   */
  parseCommand(message: string): CommandParseResult {
    // Normalize whitespace
    const normalized = message.trim().replace(/\s+/g, " ");

    // Check for @mosaic mention
    if (!this.MENTION_PATTERN.test(normalized)) {
      return {
        success: false,
        error: {
          message: "Commands must start with @mosaic",
          help: "Example: @mosaic fix #42",
        },
      };
    }

    // Remove @mosaic mention
    const withoutMention = normalized.replace(this.MENTION_PATTERN, "");

    // Tokenize
    const tokens = withoutMention.split(" ").filter((t) => t.length > 0);

    if (tokens.length === 0) {
      return {
        success: false,
        error: {
          message: "No action provided",
          help: this.getHelpText(),
        },
      };
    }

    // Parse action
    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    const actionStr = tokens[0]!.toLowerCase();
    const action = this.parseAction(actionStr);

    if (!action) {
      return {
        success: false,
        error: {
          message: `Unknown action: ${actionStr}`,
          help: this.getHelpText(),
        },
      };
    }

    // Parse arguments based on action
    const args = tokens.slice(1);
    return this.parseActionArguments(action, args);
  }

  /**
   * Parse action string to CommandAction enum
   */
  private parseAction(action: string): CommandAction | null {
    const actionMap: Record<string, CommandAction> = {
      fix: CommandAction.FIX,
      status: CommandAction.STATUS,
      cancel: CommandAction.CANCEL,
      retry: CommandAction.RETRY,
      verbose: CommandAction.VERBOSE,
      quiet: CommandAction.QUIET,
      help: CommandAction.HELP,
    };

    return actionMap[action] ?? null;
  }

  /**
   * Parse arguments for a specific action
   */
  private parseActionArguments(action: CommandAction, args: string[]): CommandParseResult {
    switch (action) {
      case CommandAction.FIX:
        return this.parseFixCommand(args);

      case CommandAction.STATUS:
      case CommandAction.CANCEL:
      case CommandAction.RETRY:
      case CommandAction.VERBOSE:
        return this.parseJobCommand(action, args);

      case CommandAction.QUIET:
      case CommandAction.HELP:
        return this.parseNoArgCommand(action, args);

      default:
        return {
          success: false,
          error: {
            message: `Unhandled action: ${String(action)}`,
          },
        };
    }
  }

  /**
   * Parse fix command (requires issue reference)
   */
  private parseFixCommand(args: string[]): CommandParseResult {
    if (args.length === 0) {
      return {
        success: false,
        error: {
          message: "Fix command requires an issue reference",
          help: "Examples: @mosaic fix #42, @mosaic fix owner/repo#42, @mosaic fix https://git.example.com/owner/repo/issues/42",
        },
      };
    }

    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    const issueRef = args[0]!;
    const issue = this.parseIssueReference(issueRef);

    if (!issue) {
      return {
        success: false,
        error: {
          message: `Invalid issue reference: ${issueRef}`,
          help: "Valid formats: #42, owner/repo#42, or full URL",
        },
      };
    }

    const command: ParsedCommand = {
      action: CommandAction.FIX,
      issue,
      rawArgs: args,
    };

    return { success: true, command };
  }

  /**
   * Parse job commands (status, cancel, retry, verbose)
   */
  private parseJobCommand(action: CommandAction, args: string[]): CommandParseResult {
    if (args.length === 0) {
      return {
        success: false,
        error: {
          message: `${action} command requires a job ID`,
          help: `Example: @mosaic ${action} job-123`,
        },
      };
    }

    // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
    const jobId = args[0]!;
    const command: ParsedCommand = {
      action,
      jobId,
      rawArgs: args,
    };

    return { success: true, command };
  }

  /**
   * Parse commands that take no arguments (quiet, help)
   */
  private parseNoArgCommand(action: CommandAction, args: string[]): CommandParseResult {
    const command: ParsedCommand = {
      action,
      rawArgs: args,
    };

    return { success: true, command };
  }

  /**
   * Parse issue reference in various formats
   */
  private parseIssueReference(ref: string): IssueReference | null {
    // Try current repo format: #42
    const currentMatch = ref.match(this.ISSUE_PATTERNS.current);
    if (currentMatch) {
      return {
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        number: parseInt(currentMatch[1]!, 10),
      };
    }

    // Try cross-repo format: owner/repo#42
    const crossRepoMatch = ref.match(this.ISSUE_PATTERNS.crossRepo);
    if (crossRepoMatch) {
      return {
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        number: parseInt(crossRepoMatch[3]!, 10),
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        owner: crossRepoMatch[1]!,
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        repo: crossRepoMatch[2]!,
      };
    }

    // Try URL format: https://git.example.com/owner/repo/issues/42
    const urlMatch = ref.match(this.ISSUE_PATTERNS.url);
    if (urlMatch) {
      return {
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        number: parseInt(urlMatch[3]!, 10),
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        owner: urlMatch[1]!,
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        repo: urlMatch[2]!,
        url: ref,
      };
    }

    return null;
  }

  /**
   * Get help text for all commands
   */
  private getHelpText(): string {
    return [
      "Available commands:",
      "  @mosaic fix <issue>       - Start job for issue (#42, owner/repo#42, or URL)",
      "  @mosaic status <job>      - Get job status",
      "  @mosaic cancel <job>      - Cancel running job",
      "  @mosaic retry <job>       - Retry failed job",
      "  @mosaic verbose <job>     - Enable verbose logging",
      "  @mosaic quiet             - Reduce notifications",
      "  @mosaic help              - Show this help",
    ].join("\n");
  }
}
