/**
 * Symbolic Evaluation Test File - TypeScript
 *
 * This file contains multiple source→sink flows for testing symbolic evaluation.
 * Each section demonstrates dangerous (unsafe) and safe patterns.
 *
 * Patterns covered:
 * - SQL injection with typed parameters
 * - Command injection
 * - Type assertions and casts
 * - Generic type propagation
 * - Interface-based patterns
 * - Async/await flows
 */

import { Request, Response, Router } from "express";
import { exec, spawn } from "child_process";
import { Pool, QueryResult } from "pg";
import * as fs from "fs";

// =============================================================================
// TYPE DEFINITIONS
// =============================================================================

interface User {
  id: string;
  username: string;
  email: string;
}

interface ApiResponse<T> {
  data: T;
  status: number;
}

type QueryParams = Record<string, string>;

// =============================================================================
// SQL INJECTION PATTERNS
// =============================================================================

class DatabaseService {
  constructor(private pool: Pool) {}

  // UNSAFE: String concatenation with typed parameter
  async getUserUnsafeConcat(userId: string): Promise<User | null> {
    const query = "SELECT * FROM users WHERE id = '" + userId + "'";
    const result = await this.pool.query(query); // Sink: danger
    return result.rows[0] || null;
  }

  // UNSAFE: Template literal with user input
  async getUserUnsafeTemplate(username: string): Promise<User | null> {
    const query = `SELECT * FROM users WHERE username = '${username}'`;
    const result = await this.pool.query(query); // Sink: danger
    return result.rows[0] || null;
  }

  // SAFE: Parameterized query
  async getUserSafeParameterized(userId: string): Promise<User | null> {
    const query = "SELECT * FROM users WHERE id = $1";
    const result = await this.pool.query(query, [userId]); // Safe
    return result.rows[0] || null;
  }

  // SAFE: Constant query
  async getAllUsersSafe(): Promise<User[]> {
    const query = "SELECT * FROM users";
    const result = await this.pool.query(query); // Safe: constant
    return result.rows;
  }
}

// =============================================================================
// COMMAND INJECTION PATTERNS
// =============================================================================

class CommandService {
  // UNSAFE: exec with typed string parameter
  executeUnsafe(command: string): Promise<string> {
    return new Promise((resolve, reject) => {
      exec(command, (error, stdout) => {
        // Sink: danger
        if (error) reject(error);
        resolve(stdout);
      });
    });
  }

  // UNSAFE: Template literal command
  listFilesUnsafe(directory: string): Promise<string> {
    const cmd = `ls -la ${directory}`;
    return this.executeUnsafe(cmd); // Propagates danger
  }

  // SAFE: spawn with array arguments
  listFilesSafe(directory: string): Promise<string> {
    return new Promise((resolve, reject) => {
      const proc = spawn("ls", ["-la", directory]); // Safer
      let output = "";
      proc.stdout.on("data", (data) => (output += data));
      proc.on("close", () => resolve(output));
    });
  }
}

// =============================================================================
// TYPE ASSERTION PATTERNS
// =============================================================================

class TypeService {
  // UNSAFE: Type assertion doesn't sanitize
  processUnsafeAssertion(input: unknown): string {
    const userInput = input as string; // Type assertion, still dangerous
    return userInput;
  }

  // UNSAFE: Non-null assertion
  processUnsafeNonNull(input: string | null): string {
    return input!; // Non-null assertion, danger propagates
  }

  // SAFE: Type guard with validation
  processWithTypeGuard(input: unknown): string {
    if (typeof input === "string" && input.length < 100) {
      return input; // Validated
    }
    return "default";
  }
}

// =============================================================================
// GENERIC TYPE PROPAGATION
// =============================================================================

class GenericService<T> {
  private data: T | null = null;

  // UNSAFE: Generic stores dangerous data
  setData(value: T): void {
    this.data = value; // Assignment propagates danger
  }

  // UNSAFE: Returns potentially dangerous data
  getData(): T | null {
    return this.data; // Returns dangerous data
  }

  // UNSAFE: Transform still propagates danger
  transform<U>(mapper: (value: T) => U): U | null {
    if (this.data === null) return null;
    return mapper(this.data); // Danger propagates through mapper
  }
}

// =============================================================================
// FILE OPERATIONS
// =============================================================================

class FileService {
  constructor(private basePath: string) {}

  // UNSAFE: Path concatenation
  readFileUnsafe(filename: string): string {
    const path = `${this.basePath}/${filename}`;
    return fs.readFileSync(path, "utf-8"); // Sink: path traversal
  }

  // SAFE: Validated path
  readFileSafe(filename: string): string {
    if (filename.includes("..") || filename.startsWith("/")) {
      throw new Error("Invalid filename");
    }
    const path = `${this.basePath}/${filename}`;
    return fs.readFileSync(path, "utf-8"); // Safe after validation
  }
}

// =============================================================================
// ASYNC/AWAIT FLOW PATTERNS
// =============================================================================

class AsyncService {
  // UNSAFE: Async propagates danger
  async processAsyncUnsafe(input: string): Promise<string> {
    const step1 = await this.step1(input); // Danger propagates
    const step2 = await this.step2(step1); // Still dangerous
    return step2;
  }

  private async step1(value: string): Promise<string> {
    return value.toUpperCase();
  }

  private async step2(value: string): Promise<string> {
    return `processed: ${value}`;
  }

  // SAFE: Async with constant
  async processAsyncSafe(): Promise<string> {
    const step1 = await this.step1("constant");
    const step2 = await this.step2(step1);
    return step2;
  }
}

// =============================================================================
// INTERFACE-BASED PATTERNS
// =============================================================================

interface RequestHandler {
  handle(params: QueryParams): Promise<ApiResponse<User>>;
}

class UnsafeHandler implements RequestHandler {
  constructor(private db: DatabaseService) {}

  // UNSAFE: Passes user input to SQL
  async handle(params: QueryParams): Promise<ApiResponse<User>> {
    const userId = params.id; // Source from interface
    const user = await this.db.getUserUnsafeConcat(userId); // Danger
    return { data: user!, status: 200 };
  }
}

class SafeHandler implements RequestHandler {
  constructor(private db: DatabaseService) {}

  // SAFE: Uses parameterized query
  async handle(params: QueryParams): Promise<ApiResponse<User>> {
    const userId = params.id;
    const user = await this.db.getUserSafeParameterized(userId);
    return { data: user!, status: 200 };
  }
}

// =============================================================================
// EXPRESS ROUTER (Sources)
// =============================================================================

const router = Router();

// UNSAFE: Query parameter to SQL
router.get("/user/unsafe", async (req: Request, res: Response) => {
  const userId = req.query.id as string; // Source: user input
  const pool = new Pool();
  const db = new DatabaseService(pool);
  const user = await db.getUserUnsafeConcat(userId); // Danger
  res.json(user);
});

// SAFE: Parameterized query
router.get("/user/safe", async (req: Request, res: Response) => {
  const userId = req.query.id as string; // Source: user input
  const pool = new Pool();
  const db = new DatabaseService(pool);
  const user = await db.getUserSafeParameterized(userId); // Safe
  res.json(user);
});

// UNSAFE: Body to command
router.post("/exec/unsafe", async (req: Request, res: Response) => {
  const directory = req.body.directory as string; // Source
  const service = new CommandService();
  const result = await service.listFilesUnsafe(directory); // Danger
  res.send(result);
});

// UNSAFE: Params to file read
router.get("/file/unsafe/:name", async (req: Request, res: Response) => {
  const filename = req.params.name; // Source
  const service = new FileService("/var/data");
  const content = service.readFileUnsafe(filename); // Danger
  res.send(content);
});

export { router, DatabaseService, CommandService, FileService };
