import { readFileSync } from "fs";
import { dirname, join } from "path";
import { fileURLToPath } from "url";

// Types for mechanics system
export interface BasicMechanic {
  term: string;
  keywords: string[];
  description: string;
}

export interface Transformation {
  name: string;
  type: string;
  category: string;
  keywords: string[];
  description: string;
  stackable: boolean;
  maxStacks: number;
  conflictsWith: string[];
  requirements: {
    eligibility: {
      allowedSubtypes: string[];
      excludedTags: string[];
    };
    cost: {
      formula?: string;
      supplyMultiplier?: number;
      baseMultiplier?: number;
      hexiteMultiplier?: number;
      fluxMultiplier?: number;
    };
    preventedByTags: string[];
  };
  statModifications?: {
    hpMultiplier?: number;
    damageMultiplier?: number;
  };
  hasPhases: boolean;
  preDeathPhase?: {
    speedMultiplier: number;
    cooldownMultiplier: number;
  };
  postDeathPhase?: {
    speedMultiplier: number;
    cooldownMultiplier: number;
  };
  triggersOnDeath: boolean;
  tagsAdded: string[];
  tagsRemoved: string[];
  notes?: string;
}

export interface MechanicsData {
  mechanics: {
    basicMechanics: Record<string, BasicMechanic>;
    transformations: Record<string, Transformation>;
  };
}

export interface FlatMechanic {
  key: string;
  category: "basic" | "transformation";
  name: string;
  keywords: string[];
  description: string;
  data: BasicMechanic | Transformation;
}

export class MarkdownFormatter {
  /**
   * Format a mechanic as markdown for MCP responses
   */
  static formatMechanic(mechanic: FlatMechanic, detailed: boolean = false): string {
    let output = `# ${mechanic.name}`;

    if (mechanic.category === "transformation") {
      output += " Transformation";
    }

    output += `\n\n`;

    if (mechanic.keywords.length > 0) {
      output += `**Keywords**: ${mechanic.keywords.join(", ")}\n\n`;
    }

    output += `**Description**: ${mechanic.description}\n\n`;

    if (detailed && mechanic.category === "transformation") {
      const transform = mechanic.data as Transformation;
      output += this.formatTransformationDetails(transform);
    }

    return output.trim();
  }

  /**
   * Format transformation-specific details
   */
  private static formatTransformationDetails(transform: Transformation): string {
    let output = "";

    output += `**Type**: ${transform.type}\n`;
    output += `**Stackable**: ${transform.stackable ? "Yes" : "No"}`;
    if (transform.maxStacks) {
      output += ` (max ${transform.maxStacks})`;
    }
    output += `\n\n`;

    if (transform.statModifications) {
      output += `**Stat Modifications**:\n`;
      if (transform.statModifications.hpMultiplier) {
        output += `- HP: ${transform.statModifications.hpMultiplier}x\n`;
      }
      if (transform.statModifications.damageMultiplier) {
        output += `- Damage: ${transform.statModifications.damageMultiplier}x\n`;
      }
      output += `\n`;
    }

    if (transform.hasPhases && (transform.preDeathPhase || transform.postDeathPhase)) {
      output += `**Phases**:\n`;
      if (transform.preDeathPhase) {
        output += `- Pre-death: Speed ${transform.preDeathPhase.speedMultiplier}x, Cooldowns ${transform.preDeathPhase.cooldownMultiplier}x\n`;
      }
      if (transform.postDeathPhase) {
        output += `- Post-death: Speed ${transform.postDeathPhase.speedMultiplier}x, Cooldowns ${transform.postDeathPhase.cooldownMultiplier}x\n`;
      }
      output += `\n`;
    }

    if (transform.notes) {
      output += `**Notes**: ${transform.notes}`;
    }

    return output;
  }

  /**
   * Format mechanics overview with statistics
   */
  static formatMechanicsOverview(
    mechanics: FlatMechanic[],
    stats: {
      basicMechanics: number;
      transformations: number;
      totalMechanics: number;
    },
  ): string {
    const mechanicsList = mechanics.map((mechanic) => ({
      category: mechanic.category === "basic" ? "Basic Mechanic" : "Transformation",
      term: mechanic.name,
      keywords: mechanic.keywords,
      description:
        (mechanic.description || "").length > 150
          ? (mechanic.description || "").substring(0, 150) + "..."
          : mechanic.description || "",
    }));

    return `# ZeroSpace Game Mechanics

Found ${stats.totalMechanics} documented mechanics:
- ${stats.basicMechanics} basic mechanics
- ${stats.transformations} transformations

${JSON.stringify(mechanicsList, null, 2)}`;
  }

  /**
   * Format error message for mechanic not found
   */
  static formatMechanicNotFound(term: string, availableKeys: string[]): string {
    return `ERROR: Mechanic '${term}' not found. Available mechanics: ${availableKeys.join(", ")}`;
  }

  /**
   * Format general error message
   */
  static formatError(message: string): string {
    return `ERROR: ${message}`;
  }
}

export class MechanicsService {
  private mechanicsData: MechanicsData | null = null;
  private __dirname: string;

  constructor() {
    const __filename = fileURLToPath(import.meta.url);
    this.__dirname = dirname(__filename);
  }

  /**
   * Load mechanics data from the JSON file
   */
  async loadMechanics(): Promise<MechanicsData> {
    if (this.mechanicsData) {
      return this.mechanicsData;
    }

    try {
      const mechanicsPath = join(
        this.__dirname,
        "..",
        "..",
        "iolin",
        "dist",
        "json",
        "meta",
        "mechanics.json",
      );

      const mechanicsFileContent = readFileSync(mechanicsPath, "utf-8");
      const parsed = JSON.parse(mechanicsFileContent);

      // Validate structure
      if (!parsed.mechanics) {
        throw new Error("Invalid mechanics file: missing 'mechanics' root object");
      }

      if (!parsed.mechanics.basicMechanics) {
        console.warn("Warning: No basicMechanics found in mechanics file");
        parsed.mechanics.basicMechanics = {};
      }

      if (!parsed.mechanics.transformations) {
        console.warn("Warning: No transformations found in mechanics file");
        parsed.mechanics.transformations = {};
      }

      this.mechanicsData = parsed;
      return this.mechanicsData!;
    } catch (error) {
      console.error("Failed to load mechanics data:", error);
      // Return empty structure on error
      const emptyData: MechanicsData = {
        mechanics: {
          basicMechanics: {},
          transformations: {},
        },
      };
      this.mechanicsData = emptyData;
      return emptyData;
    }
  }

  /**
   * Get flattened mechanics for easy searching
   */
  async getFlattenedMechanics(): Promise<Record<string, FlatMechanic>> {
    const data = await this.loadMechanics();
    const flattened: Record<string, FlatMechanic> = {};

    // Add basic mechanics
    for (const [key, mechanic] of Object.entries(data.mechanics.basicMechanics)) {
      flattened[key] = {
        key,
        category: "basic",
        name: mechanic.term || key,
        keywords: mechanic.keywords || [],
        description: mechanic.description || "",
        data: mechanic,
      };
    }

    // Add transformations
    for (const [key, transformation] of Object.entries(data.mechanics.transformations)) {
      flattened[key] = {
        key,
        category: "transformation",
        name: transformation.name || key,
        keywords: transformation.keywords || [],
        description: transformation.description || "",
        data: transformation,
      };
    }

    return flattened;
  }

  /**
   * Search for a specific mechanic by key
   */
  async getMechanic(key: string): Promise<FlatMechanic | null> {
    const flattened = await this.getFlattenedMechanics();
    return flattened[key] || null;
  }

  /**
   * Get all mechanics with optional filtering
   */
  async getAllMechanics(filter?: {
    category?: "basic" | "transformation";
    searchTerm?: string;
  }): Promise<FlatMechanic[]> {
    const flattened = await this.getFlattenedMechanics();
    let mechanics = Object.values(flattened);

    if (filter?.category) {
      mechanics = mechanics.filter((m) => m.category === filter.category);
    }

    if (filter?.searchTerm) {
      const searchLower = filter.searchTerm.toLowerCase();
      mechanics = mechanics.filter(
        (m) =>
          m.name.toLowerCase().includes(searchLower) ||
          m.description.toLowerCase().includes(searchLower) ||
          m.keywords.some((k) => k.toLowerCase().includes(searchLower)),
      );
    }

    return mechanics;
  }

  /**
   * Get statistics about the mechanics system
   */
  async getStats(): Promise<{
    totalMechanics: number;
    basicMechanics: number;
    transformations: number;
    mechanicsWithKeywords: number;
    averageKeywords: number;
  }> {
    const data = await this.loadMechanics();
    const flattened = await this.getFlattenedMechanics();

    const basicCount = Object.keys(data.mechanics.basicMechanics).length;
    const transformCount = Object.keys(data.mechanics.transformations).length;
    const totalCount = basicCount + transformCount;

    const mechanicsWithKeywords = Object.values(flattened).filter(
      (m) => m.keywords.length > 0,
    ).length;
    const totalKeywords = Object.values(flattened).reduce((sum, m) => sum + m.keywords.length, 0);
    const averageKeywords = totalCount > 0 ? totalKeywords / totalCount : 0;

    return {
      totalMechanics: totalCount,
      basicMechanics: basicCount,
      transformations: transformCount,
      mechanicsWithKeywords,
      averageKeywords: Math.round(averageKeywords * 100) / 100,
    };
  }

  /**
   * Format a mechanic for display (delegated to MarkdownFormatter)
   */
  formatMechanic(mechanic: FlatMechanic, detailed: boolean = false): string {
    return MarkdownFormatter.formatMechanic(mechanic, detailed);
  }

  /**
   * Format mechanics list for overview (delegated to MarkdownFormatter)
   */
  formatMechanicsList(mechanics: FlatMechanic[]): any[] {
    return mechanics.map((mechanic) => ({
      category: mechanic.category === "basic" ? "Basic Mechanic" : "Transformation",
      term: mechanic.name,
      keywords: mechanic.keywords,
      description:
        (mechanic.description || "").length > 150
          ? (mechanic.description || "").substring(0, 150) + "..."
          : mechanic.description || "",
    }));
  }
}

// Export singleton instance for convenience
export const mechanicsService = new MechanicsService();
