/**
 * Core MCP Client for Mawidi
 *
 * This is the bridge between code execution and actual tool calls.
 * It routes tool calls to appropriate server implementations.
 *
 * Usage:
 * ```typescript
 * import { callMCPTool } from './client.js';
 *
 * const bookings = await callMCPTool('database__get_bookings', {
 *   status: 'pending'
 * });
 * ```
 */

import { exec } from "child_process";
import { promisify } from "util";
import * as fs from "fs/promises";
import * as path from "path";
import { fileURLToPath } from "url";

const execAsync = promisify(exec);

// ESM equivalent of __dirname
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

/**
 * Call an MCP tool by name with input parameters
 *
 * @param toolName - Tool identifier in format 'server__tool_name'
 * @param input - Input parameters for the tool
 * @returns Tool execution result
 *
 * @example
 * ```typescript
 * // Database query
 * const bookings = await callMCPTool('database__get_bookings', {
 *   status: 'pending',
 *   limit: 10
 * });
 *
 * // Run tests
 * const testResults = await callMCPTool('testing__run_unit_tests', {});
 *
 * // Read file
 * const file = await callMCPTool('files__read_file', {
 *   path: 'mawidi-site/lib/auth.ts'
 * });
 * ```
 */
export async function callMCPTool<T>(toolName: string, input: any): Promise<T> {
  const [server, tool] = toolName.split("__");

  switch (server) {
    case "database":
      return callDatabaseTool(tool, input) as T;
    case "testing":
      return callTestingTool(tool, input) as T;
    case "files":
      return callFilesTool(tool, input) as T;
    case "commands":
      return callCommandsTool(tool, input) as T;
    case "api":
      return callAPITool(tool, input) as T;
    case "stripe":
      return callStripeTool(tool, input) as T;
    case "email":
      return callEmailTool(tool, input) as T;
    case "i18n":
      return callI18nTool(tool, input) as T;
    default:
      throw new Error(`Unknown MCP server: ${server}`);
  }
}

/**
 * Database tool implementations
 * Uses Prisma for primary database and Supabase for analytics
 */
async function callDatabaseTool(tool: string, input: any): Promise<any> {
  // Import Prisma client dynamically to avoid loading when not needed
  const { PrismaClient } =
    await import("../../mawidi-site/node_modules/@prisma/client");
  const prisma = new PrismaClient();

  try {
    switch (tool) {
      case "get_bookings": {
        const whereClause: any = {};

        if (input.status) {
          whereClause.status = input.status;
        }

        if (input.startDate || input.endDate) {
          whereClause.date = {};
          if (input.startDate) {
            whereClause.date.gte = new Date(input.startDate);
          }
          if (input.endDate) {
            whereClause.date.lte = new Date(input.endDate);
          }
        }

        return await prisma.demoBooking.findMany({
          where: Object.keys(whereClause).length > 0 ? whereClause : undefined,
          take: input.limit || 100,
          skip: input.offset || 0,
          orderBy: { date: "desc" },
        });
      }

      case "get_booking_by_id": {
        return await prisma.demoBooking.findUnique({
          where: { id: input.bookingId },
        });
      }

      case "get_booking_stats": {
        const [total, pending, confirmed, cancelled] = await Promise.all([
          prisma.demoBooking.count(),
          prisma.demoBooking.count({ where: { status: "pending" } }),
          prisma.demoBooking.count({ where: { status: "confirmed" } }),
          prisma.demoBooking.count({ where: { status: "cancelled" } }),
        ]);
        return { total, pending, confirmed, cancelled };
      }

      case "get_users": {
        return await prisma.user.findMany({
          take: input.limit || 100,
          skip: input.offset || 0,
          select: {
            id: true,
            email: true,
            name: true,
            companyName: true,
            createdAt: true,
            emailVerified: true,
          },
        });
      }

      case "get_user_by_email": {
        return await prisma.user.findUnique({
          where: { email: input.email },
          select: {
            id: true,
            email: true,
            name: true,
            companyName: true,
            createdAt: true,
            emailVerified: true,
          },
        });
      }

      default:
        throw new Error(`Unknown database tool: ${tool}`);
    }
  } finally {
    await prisma.$disconnect();
  }
}

/**
 * Testing tool implementations
 * Executes Jest and Playwright tests
 */
async function callTestingTool(tool: string, input: any): Promise<any> {
  const cwd = path.join(process.cwd(), "mawidi-site");

  switch (tool) {
    case "run_unit_tests": {
      try {
        const { stdout, stderr } = await execAsync(
          "npm run test:unit -- --passWithNoTests",
          { cwd },
        );

        // Parse test results from output
        const output = stdout + stderr;
        const testsMatch = output.match(
          /Tests:\s+(\d+)\s+passed,\s+(\d+)\s+total/,
        );

        return {
          output: output,
          success: !stderr.includes("FAIL"),
          testsRun: testsMatch ? parseInt(testsMatch[2]) : 0,
          testsPassed: testsMatch ? parseInt(testsMatch[1]) : 0,
          testsFailed: testsMatch
            ? parseInt(testsMatch[2]) - parseInt(testsMatch[1])
            : 0,
        };
      } catch (error: any) {
        return {
          output: error.stdout + error.stderr,
          success: false,
          error: error.message,
        };
      }
    }

    case "run_unit_tests_with_coverage": {
      try {
        const { stdout, stderr } = await execAsync(
          "npm run test:coverage -- --passWithNoTests",
          { cwd },
        );
        const output = stdout + stderr;

        // Parse coverage from output
        const coverageMatch = output.match(
          /All files\s+\|\s+([\d.]+)\s+\|\s+([\d.]+)\s+\|\s+([\d.]+)\s+\|\s+([\d.]+)/,
        );

        return {
          output: output,
          success: !stderr.includes("FAIL"),
          coverage: coverageMatch
            ? {
                statements: parseFloat(coverageMatch[1]),
                branches: parseFloat(coverageMatch[2]),
                functions: parseFloat(coverageMatch[3]),
                lines: parseFloat(coverageMatch[4]),
              }
            : undefined,
        };
      } catch (error: any) {
        return {
          output: error.stdout + error.stderr,
          success: false,
          error: error.message,
        };
      }
    }

    case "run_e2e_tests": {
      try {
        const { stdout, stderr } = await execAsync("npm run test:e2e", { cwd });
        return {
          output: stdout + stderr,
          success: !stderr.includes("failed"),
        };
      } catch (error: any) {
        return {
          output: error.stdout + error.stderr,
          success: false,
          error: error.message,
        };
      }
    }

    default:
      throw new Error(`Unknown testing tool: ${tool}`);
  }
}

/**
 * File tool implementations
 * Provides file system operations
 */
async function callFilesTool(tool: string, input: any): Promise<any> {
  const basePath = process.cwd();

  switch (tool) {
    case "read_file": {
      const filePath = path.isAbsolute(input.path)
        ? input.path
        : path.join(basePath, input.path);

      const content = await fs.readFile(filePath, "utf-8");
      return {
        content,
        path: input.path,
        size: content.length,
      };
    }

    case "list_directory": {
      const dirPath = path.isAbsolute(input.path)
        ? input.path
        : path.join(basePath, input.path);

      const entries = await fs.readdir(dirPath, { withFileTypes: true });
      const files = entries.filter((e) => e.isFile()).map((e) => e.name);
      const directories = entries
        .filter((e) => e.isDirectory())
        .map((e) => e.name);

      return {
        files,
        directories,
        path: input.path,
        total: entries.length,
      };
    }

    case "search_files": {
      const searchPath = path.isAbsolute(input.path)
        ? input.path
        : path.join(basePath, input.path);

      const pattern = input.pattern || "*";
      const results: string[] = [];

      async function searchRecursive(dir: string) {
        const entries = await fs.readdir(dir, { withFileTypes: true });

        for (const entry of entries) {
          const fullPath = path.join(dir, entry.name);
          const relativePath = path.relative(basePath, fullPath);

          if (
            entry.isDirectory() &&
            !entry.name.startsWith(".") &&
            entry.name !== "node_modules"
          ) {
            await searchRecursive(fullPath);
          } else if (entry.isFile() && entry.name.includes(pattern)) {
            results.push(relativePath);
          }
        }
      }

      await searchRecursive(searchPath);
      return {
        files: results,
        count: results.length,
        pattern,
      };
    }

    default:
      throw new Error(`Unknown files tool: ${tool}`);
  }
}

/**
 * Commands tool implementations
 * Executes common development commands
 */
async function callCommandsTool(tool: string, input: any): Promise<any> {
  const cwd = path.join(process.cwd(), "mawidi-site");

  switch (tool) {
    case "run_dev": {
      // Non-blocking - starts server in background
      exec("npm run dev", { cwd });
      return {
        message: "Dev server starting on port 9000...",
        port: 9000,
        url: "http://localhost:9000",
      };
    }

    case "run_build": {
      try {
        const { stdout, stderr } = await execAsync("npm run build", { cwd });
        return {
          output: stdout + stderr,
          success: !stderr.includes("Error"),
        };
      } catch (error: any) {
        return {
          output: error.stdout + error.stderr,
          success: false,
          error: error.message,
        };
      }
    }

    case "run_lint": {
      try {
        const { stdout, stderr } = await execAsync("npm run lint", { cwd });
        return {
          output: stdout + stderr,
          success: !stderr.includes("error"),
        };
      } catch (error: any) {
        return {
          output: error.stdout + error.stderr,
          success: false,
          error: error.message,
        };
      }
    }

    default:
      throw new Error(`Unknown commands tool: ${tool}`);
  }
}

/**
 * API tool implementations
 * Provides API endpoint testing operations
 */
async function callAPITool(tool: string, input: any): Promise<any> {
  // Placeholder implementations - these would make actual HTTP calls to API endpoints
  // For now, they simulate API responses for testing

  switch (tool) {
    case "test_register":
    case "test_login":
    case "test_verify_otp":
    case "test_forgot_password":
    case "test_reset_password":
    case "test_auth_flow":
      return {
        success: true,
        message: `Simulated ${tool} - implement actual API call`,
        ...input,
      };

    case "create_checkout_session":
    case "test_stripe_webhook":
    case "test_payment_flow":
      return {
        success: true,
        message: `Simulated ${tool} - implement actual Stripe API call`,
        ...input,
      };

    case "send_email":
    case "send_template_email":
    case "test_email_connection":
      return {
        success: true,
        message: `Simulated ${tool} - implement actual email send`,
        ...input,
      };

    case "test_create_demo_booking":
    case "test_send_demo_confirmation":
      return {
        success: true,
        message: `Simulated ${tool} - implement actual API call`,
        ...input,
      };

    default:
      throw new Error(`Unknown API tool: ${tool}`);
  }
}

/**
 * Stripe tool implementations
 * Direct Stripe API operations
 */
async function callStripeTool(tool: string, input: any): Promise<any> {
  // Placeholder - would use actual Stripe SDK
  // import Stripe from 'stripe';
  // const stripe = new Stripe(process.env.STRIPE_SECRET_KEY);

  switch (tool) {
    case "get_customer":
    case "list_customers":
    case "create_customer":
    case "get_customer_by_email":
      return {
        message: `Simulated ${tool} - implement actual Stripe customer operation`,
        ...input,
      };

    case "get_payment_intent":
    case "list_payments":
    case "get_payment_stats":
      return {
        message: `Simulated ${tool} - implement actual Stripe payment operation`,
        ...input,
      };

    case "get_subscription":
    case "list_subscriptions":
    case "cancel_subscription":
    case "reactivate_subscription":
      return {
        message: `Simulated ${tool} - implement actual Stripe subscription operation`,
        ...input,
      };

    default:
      throw new Error(`Unknown Stripe tool: ${tool}`);
  }
}

/**
 * Email tool implementations
 * Email sending and template operations
 */
async function callEmailTool(tool: string, input: any): Promise<any> {
  // Placeholder - would use actual email service (nodemailer, etc.)
  // const emailService = require('../../mawidi-site/lib/email');

  switch (tool) {
    case "send":
    case "send_bilingual":
    case "send_bulk":
      return {
        success: true,
        message: `Simulated ${tool} - implement actual email send`,
        messageId: `msg_${Date.now()}`,
        ...input,
      };

    case "render_template":
    case "list_templates":
    case "preview_template":
      return {
        message: `Simulated ${tool} - implement actual template rendering`,
        ...input,
      };

    case "send_otp":
    case "verify_otp":
    case "resend_otp":
      return {
        success: true,
        message: `Simulated ${tool} - implement actual OTP operation`,
        otpSent: true,
        expiresIn: 300,
        ...input,
      };

    default:
      throw new Error(`Unknown email tool: ${tool}`);
  }
}

/**
 * i18n tool implementations
 * Translation management for 5,979 lines of content
 */
async function callI18nTool(tool: string, input: any): Promise<any> {
  // Placeholder - would use actual translation files
  // const UI = require('../../mawidi-site/lib/config');

  switch (tool) {
    case "get_translation":
    case "search_translations":
    case "add_translation":
    case "get_translation_stats":
    case "list_modules":
    case "get_module_translations":
    case "validate_translations":
      return {
        message: `Simulated ${tool} - implement actual i18n operation`,
        ...input,
      };

    default:
      throw new Error(`Unknown i18n tool: ${tool}`);
  }
}

/**
 * Search for available tools across all servers
 *
 * @param query - Search term to find relevant tools
 * @returns Array of matching tool file paths
 *
 * @example
 * ```typescript
 * const tools = await searchTools('booking');
 * // Returns: ['database/bookings.ts', 'api/demo.ts']
 * ```
 */
export async function searchTools(query: string): Promise<string[]> {
  const serversDir = path.join(__dirname, "servers");
  const allTools: string[] = [];

  async function findTools(dir: string): Promise<void> {
    try {
      const entries = await fs.readdir(dir, { withFileTypes: true });

      for (const entry of entries) {
        const fullPath = path.join(dir, entry.name);

        if (entry.isDirectory()) {
          await findTools(fullPath);
        } else if (entry.name.endsWith(".ts") && entry.name !== "index.ts") {
          const content = await fs.readFile(fullPath, "utf-8");

          // Simple search: check if query appears in file
          if (content.toLowerCase().includes(query.toLowerCase())) {
            const relativePath = path.relative(serversDir, fullPath);
            allTools.push(relativePath);
          }
        }
      }
    } catch (error) {
      // Directory might not exist yet
    }
  }

  await findTools(serversDir);
  return allTools;
}

/**
 * List all available MCP servers
 *
 * @returns Array of server names
 *
 * @example
 * ```typescript
 * const servers = await listServers();
 * // Returns: ['database', 'testing', 'files', 'commands']
 * ```
 */
export async function listServers(): Promise<string[]> {
  const serversDir = path.join(__dirname, "servers");

  try {
    const entries = await fs.readdir(serversDir, { withFileTypes: true });
    return entries.filter((e) => e.isDirectory()).map((e) => e.name);
  } catch (error) {
    return [];
  }
}

/**
 * Get information about a specific server
 *
 * @param serverName - Name of the server
 * @returns Server info including available tools
 *
 * @example
 * ```typescript
 * const info = await getServerInfo('database');
 * console.log(info.tools); // ['bookings.ts', 'users.ts']
 * ```
 */
export async function getServerInfo(serverName: string): Promise<{
  name: string;
  tools: string[];
  description?: string;
}> {
  const serverDir = path.join(__dirname, "servers", serverName);

  const entries = await fs.readdir(serverDir, { withFileTypes: true });
  const tools = entries
    .filter((e) => e.isFile() && e.name.endsWith(".ts"))
    .map((e) => e.name);

  return {
    name: serverName,
    tools,
    description: `MCP server for ${serverName} operations`,
  };
}
